user_specified_ports/
user_specified_ports.rs

1//! User-Specified Ports Example
2//! 
3//! This example demonstrates how users can specify exact port mappings
4//! instead of using auto-assigned ports. This is useful when:
5//! - You need services on specific ports (e.g., 5432 for PostgreSQL)
6//! - You want consistent ports across test runs
7//! - You're integrating with external tools that expect specific ports
8
9use rust_test_harness::{test, before_each, after_each, run_tests_with_config, TestConfig, ContainerConfig};
10use std::time::Duration;
11
12fn main() {
13    println!("๐Ÿ”ง User-Specified Ports Example");
14    println!("==============================");
15    println!();
16    
17    // Example 1: PostgreSQL on standard port 5432
18    let postgres_container = ContainerConfig::new("postgres:13-alpine")
19        .port(5432, 5432)     // Map host port 5432 to container port 5432
20        .env("POSTGRES_PASSWORD", "testpass")
21        .env("POSTGRES_DB", "testdb")
22        .name("postgres_test")
23        .ready_timeout(Duration::from_secs(15));
24    
25    // Example 2: Web service on port 8080
26    let web_container = ContainerConfig::new("nginx:alpine")
27        .port(8080, 80)       // Map host port 8080 to container port 80
28        .name("nginx_test")
29        .ready_timeout(Duration::from_secs(10));
30    
31    // Example 3: Mixed configuration - some fixed, some auto
32    let api_container = ContainerConfig::new("httpd:alpine")
33        .port(3000, 80)       // Fixed port for main API
34        .auto_port(443)       // Auto-assign for HTTPS
35        .auto_port(9090)      // Auto-assign for metrics
36        .name("api_test")
37        .ready_timeout(Duration::from_secs(10));
38    
39    println!("๐Ÿ“‹ Container Configurations:");
40    println!("1. PostgreSQL: Fixed on localhost:5432 (standard PostgreSQL port)");
41    println!("2. Web Service: Fixed on localhost:8080");
42    println!("3. API Service: Fixed localhost:3000, auto-assigned for 443 and 9090");
43    println!();
44    
45    // Clone containers for hooks
46    let postgres_before = postgres_container.clone();
47    let web_before = web_container.clone();
48    let api_before = api_container.clone();
49    
50    // Start containers in before_each
51    before_each(move |ctx| {
52        println!("๐Ÿš€ before_each: Starting containers with specified ports...");
53        
54        // Start PostgreSQL container
55        let postgres_info = postgres_before.start()
56            .map_err(|e| format!("Failed to start PostgreSQL container: {}", e))?;
57        ctx.set_data("postgres_container_info", postgres_info.clone());
58        
59        println!("โœ… PostgreSQL started: {}", postgres_info.container_id);
60        println!("   ๐Ÿ“ Port mappings: {}", postgres_info.ports_summary());
61        println!("   ๐Ÿ—„๏ธ Database URL: postgresql://postgres:testpass@localhost:5432/testdb");
62        
63        // Start web container
64        let web_info = web_before.start()
65            .map_err(|e| format!("Failed to start web container: {}", e))?;
66        ctx.set_data("web_container_info", web_info.clone());
67        
68        println!("โœ… Web service started: {}", web_info.container_id);
69        println!("   ๐Ÿ“ Port mappings: {}", web_info.ports_summary());
70        println!("   ๐ŸŒ Web accessible at: http://localhost:8080");
71        
72        // Start API container
73        let api_info = api_before.start()
74            .map_err(|e| format!("Failed to start API container: {}", e))?;
75        ctx.set_data("api_container_info", api_info.clone());
76        
77        println!("โœ… API service started: {}", api_info.container_id);
78        println!("   ๐Ÿ“ Port mappings: {}", api_info.ports_summary());
79        println!("   ๐Ÿ”— API accessible at: http://localhost:3000");
80        if let Some(https_port) = api_info.host_port_for(443) {
81            println!("   ๐Ÿ”’ HTTPS accessible at: https://localhost:{}", https_port);
82        }
83        if let Some(metrics_port) = api_info.host_port_for(9090) {
84            println!("   ๐Ÿ“Š Metrics accessible at: http://localhost:{}/metrics", metrics_port);
85        }
86        
87        Ok(())
88    });
89    
90    // Cleanup containers in after_each
91    let postgres_after = postgres_container.clone();
92    let web_after = web_container.clone();
93    let api_after = api_container.clone();
94    
95    after_each(move |ctx| {
96        println!("๐Ÿงน after_each: Cleaning up containers...");
97        
98        if let Some(postgres_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("postgres_container_info") {
99            let _ = postgres_after.stop(&postgres_info.container_id);
100            println!("๐Ÿ›‘ Stopped PostgreSQL container");
101        }
102        
103        if let Some(web_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info") {
104            let _ = web_after.stop(&web_info.container_id);
105            println!("๐Ÿ›‘ Stopped web container");
106        }
107        
108        if let Some(api_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("api_container_info") {
109            let _ = api_after.stop(&api_info.container_id);
110            println!("๐Ÿ›‘ Stopped API container");
111        }
112        
113        println!("โœ… All containers cleaned up!");
114        Ok(())
115    });
116    
117    // Test 1: Database connection test
118    test("test_database_connection", |ctx| {
119        println!("๐Ÿงช Testing database connection on fixed port 5432...");
120        
121        if let Some(postgres_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("postgres_container_info") {
122            assert_eq!(postgres_info.host_port_for(5432), Some(5432));
123            assert_eq!(postgres_info.url_for_port(5432), Some("localhost:5432".to_string()));
124            println!("โœ… Database is accessible on the expected port 5432");
125        } else {
126            return Err("PostgreSQL container info not found".into());
127        }
128        
129        Ok(())
130    });
131    
132    // Test 2: Web service test
133    test("test_web_service_fixed_port", |ctx| {
134        println!("๐Ÿงช Testing web service on fixed port 8080...");
135        
136        if let Some(web_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info") {
137            assert_eq!(web_info.host_port_for(80), Some(8080));
138            assert_eq!(web_info.url_for_port(80), Some("localhost:8080".to_string()));
139            println!("โœ… Web service is accessible on the expected port 8080");
140        } else {
141            return Err("Web container info not found".into());
142        }
143        
144        Ok(())
145    });
146    
147    // Test 3: Mixed configuration test
148    test("test_mixed_port_configuration", |ctx| {
149        println!("๐Ÿงช Testing mixed port configuration (fixed + auto)...");
150        
151        if let Some(api_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("api_container_info") {
152            // Test fixed port
153            assert_eq!(api_info.host_port_for(80), Some(3000));
154            assert_eq!(api_info.url_for_port(80), Some("localhost:3000".to_string()));
155            println!("โœ… Fixed port 3000 -> 80 is working");
156            
157            // Test auto-assigned ports
158            if let Some(https_port) = api_info.host_port_for(443) {
159                assert!(https_port > 1024); // Should be a high port
160                println!("โœ… Auto-assigned HTTPS port: {}", https_port);
161            }
162            
163            if let Some(metrics_port) = api_info.host_port_for(9090) {
164                assert!(metrics_port > 1024); // Should be a high port
165                println!("โœ… Auto-assigned metrics port: {}", metrics_port);
166            }
167            
168            println!("โœ… Mixed configuration is working correctly");
169        } else {
170            return Err("API container info not found".into());
171        }
172        
173        Ok(())
174    });
175    
176    println!("๐Ÿš€ Running user-specified ports tests...");
177    
178    let config = TestConfig {
179        html_report: Some("user-specified-ports-report.html".to_string()),
180        ..Default::default()
181    };
182    
183    let exit_code = run_tests_with_config(config);
184    
185    println!();
186    println!("๐ŸŽ‰ Tests completed!");
187    println!("๐Ÿ“Š Check the HTML report for detailed results.");
188    println!();
189    println!("๐Ÿ’ก Key Takeaways:");
190    println!("   โ€ข Use .port(host_port, container_port) for fixed port mappings");
191    println!("   โ€ข Use .auto_port(container_port) for automatic port assignment");
192    println!("   โ€ข Mix both approaches for maximum flexibility");
193    println!("   โ€ข Fixed ports are great for services with standard ports (5432, 3306, etc.)");
194    println!("   โ€ข Auto-ports prevent conflicts in parallel test environments");
195    
196    std::process::exit(exit_code);
197}