/// 745: Integration Test Structure
///
/// In a real project, integration tests live in `tests/` directory:
///
/// ```
/// my_crate/
/// โโโ src/
/// โ โโโ lib.rs โ library code (pub API)
/// โโโ tests/
/// โ โโโ common/
/// โ โ โโโ mod.rs โ shared helpers (NOT a test file itself)
/// โ โโโ config_test.rs
/// โ โโโ api_test.rs
/// โโโ Cargo.toml
/// ```
///
/// This file shows both the library code AND simulates the integration test pattern.
// โโ "Library" public API (would be in src/lib.rs) โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub host: String,
pub port: u16,
pub max_connections: u32,
}
impl Config {
pub fn new(host: impl Into<String>, port: u16, max_connections: u32) -> Self {
Config { host: host.into(), port, max_connections }
}
pub fn default() -> Self {
Config::new("localhost", 8080, 100)
}
pub fn to_addr(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
#[derive(Debug, PartialEq)]
pub enum ConfigError {
PortOutOfRange(u16),
EmptyHost,
InvalidMaxConnections,
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConfigError::PortOutOfRange(p) => write!(f, "port {} is invalid (use 1-65535)", p),
ConfigError::EmptyHost => write!(f, "host cannot be empty"),
ConfigError::InvalidMaxConnections => write!(f, "max_connections must be > 0"),
}
}
}
pub fn validate_config(c: &Config) -> Result<(), ConfigError> {
if c.host.is_empty() { return Err(ConfigError::EmptyHost); }
if c.port == 0 { return Err(ConfigError::PortOutOfRange(0)); }
if c.max_connections == 0 { return Err(ConfigError::InvalidMaxConnections); }
Ok(())
}
pub fn parse_port(s: &str) -> Result<u16, String> {
let n: u32 = s.parse().map_err(|_| format!("'{}' is not a number", s))?;
if n == 0 || n > 65535 {
return Err(format!("port {} out of range (1-65535)", n));
}
Ok(n as u16)
}
fn main() {
let cfg = Config::default();
println!("Default config: {}", cfg.to_addr());
match validate_config(&cfg) {
Ok(()) => println!("Config valid"),
Err(e) => println!("Config error: {}", e),
}
}
// โโ Simulated integration tests (in real project: tests/config_test.rs) โโโโโโโ
//
// Real integration test file would start with:
// use my_crate::{Config, validate_config, parse_port};
//
// And `tests/common/mod.rs` would contain:
// pub fn test_config() -> Config { Config::new("test-host", 9999, 10) }
// pub fn assert_valid(c: &Config) { assert!(validate_config(c).is_ok()); }
// For this self-contained example, we put them in a sub-module:
#[cfg(test)]
mod common {
use super::*;
pub fn test_config() -> Config {
Config::new("test-host", 9999, 10)
}
pub fn assert_valid(c: &Config) {
assert!(validate_config(c).is_ok(), "config should be valid: {:?}", c);
}
}
#[cfg(test)]
mod integration_tests {
use super::*;
use common::*;
#[test]
fn default_config_is_valid() {
let cfg = Config::default();
assert_valid(&cfg);
}
#[test]
fn test_config_helper_works() {
let cfg = test_config();
assert_eq!(cfg.host, "test-host");
assert_eq!(cfg.port, 9999);
assert_valid(&cfg);
}
#[test]
fn to_addr_formats_correctly() {
let cfg = Config::new("example.com", 443, 50);
assert_eq!(cfg.to_addr(), "example.com:443");
}
#[test]
fn empty_host_is_invalid() {
let cfg = Config::new("", 80, 10);
assert_eq!(validate_config(&cfg), Err(ConfigError::EmptyHost));
}
#[test]
fn zero_port_is_invalid() {
let cfg = Config::new("localhost", 0, 10);
assert_eq!(validate_config(&cfg), Err(ConfigError::PortOutOfRange(0)));
}
#[test]
fn parse_port_valid() {
assert_eq!(parse_port("8080"), Ok(8080));
assert_eq!(parse_port("1"), Ok(1));
assert_eq!(parse_port("65535"), Ok(65535));
}
#[test]
fn parse_port_invalid() {
assert!(parse_port("0").is_err());
assert!(parse_port("65536").is_err());
assert!(parse_port("not_a_number").is_err());
assert!(parse_port("").is_err());
}
}
(* 745: Integration Test Setup โ OCaml
In OCaml with Dune, integration tests are separate executables
in a test/ directory. We simulate the pattern here. *)
(* === lib.ml (the library) === *)
module MyLib = struct
type config = {
host: string;
port: int;
max_connections: int;
}
let default_config = { host = "localhost"; port = 8080; max_connections = 100 }
let with_config host port max_connections =
{ host; port; max_connections }
let config_to_string c =
Printf.sprintf "%s:%d (max=%d)" c.host c.port c.max_connections
type 'a result = Ok of 'a | Err of string
let parse_port s =
match int_of_string_opt s with
| Some n when n > 0 && n < 65536 -> Ok n
| Some n -> Err (Printf.sprintf "port %d out of range" n)
| None -> Err (Printf.sprintf "'%s' is not a number" s)
end
(* === tests/common.ml (shared helpers) === *)
module TestCommon = struct
let test_config = MyLib.with_config "test-host" 9999 10
let make_test_config ?(host="test") ?(port=9999) ?(max=10) () =
MyLib.with_config host port max
end
(* === tests/config_test.ml === *)
let () =
(* Integration test: uses only public API *)
let cfg = TestCommon.make_test_config () in
assert (cfg.MyLib.port = 9999);
let s = MyLib.config_to_string cfg in
assert (String.length s > 0);
(match MyLib.parse_port "8080" with
| MyLib.Ok n -> assert (n = 8080)
| MyLib.Err e -> failwith e);
(match MyLib.parse_port "99999" with
| MyLib.Err _ -> ()
| MyLib.Ok _ -> failwith "expected error");
Printf.printf "Integration tests: OK\n"