auto_port_demo/
auto_port_demo.rs

1//! Auto-port and Container Management Demo
2//! 
3//! This example demonstrates:
4//! 1. Automatic port allocation for containers
5//! 2. Easy access to container URLs and port information
6//! 3. Automatic cleanup of containers
7//! 4. Multiple containers with different port configurations
8
9use rust_test_harness::{test, run_tests_with_config, TestConfig, ContainerConfig, before_each, after_each};
10use std::time::Duration;
11
12fn main() {
13    println!("๐Ÿš€ Auto-Port and Container Management Demo");
14    println!("==========================================");
15    println!();
16    
17    // Example 1: Web service with auto-port
18    let web_container = ContainerConfig::new("nginx:alpine")
19        .auto_port(80) // Automatically assign host port for container port 80
20        .env("NGINX_HOST", "localhost")
21        .name("web_service")
22        .ready_timeout(Duration::from_secs(15));
23    
24    // Example 2: Database with auto-port
25    let db_container = ContainerConfig::new("postgres:13-alpine")
26        .auto_port(5432) // Automatically assign host port for container port 5432
27        .env("POSTGRES_PASSWORD", "testpass")
28        .env("POSTGRES_DB", "testdb")
29        .name("test_database")
30        .ready_timeout(Duration::from_secs(20));
31    
32    // Example 3: Redis with auto-port
33    let redis_container = ContainerConfig::new("redis:6-alpine")
34        .auto_port(6379) // Automatically assign host port for container port 6379
35        .name("test_redis")
36        .ready_timeout(Duration::from_secs(10));
37    
38    // Example 4: Mixed configuration (manual + auto ports)
39    let mixed_container = ContainerConfig::new("httpd:alpine")
40        .port(8080, 80) // Manual port mapping
41        .auto_port(443) // Auto port for HTTPS
42        .env("APACHE_DOCUMENT_ROOT", "/var/www/html")
43        .name("mixed_ports")
44        .ready_timeout(Duration::from_secs(15));
45    
46    println!("๐Ÿ“‹ Container Configurations:");
47    println!("1. Web Service (nginx):");
48    println!("   - Image: {}", web_container.image);
49    println!("   - Auto-ports: {:?}", web_container.auto_ports);
50    println!("   - Auto-cleanup: {}", web_container.auto_cleanup);
51    
52    println!("2. Database (postgres):");
53    println!("   - Image: {}", db_container.image);
54    println!("   - Auto-ports: {:?}", db_container.auto_ports);
55    println!("   - Auto-cleanup: {}", db_container.auto_cleanup);
56    
57    println!("3. Cache (redis):");
58    println!("   - Image: {}", redis_container.image);
59    println!("   - Auto-ports: {:?}", redis_container.auto_ports);
60    println!("   - Auto-cleanup: {}", redis_container.auto_cleanup);
61    
62    println!("4. Mixed (httpd):");
63    println!("   - Image: {}", mixed_container.image);
64    println!("   - Manual ports: {:?}", mixed_container.ports);
65    println!("   - Auto-ports: {:?}", mixed_container.auto_ports);
66    println!("   - Auto-cleanup: {}", mixed_container.auto_cleanup);
67    println!();
68    
69    // Clone containers for hooks
70    let web_before = web_container.clone();
71    let db_before = db_container.clone();
72    let redis_before = redis_container.clone();
73    let mixed_before = mixed_container.clone();
74    
75    // Start all containers in before_each for each test
76    before_each(move |ctx| {
77        println!("๐Ÿš€ before_each: Starting all containers...");
78        
79        // Start web container
80        let web_info = web_before.start()
81            .map_err(|e| format!("Failed to start web container: {}", e))?;
82        ctx.set_data("web_container_info", web_info.clone());
83        println!("โœ… Web container started: {}", web_info.container_id);
84        println!("   Ports: {}", web_info.ports_summary());
85        if let Some(url) = web_info.primary_url() {
86            println!("   URL: {}", url);
87        }
88        
89        // Start database container
90        let db_info = db_before.start()
91            .map_err(|e| format!("Failed to start db container: {}", e))?;
92        ctx.set_data("db_container_info", db_info.clone());
93        println!("โœ… Database container started: {}", db_info.container_id);
94        println!("   Ports: {}", db_info.ports_summary());
95        if let Some(host_port) = db_info.host_port_for(5432) {
96            println!("   PostgreSQL accessible at: localhost:{}", host_port);
97        }
98        
99        // Start redis container
100        let redis_info = redis_before.start()
101            .map_err(|e| format!("Failed to start redis container: {}", e))?;
102        ctx.set_data("redis_container_info", redis_info.clone());
103        println!("โœ… Redis container started: {}", redis_info.container_id);
104        println!("   Ports: {}", redis_info.ports_summary());
105        if let Some(host_port) = redis_info.host_port_for(6379) {
106            println!("   Redis accessible at: localhost:{}", host_port);
107        }
108        
109        // Start mixed container
110        let mixed_info = mixed_before.start()
111            .map_err(|e| format!("Failed to start mixed container: {}", e))?;
112        ctx.set_data("mixed_container_info", mixed_info.clone());
113        println!("โœ… Mixed container started: {}", mixed_info.container_id);
114        println!("   Ports: {}", mixed_info.ports_summary());
115        if let Some(host_port) = mixed_info.host_port_for(443) {
116            println!("   HTTPS accessible at: localhost:{}", host_port);
117        }
118        
119        println!("๐ŸŽ‰ All containers started successfully!");
120        Ok(())
121    });
122    
123    // Cleanup all containers in after_each for each test
124    let web_after = web_container.clone();
125    let db_after = db_container.clone();
126    let redis_after = redis_container.clone();
127    let mixed_after = mixed_container.clone();
128    
129    after_each(move |ctx| {
130        println!("๐Ÿงน after_each: Cleaning up all containers...");
131        
132        // Get all container info and stop them
133        if let Some(web_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info") {
134            let _ = web_after.stop(&web_info.container_id);
135            println!("๐Ÿ›‘ Stopped web container: {}", web_info.container_id);
136        }
137        
138        if let Some(db_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("db_container_info") {
139            let _ = db_after.stop(&db_info.container_id);
140            println!("๐Ÿ›‘ Stopped database container: {}", db_info.container_id);
141        }
142        
143        if let Some(redis_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("redis_container_info") {
144            let _ = redis_after.stop(&redis_info.container_id);
145            println!("๐Ÿ›‘ Stopped redis container: {}", redis_info.container_id);
146        }
147        
148        if let Some(mixed_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("mixed_container_info") {
149            let _ = mixed_after.stop(&mixed_info.container_id);
150            println!("๐Ÿ›‘ Stopped mixed container: {}", mixed_info.container_id);
151        }
152        
153        println!("โœ… All containers cleaned up!");
154        Ok(())
155    });
156    
157    // Test 1: Access web service
158    test("test_web_service_access", |ctx| {
159        println!("๐Ÿงช Testing web service access...");
160        
161        let web_info = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info")
162            .expect("Web container info should be available");
163        
164        println!("๐ŸŒ Web service container: {}", web_info.container_id);
165        println!("   Ports: {}", web_info.ports_summary());
166        println!("   Primary URL: {}", web_info.primary_url().unwrap_or("None"));
167        
168        // In a real test, you would make HTTP requests to the container
169        // For demo purposes, just verify the port information
170        assert!(web_info.host_port_for(80).is_some(), "Web service should have port 80 exposed");
171        
172        println!("โœ… Web service test passed");
173        Ok(())
174    });
175    
176    // Test 2: Access database
177    test("test_database_access", |ctx| {
178        println!("๐Ÿงช Testing database access...");
179        
180        let db_info = ctx.get_data::<rust_test_harness::ContainerInfo>("db_container_info")
181            .expect("Database container info should be available");
182        
183        println!("๐Ÿ—„๏ธ Database container: {}", db_info.container_id);
184        println!("   Ports: {}", db_info.ports_summary());
185        
186        // Verify PostgreSQL port is exposed
187        let postgres_port = db_info.host_port_for(5432)
188            .expect("PostgreSQL port 5432 should be exposed");
189        println!("   PostgreSQL accessible at: localhost:{}", postgres_port);
190        
191        // In a real test, you would connect to the database
192        // For demo purposes, just verify the port information
193        assert!(postgres_port > 0, "PostgreSQL port should be valid");
194        
195        println!("โœ… Database test passed");
196        Ok(())
197    });
198    
199    // Test 3: Access redis
200    test("test_redis_access", |ctx| {
201        println!("๐Ÿงช Testing redis access...");
202        
203        let redis_info = ctx.get_data::<rust_test_harness::ContainerInfo>("redis_container_info")
204            .expect("Redis container info should be available");
205        
206        println!("๐Ÿ”ด Redis container: {}", redis_info.container_id);
207        println!("   Ports: {}", redis_info.ports_summary());
208        
209        // Verify Redis port is exposed
210        let redis_port = redis_info.host_port_for(6379)
211            .expect("Redis port 6379 should be exposed");
212        println!("   Redis accessible at: localhost:{}", redis_port);
213        
214        // In a real test, you would connect to Redis
215        // For demo purposes, just verify the port information
216        assert!(redis_port > 0, "Redis port should be valid");
217        
218        println!("โœ… Redis test passed");
219        Ok(())
220    });
221    
222    // Test 4: Mixed port configuration
223    test("test_mixed_port_config", |ctx| {
224        println!("๐Ÿงช Testing mixed port configuration...");
225        
226        let mixed_info = ctx.get_data::<rust_test_harness::ContainerInfo>("mixed_container_info")
227            .expect("Mixed container info should be available");
228        
229        println!("๐Ÿ”€ Mixed container: {}", mixed_info.container_id);
230        println!("   Ports: {}", mixed_info.ports_summary());
231        
232        // Verify both manual and auto ports
233        let http_port = mixed_info.host_port_for(80)
234            .expect("HTTP port 80 should be exposed");
235        let https_port = mixed_info.host_port_for(443)
236            .expect("HTTPS port 443 should be exposed");
237        
238        println!("   HTTP accessible at: localhost:{}", http_port);
239        println!("   HTTPS accessible at: localhost:{}", https_port);
240        
241        // Verify the manual port mapping (8080 -> 80)
242        assert_eq!(http_port, 8080, "Manual port mapping should work");
243        
244        // Verify auto port is different from manual port
245        assert_ne!(https_port, 8080, "Auto port should be different from manual port");
246        
247        println!("โœ… Mixed port configuration test passed");
248        Ok(())
249    });
250    
251    // Test 5: Container info methods
252    test("test_container_info_methods", |ctx| {
253        println!("๐Ÿงช Testing container info methods...");
254        
255        let web_info = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info")
256            .expect("Web container info should be available");
257        
258        // Test all the convenience methods
259        println!("๐Ÿ” Testing container info methods for: {}", web_info.container_id);
260        
261        // Test primary_url
262        if let Some(url) = web_info.primary_url() {
263            println!("   Primary URL: {}", url);
264            assert!(url.starts_with("http://localhost:"), "URL should start with http://localhost:");
265        }
266        
267        // Test url_for_port
268        if let Some(url) = web_info.url_for_port(80) {
269            println!("   URL for port 80: {}", url);
270            assert!(url.starts_with("http://localhost:"), "URL should start with http://localhost:");
271        }
272        
273        // Test host_port_for
274        if let Some(host_port) = web_info.host_port_for(80) {
275            println!("   Host port for container port 80: {}", host_port);
276            assert!(host_port > 0, "Host port should be valid");
277        }
278        
279        // Test ports_summary
280        let summary = web_info.ports_summary();
281        println!("   Ports summary: {}", summary);
282        assert!(!summary.is_empty(), "Ports summary should not be empty");
283        
284        println!("โœ… Container info methods test passed");
285        Ok(())
286    });
287    
288    println!("๐Ÿš€ Running tests with auto-port functionality...");
289    println!();
290    
291    // Run the tests
292    let config = TestConfig {
293        html_report: Some("auto-port-demo-report.html".to_string()),
294        ..Default::default()
295    };
296    
297    let exit_code = run_tests_with_config(config);
298    
299    println!();
300    if exit_code == 0 {
301        println!("๐ŸŽ‰ All tests passed! Auto-port functionality is working correctly.");
302        println!("๐Ÿ“Š Check the HTML report for detailed results.");
303    } else {
304        println!("โŒ Some tests failed. Check the output above for details.");
305    }
306    
307    std::process::exit(exit_code);
308}