pub fn test<F>(name: &str, f: F)Examples found in repository?
examples/user_specified_ports.rs (lines 118-130)
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}More examples
examples/mongodb_integration.rs (lines 132-169)
68fn main() {
69 println!("๐ณ MongoDB Integration Example with Container Hooks");
70 println!("==================================================");
71 println!();
72
73 // Define container configuration with auto-port for MongoDB
74 let mongo_container = ContainerConfig::new("mongo:5.0")
75 .auto_port(27017) // Automatically assign available host port for MongoDB
76 .env("MONGO_INITDB_ROOT_USERNAME", "admin")
77 .env("MONGO_INITDB_ROOT_PASSWORD", "password")
78 .name("test_mongodb")
79 .ready_timeout(Duration::from_secs(30));
80
81 println!("๐ Container Configuration:");
82 println!(" Image: {}", mongo_container.image);
83 println!(" Auto-ports: {:?}", mongo_container.auto_ports);
84 println!(" Environment: {:?}", mongo_container.env);
85 println!(" Name: {:?}", mongo_container.name);
86 println!(" Ready Timeout: {:?}", mongo_container.ready_timeout);
87 println!(" Auto-cleanup: {}", mongo_container.auto_cleanup);
88 println!();
89
90 // Clone for before_each hook
91 let mongo_container_before = mongo_container.clone();
92
93 // Register before_each hook to start container
94 before_each(move |ctx| {
95 println!("๐ before_each: Starting MongoDB container...");
96
97 // Start the container and get ContainerInfo
98 let container_info = mongo_container_before.start()
99 .map_err(|e| format!("Failed to start container: {}", e))?;
100
101 // Store container info in context for tests to access
102 ctx.set_data("mongo_container_info", container_info.clone());
103
104 // Log container details including auto-assigned ports
105 println!("โ
before_each: MongoDB container {} started", container_info.container_id);
106 println!("๐ Container exposed on: {}", container_info.ports_summary());
107 if let Some(primary_url) = container_info.primary_url() {
108 println!("๐ Primary URL: {}", primary_url);
109 }
110
111 Ok(())
112 });
113
114 // Register after_each hook to stop container
115 let mongo_container_after = mongo_container.clone();
116 rust_test_harness::after_each(move |ctx| {
117 println!("๐ after_each: Stopping MongoDB container...");
118
119 // Get container info from context
120 let container_info = ctx.get_data::<rust_test_harness::ContainerInfo>("mongo_container_info")
121 .expect("Container info should be available");
122
123 // Stop the container
124 mongo_container_after.stop(&container_info.container_id)
125 .map_err(|e| format!("Failed to stop container: {}", e))?;
126
127 println!("โ
after_each: MongoDB container {} stopped", container_info.container_id);
128 Ok(())
129 });
130
131 // Test 1: Basic MongoDB operations
132 test("test_mongodb_basic_operations", |ctx| {
133 println!("๐งช Running test: test_mongodb_basic_operations");
134
135 // Get container info from context
136 let container_info = ctx.get_data::<rust_test_harness::ContainerInfo>("mongo_container_info")
137 .expect("Container info should be available");
138
139 // Show how to access port information
140 println!("๐ Container {} is running on:", container_info.container_id);
141 println!(" Ports: {}", container_info.ports_summary());
142 if let Some(primary_url) = container_info.primary_url() {
143 println!(" Primary URL: {}", primary_url);
144 }
145
146 // Get the MongoDB port (27017) and show the actual host port
147 if let Some(host_port) = container_info.host_port_for(27017) {
148 println!(" MongoDB accessible at: localhost:{}", host_port);
149 }
150
151 // Create MongoDB client
152 let mut client = MongoClient::new(container_info.container_id.clone());
153
154 // Connect to MongoDB
155 client.connect()?;
156
157 // Insert a document
158 client.insert_document("users", r#"{"name": "John Doe", "email": "john@example.com"}"#)?;
159
160 // Find documents
161 let documents = client.find_documents("users", r#"{"name": "John Doe"}"#)?;
162 assert_eq!(documents.len(), 2);
163
164 // Disconnect
165 client.disconnect()?;
166
167 println!("โ
test_mongodb_basic_operations passed");
168 Ok(())
169 });
170
171 // Test 2: Multiple operations
172 test("test_mongodb_multiple_operations", |ctx| {
173 println!("๐งช Running test: test_mongodb_multiple_operations");
174
175 // Get container info from context
176 let container_info = ctx.get_data::<rust_test_harness::ContainerInfo>("mongo_container_info")
177 .expect("Container info should be available");
178
179 // Show container status
180 println!("๐ Container {} status:", container_info.container_id);
181 println!(" Active ports: {}", container_info.ports_summary());
182
183 // Create MongoDB client
184 let mut client = MongoClient::new(container_info.container_id.clone());
185
186 // Connect to MongoDB
187 client.connect()?;
188
189 // Insert multiple documents
190 client.insert_document("products", r#"{"name": "Laptop", "price": 999.99}"#)?;
191 client.insert_document("products", r#"{"name": "Mouse", "price": 29.99}"#)?;
192 client.insert_document("products", r#"{"name": "Keyboard", "price": 79.99}"#)?;
193
194 // Find documents
195 let documents = client.find_documents("products", r#"{"price": {"$gt": 50}}"#)?;
196 assert_eq!(documents.len(), 2); // Laptop and Keyboard
197
198 // Disconnect
199 client.disconnect()?;
200
201 println!("โ
test_mongodb_multiple_operations passed");
202 Ok(())
203 });
204
205 // Test 3: Error handling
206 test("test_mongodb_error_handling", |ctx| {
207 println!("๐งช Running test: test_mongodb_error_handling");
208
209 // Get container ID from context
210 let container_id = ctx.get_data::<String>("mongo_container_id")
211 .expect("Container ID should be available")
212 .to_string();
213
214 // Create MongoDB client
215 let client = MongoClient::new(container_id);
216
217 // Try to insert without connecting (should fail)
218 let result = client.insert_document("users", r#"{"name": "Test"}"#);
219 assert!(result.is_err());
220 assert_eq!(result.unwrap_err(), "Not connected to MongoDB");
221
222 println!("โ
test_mongodb_error_handling passed");
223 Ok(())
224 });
225
226 // Test 4: Performance testing
227 test("test_mongodb_performance", |ctx| {
228 println!("๐งช Running test: test_mongodb_performance");
229
230 // Get container ID from context
231 let container_id = ctx.get_data::<String>("mongo_container_id")
232 .expect("Container ID should be available")
233 .to_string();
234
235 // Create MongoDB client
236 let mut client = MongoClient::new(container_id);
237
238 // Connect to MongoDB
239 client.connect()?;
240
241 // Simulate bulk operations
242 for i in 0..100 {
243 client.insert_document("bulk_data", &format!(r#"{{"index": {}, "data": "bulk_item_{}"}}"#, i, i))?;
244 }
245
246 // Simulate bulk retrieval
247 let documents = client.find_documents("bulk_data", r#"{"index": {"$lt": 50}}"#)?;
248 assert_eq!(documents.len(), 2); // Mock always returns 2
249
250 // Disconnect
251 client.disconnect()?;
252
253 println!("โ
test_mongodb_performance passed");
254 Ok(())
255 });
256
257 println!("\n๐ Running MongoDB integration tests...");
258 println!(" Each test will get a fresh MongoDB container via before_each");
259 println!(" Each test will clean up its container via after_each");
260 println!();
261
262 // Run tests with container hooks enabled
263 let config = TestConfig {
264 skip_hooks: Some(false),
265 ..Default::default()
266 };
267
268 let result = run_tests_with_config(config);
269
270 println!("\n๐ Test Results:");
271 if result == 0 {
272 println!("โ
All MongoDB integration tests passed!");
273 println!("๐ฏ Container lifecycle management working correctly");
274 println!("๐ณ Each test got its own MongoDB container");
275 println!("๐งน Each test cleaned up its container properly");
276 } else {
277 println!("โ Some tests failed");
278 }
279
280 println!("\n๐ก Key Benefits of this approach:");
281 println!(" โข Clean separation of concerns");
282 println!(" โข Each test gets a fresh container");
283 println!(" โข Automatic cleanup via after_each");
284 println!(" โข Easy to configure containers");
285 println!(" โข No complex global state management");
286}examples/html_reporting.rs (line 28)
18fn main() {
19 println!("๐งช HTML Reporting Example");
20 println!("=========================");
21 println!();
22
23 // Example 1: Basic HTML Report Generation
24 println!("๐ Example 1: Basic HTML Report");
25 println!("Generating basic HTML report...");
26
27 // Register some tests
28 test("basic_passing_test", |_| Ok(()));
29 test("another_passing_test", |_| Ok(()));
30
31 let config = TestConfig {
32 html_report: Some("basic_report.html".to_string()),
33 skip_hooks: None,
34 ..Default::default()
35 };
36
37 let result = run_tests_with_config(config);
38 println!("โ
Basic report generated with exit code: {}", result);
39 println!();
40
41 // Example 2: HTML Report with Mixed Results
42 println!("๐ Example 2: Mixed Results Report");
43 println!("Generating report with pass/fail/skip results...");
44
45 // Clear previous tests and register new ones
46 test("successful_test", |_| Ok(()));
47 test("failing_test", |_| Err("intentional failure".into()));
48 test("skipped_test", |_| Ok(()));
49
50 let config = TestConfig {
51 html_report: Some("mixed_results_report.html".to_string()),
52 skip_hooks: None,
53 ..Default::default()
54 };
55
56 let result = run_tests_with_config(config);
57 println!("โ
Mixed results report generated with exit code: {}", result);
58 println!();
59
60 // Example 3: HTML Report with Rich Metadata
61 println!("๐ Example 3: Rich Metadata Report");
62 println!("Generating report with tags, timeouts, and Docker...");
63
64 // Clear previous tests and register new ones with realistic scenarios
65 test("tagged_test", |_| Ok(()));
66 test("timeout_test", |_| Ok(()));
67 test("docker_integration_test", |_| Ok(()));
68 test("database_connection_test", |_| Ok(()));
69 test("api_endpoint_test", |_| Ok(()));
70
71 // Note: In a real scenario, you'd use test_with_tags and test_with_docker
72 // For this example, we'll simulate the metadata by showing different test types
73
74 let config = TestConfig {
75 html_report: Some("rich_metadata_report.html".to_string()),
76 skip_hooks: None,
77 ..Default::default()
78 };
79
80 let result = run_tests_with_config(config);
81 println!("โ
Rich metadata report generated with exit code: {}", result);
82 println!(" ๐ 5 tests with different types (tags, timeouts, Docker, DB, API)");
83 println!();
84
85 // Example 4: Large Test Suite Report
86 println!("๐ Example 4: Large Test Suite Report");
87 println!("Generating report for many tests with realistic mixed results...");
88
89 // Clear previous tests and register many tests with realistic scenarios
90 for i in 0..25 {
91 match i {
92 // Some tests pass normally
93 0..=15 => {
94 test(&format!("large_suite_test_{}", i), |_| Ok(()));
95 },
96 // Some tests fail with different error types
97 16..=19 => {
98 test(&format!("large_suite_test_{}", i), move |_| {
99 Err(format!("Test {} failed due to assertion error", i).into())
100 });
101 },
102 // Some tests have timeouts
103 20..=22 => {
104 test(&format!("large_suite_test_{}", i), move |_| {
105 // Simulate a test that takes too long and fails due to timeout
106 std::thread::sleep(Duration::from_millis(50));
107 Err(format!("Test {} failed due to timeout (exceeded 30ms limit)", i).into())
108 });
109 },
110 // Some tests panic
111 23 => {
112 test(&format!("large_suite_test_{}", i), move |_| {
113 panic!("Test {} panicked due to unexpected condition", i);
114 });
115 },
116 // Some tests are skipped (simulated by returning error)
117 24 => {
118 test(&format!("large_suite_test_{}", i), |_| {
119 Err("Test skipped due to missing dependencies".into())
120 });
121 },
122 _ => unreachable!(),
123 }
124 }
125
126 let config = TestConfig {
127 html_report: Some("large_suite_report.html".to_string()),
128 skip_hooks: None,
129 ..Default::default()
130 };
131
132 let result = run_tests_with_config(config);
133 println!("โ
Large suite report generated with exit code: {}", result);
134 println!(" ๐ 16 tests passed, 7 failed (4 errors + 3 timeouts), 1 panic, 1 skipped");
135 println!();
136
137 // Example 5: Environment Variable Configuration
138 println!("๐ Example 5: Environment Variable Configuration");
139 println!("Setting TEST_HTML_REPORT environment variable...");
140
141 // Set environment variable
142 std::env::set_var("TEST_HTML_REPORT", "env_var_report.html");
143
144 // Clear previous tests and register new ones
145 test("env_test", |_| Ok(()));
146
147 let config = TestConfig::default();
148 println!("๐ Config HTML report path: {:?}", config.html_report);
149
150 let result = run_tests_with_config(config);
151 println!("โ
Environment variable report generated with exit code: {}", result);
152 println!();
153
154 // Example 6: Performance Testing Report
155 println!("๐ Example 6: Performance Testing Report");
156 println!("Generating report for performance tests with realistic scenarios...");
157
158 // Clear previous tests and register performance tests with mixed results
159 for i in 0..15 {
160 match i {
161 // Fast tests that pass
162 0..=8 => {
163 test(&format!("perf_test_{}", i), |_| {
164 // Simulate some work
165 std::thread::sleep(Duration::from_millis(5));
166 Ok(())
167 });
168 },
169 // Medium tests that pass
170 9..=11 => {
171 test(&format!("perf_test_{}", i), |_| {
172 // Simulate medium work
173 std::thread::sleep(Duration::from_millis(20));
174 Ok(())
175 });
176 },
177 // Slow tests that pass
178 12..=13 => {
179 test(&format!("perf_test_{}", i), |_| {
180 // Simulate slow but successful tests
181 std::thread::sleep(Duration::from_millis(50));
182 Ok(())
183 });
184 },
185 // One test that fails due to timeout
186 14 => {
187 test(&format!("perf_test_{}", i), |_| {
188 // Simulate a test that takes too long and fails due to timeout
189 std::thread::sleep(Duration::from_millis(100));
190 Err("Performance test exceeded expected time limit (50ms)".into())
191 });
192 },
193 _ => unreachable!(),
194 }
195 }
196
197 let config = TestConfig {
198 html_report: Some("performance_report.html".to_string()),
199 max_concurrency: Some(4),
200 skip_hooks: None,
201 ..Default::default()
202 };
203
204 let result = run_tests_with_config(config);
205 println!("โ
Performance report generated with exit code: {}", result);
206 println!(" ๐ 14 tests passed, 1 failed (timeout)");
207 println!();
208
209 // Summary
210 println!("๐ HTML Reporting Examples Complete!");
211 println!("=====================================");
212 println!();
213 println!("Generated HTML reports (stored in target/test-reports/):");
214 println!(" ๐ basic_report.html - Basic functionality");
215 println!(" ๐ mixed_results_report.html - Pass/fail/skip results");
216 println!(" ๐ rich_metadata_report.html - Rich test metadata");
217 println!(" ๐ large_suite_report.html - Large test suite");
218 println!(" ๐ env_var_report.html - Environment variable config");
219 println!(" ๐ performance_report.html - Performance testing");
220 println!();
221 println!("๐ All reports are automatically organized in target/test-reports/");
222 println!();
223 println!("๐ HTML Report Features:");
224 println!(" ๐ฝ Expandable test details - Click any test to expand");
225 println!(" ๐ Real-time search - Search by name, status, or tags");
226 println!(" โจ๏ธ Keyboard shortcuts - Ctrl+F (search), Ctrl+A (expand all)");
227 println!(" ๐จ Auto-expand failed - Failed tests automatically expand");
228 println!(" ๐ฑ Responsive design - Works on all devices");
229 println!();
230 println!("๐ก Usage Tips:");
231 println!(" โข Open any .html file in your web browser");
232 println!(" โข Use Ctrl+F to search for specific tests");
233 println!(" โข Click test headers to expand/collapse details");
234 println!(" โข Failed tests are automatically expanded for visibility");
235 println!(" โข Reports work great in CI/CD pipelines and team sharing");
236 println!(" โข All reports are neatly organized in target/test-reports/");
237 println!();
238 println!("๐ง Configuration Options:");
239 println!(" โข Set TEST_HTML_REPORT environment variable");
240 println!(" โข Use TestConfig.html_report for programmatic control");
241 println!(" โข Combine with other config options (filtering, concurrency)");
242 println!(" โข Reports automatically go to target/test-reports/ for clean organization");
243}examples/auto_port_demo.rs (lines 158-174)
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}examples/container_port_access.rs (lines 149-197)
12fn main() {
13 println!("๐ Container Port and URL Access Example");
14 println!("========================================");
15 println!();
16
17 // Example 1: Web service with multiple auto-ports
18 let web_container = ContainerConfig::new("nginx:alpine")
19 .auto_port(80) // Auto-assign port for HTTP
20 .auto_port(443) // Auto-assign port for HTTPS
21 .env("NGINX_HOST", "localhost")
22 .name("web_service")
23 .ready_timeout(Duration::from_secs(15));
24
25 // Example 2: Database with auto-port
26 let db_container = ContainerConfig::new("postgres:13-alpine")
27 .auto_port(5432) // Auto-assign port for PostgreSQL
28 .env("POSTGRES_PASSWORD", "testpass")
29 .env("POSTGRES_DB", "testdb")
30 .name("test_database")
31 .ready_timeout(Duration::from_secs(20));
32
33 // Example 3: Mixed configuration (manual + auto ports)
34 let api_container = ContainerConfig::new("httpd:alpine")
35 .port(8080, 80) // Manual port mapping
36 .auto_port(9090) // Auto-assign port for API
37 .auto_port(9091) // Auto-assign port for metrics
38 .env("API_VERSION", "v1")
39 .name("api_service")
40 .ready_timeout(Duration::from_secs(15));
41
42 println!("๐ Container Configurations:");
43 println!("1. Web Service:");
44 println!(" - Image: {}", web_container.image);
45 println!(" - Auto-ports: {:?}", web_container.auto_ports);
46 println!(" - Will auto-assign host ports for container ports 80 and 443");
47
48 println!("2. Database:");
49 println!(" - Image: {}", db_container.image);
50 println!(" - Auto-ports: {:?}", db_container.auto_ports);
51 println!(" - Will auto-assign host port for container port 5432");
52
53 println!("3. API Service:");
54 println!(" - Image: {}", api_container.image);
55 println!(" - Manual ports: {:?}", api_container.ports);
56 println!(" - Auto-ports: {:?}", api_container.auto_ports);
57 println!(" - Manual mapping: 8080 -> 80, Auto-assign: 9090, 9091");
58 println!();
59
60 // Clone containers for hooks
61 let web_before = web_container.clone();
62 let db_before = db_container.clone();
63 let api_before = api_container.clone();
64
65 // Start containers in before_each
66 before_each(move |ctx| {
67 println!("๐ before_each: Starting containers and capturing port info...");
68
69 // Start web container
70 let web_info = web_before.start()
71 .map_err(|e| format!("Failed to start web container: {}", e))?;
72 ctx.set_data("web_container_info", web_info.clone());
73
74 println!("โ
Web container started: {}", web_info.container_id);
75 println!(" ๐ Port mappings: {}", web_info.ports_summary());
76 println!(" ๐ Primary URL: {}", web_info.primary_url().unwrap_or("None"));
77
78 // Show specific port access
79 if let Some(http_port) = web_info.host_port_for(80) {
80 println!(" ๐ HTTP accessible at: http://localhost:{}", http_port);
81 }
82 if let Some(https_port) = web_info.host_port_for(443) {
83 println!(" ๐ HTTPS accessible at: https://localhost:{}", https_port);
84 }
85
86 // Start database container
87 let db_info = db_before.start()
88 .map_err(|e| format!("Failed to start db container: {}", e))?;
89 ctx.set_data("db_container_info", db_info.clone());
90
91 println!("โ
Database container started: {}", db_info.container_id);
92 println!(" ๐ Port mappings: {}", db_info.ports_summary());
93 if let Some(db_port) = db_info.host_port_for(5432) {
94 println!(" ๐๏ธ PostgreSQL accessible at: localhost:{}", db_port);
95 println!(" ๐ Connection string: postgresql://user:pass@localhost:{}/testdb", db_port);
96 }
97
98 // Start API container
99 let api_info = api_before.start()
100 .map_err(|e| format!("Failed to start api container: {}", e))?;
101 ctx.set_data("api_container_info", api_info.clone());
102
103 println!("โ
API container started: {}", api_info.container_id);
104 println!(" ๐ Port mappings: {}", api_info.ports_summary());
105
106 // Show both manual and auto ports
107 if let Some(http_port) = api_info.host_port_for(80) {
108 println!(" ๐ HTTP API at: http://localhost:{} (manual mapping)", http_port);
109 }
110 if let Some(api_port) = api_info.host_port_for(9090) {
111 println!(" ๐ง API endpoint at: http://localhost:{} (auto-assigned)", api_port);
112 }
113 if let Some(metrics_port) = api_info.host_port_for(9091) {
114 println!(" ๐ Metrics at: http://localhost:{} (auto-assigned)", metrics_port);
115 }
116
117 println!("๐ All containers started with auto-assigned ports!");
118 Ok(())
119 });
120
121 // Cleanup containers in after_each
122 let web_after = web_container.clone();
123 let db_after = db_container.clone();
124 let api_after = api_container.clone();
125
126 after_each(move |ctx| {
127 println!("๐งน after_each: Cleaning up containers...");
128
129 if let Some(web_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info") {
130 let _ = web_after.stop(&web_info.container_id);
131 println!("๐ Stopped web container: {}", web_info.container_id);
132 }
133
134 if let Some(db_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("db_container_info") {
135 let _ = db_after.stop(&db_info.container_id);
136 println!("๐ Stopped database container: {}", db_info.container_id);
137 }
138
139 if let Some(api_info) = ctx.get_data::<rust_test_harness::ContainerInfo>("api_container_info") {
140 let _ = api_after.stop(&api_info.container_id);
141 println!("๐ Stopped API container: {}", api_info.container_id);
142 }
143
144 println!("โ
All containers cleaned up!");
145 Ok(())
146 });
147
148 // Test 1: Web service port access
149 test("test_web_service_port_access", |ctx| {
150 println!("๐งช Testing web service port access...");
151
152 let web_info = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info")
153 .expect("Web container info should be available");
154
155 println!("๐ Demonstrating port access methods:");
156
157 // Method 1: Get specific host port for container port
158 if let Some(http_port) = web_info.host_port_for(80) {
159 println!(" โ
HTTP port 80 is mapped to host port: {}", http_port);
160 println!(" ๐ก Use this for HTTP client connections");
161 }
162
163 if let Some(https_port) = web_info.host_port_for(443) {
164 println!(" โ
HTTPS port 443 is mapped to host port: {}", https_port);
165 println!(" ๐ก Use this for HTTPS client connections");
166 }
167
168 // Method 2: Get ready-to-use URLs
169 if let Some(http_url) = web_info.url_for_port(80) {
170 println!(" ๐ HTTP URL: {}", http_url);
171 println!(" ๐ก Ready to use in HTTP requests");
172 }
173
174 if let Some(https_url) = web_info.url_for_port(443) {
175 println!(" ๐ HTTPS URL: {}", https_url);
176 println!(" ๐ก Ready to use in HTTPS requests (change http:// to https://)");
177 }
178
179 // Method 3: Get all URLs
180 println!(" ๐ All service URLs:");
181 for (i, url) in web_info.urls.iter().enumerate() {
182 println!(" {}. {}", i + 1, url);
183 }
184
185 // Method 4: Get all port mappings
186 println!(" ๐ All port mappings:");
187 for (host_port, container_port) in &web_info.port_mappings {
188 println!(" Host {} -> Container {}", host_port, container_port);
189 }
190
191 // Verify ports are different (auto-assigned)
192 assert!(web_info.host_port_for(80).unwrap() != web_info.host_port_for(443).unwrap(),
193 "Auto-assigned ports should be different");
194
195 println!("โ
Web service port access test passed");
196 Ok(())
197 });
198
199 // Test 2: Database connection string generation
200 test("test_database_connection_info", |ctx| {
201 println!("๐งช Testing database connection info generation...");
202
203 let db_info = ctx.get_data::<rust_test_harness::ContainerInfo>("db_container_info")
204 .expect("Database container info should be available");
205
206 println!("๐๏ธ Database connection information:");
207
208 // Get the auto-assigned port for PostgreSQL
209 let db_port = db_info.host_port_for(5432)
210 .expect("PostgreSQL port should be exposed");
211
212 println!(" ๐ PostgreSQL running on: localhost:{}", db_port);
213
214 // Generate connection strings using the auto-assigned port
215 let connection_string = format!("postgresql://admin:testpass@localhost:{}/testdb", db_port);
216 let jdbc_url = format!("jdbc:postgresql://localhost:{}/testdb", db_port);
217 let docker_internal = format!("postgresql://admin:testpass@{}:5432/testdb", db_info.container_id);
218
219 println!(" ๐ Connection strings:");
220 println!(" Standard: {}", connection_string);
221 println!(" JDBC: {}", jdbc_url);
222 println!(" Docker internal: {}", docker_internal);
223
224 // Show how to use this in real applications
225 println!(" ๐ก Usage examples:");
226 println!(" - Set DATABASE_URL={}", connection_string);
227 println!(" - Use in tests: connect to localhost:{}", db_port);
228 println!(" - Container-to-container: use port 5432");
229
230 // Verify port is valid
231 assert!(db_port > 0, "Database port should be valid");
232 println!("โ
Database connection info test passed");
233
234 Ok(())
235 });
236
237 // Test 3: Mixed port configuration
238 test("test_mixed_port_configuration", |ctx| {
239 println!("๐งช Testing mixed port configuration (manual + auto)...");
240
241 let api_info = ctx.get_data::<rust_test_harness::ContainerInfo>("api_container_info")
242 .expect("API container info should be available");
243
244 println!("๐ Mixed port configuration analysis:");
245
246 // Manual port mapping
247 let http_port = api_info.host_port_for(80)
248 .expect("HTTP port should be mapped");
249 println!(" ๐ Manual mapping - HTTP port 80 -> host port: {}", http_port);
250 assert_eq!(http_port, 8080, "Manual port should be exactly as specified");
251
252 // Auto-assigned ports
253 let api_port = api_info.host_port_for(9090)
254 .expect("API port should be auto-assigned");
255 let metrics_port = api_info.host_port_for(9091)
256 .expect("Metrics port should be auto-assigned");
257
258 println!(" ๐ฒ Auto-assigned - API port 9090 -> host port: {}", api_port);
259 println!(" ๐ฒ Auto-assigned - Metrics port 9091 -> host port: {}", metrics_port);
260
261 // Verify auto-assigned ports are different from manual port
262 assert_ne!(api_port, 8080, "Auto-assigned port should be different from manual port");
263 assert_ne!(metrics_port, 8080, "Auto-assigned port should be different from manual port");
264 assert_ne!(api_port, metrics_port, "Auto-assigned ports should be different from each other");
265
266 println!(" ๐ Service endpoints:");
267 println!(" Main API: http://localhost:{}/", http_port);
268 println!(" API v2: http://localhost:{}/", api_port);
269 println!(" Metrics: http://localhost:{}/metrics", metrics_port);
270
271 // Show complete port summary
272 println!(" ๐ Complete port summary: {}", api_info.ports_summary());
273
274 println!("โ
Mixed port configuration test passed");
275 Ok(())
276 });
277
278 // Test 4: Container info convenience methods
279 test("test_container_info_convenience_methods", |ctx| {
280 println!("๐งช Testing ContainerInfo convenience methods...");
281
282 let web_info = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info")
283 .expect("Web container info should be available");
284
285 println!("๐ง Testing all ContainerInfo methods:");
286
287 // Test primary_url()
288 if let Some(primary_url) = web_info.primary_url() {
289 println!(" โ
primary_url(): {}", primary_url);
290 assert!(primary_url.starts_with("http://localhost:"), "Primary URL should be valid");
291 }
292
293 // Test url_for_port()
294 if let Some(url_80) = web_info.url_for_port(80) {
295 println!(" โ
url_for_port(80): {}", url_80);
296 assert!(url_80.contains("localhost:"), "URL should contain localhost");
297 }
298
299 // Test host_port_for()
300 if let Some(port_80) = web_info.host_port_for(80) {
301 println!(" โ
host_port_for(80): {}", port_80);
302 assert!(port_80 > 0, "Port should be positive");
303 }
304
305 // Test ports_summary()
306 let summary = web_info.ports_summary();
307 println!(" โ
ports_summary(): {}", summary);
308 assert!(summary.contains("->"), "Summary should contain port mappings");
309
310 // Test direct field access
311 println!(" โ
container_id: {}", web_info.container_id);
312 println!(" โ
image: {}", web_info.image);
313 println!(" โ
auto_cleanup: {}", web_info.auto_cleanup);
314
315 println!(" ๐ All URLs:");
316 for (i, url) in web_info.urls.iter().enumerate() {
317 println!(" {}. {}", i + 1, url);
318 }
319
320 println!(" ๐ All port mappings:");
321 for (host_port, container_port) in &web_info.port_mappings {
322 println!(" {} -> {}", host_port, container_port);
323 }
324
325 println!("โ
Container info convenience methods test passed");
326 Ok(())
327 });
328
329 // Test 5: Real-world usage patterns
330 test("test_real_world_usage_patterns", |ctx| {
331 println!("๐งช Testing real-world usage patterns...");
332
333 let web_info = ctx.get_data::<rust_test_harness::ContainerInfo>("web_container_info")
334 .expect("Web container info should be available");
335 let db_info = ctx.get_data::<rust_test_harness::ContainerInfo>("db_container_info")
336 .expect("Database container info should be available");
337
338 println!("๐ Real-world usage examples:");
339
340 // Pattern 1: HTTP client testing
341 if let Some(web_url) = web_info.primary_url() {
342 println!(" ๐ HTTP Client Testing:");
343 println!(" Base URL: {}", web_url);
344 println!(" GET {}/health", web_url);
345 println!(" POST {}/api/users", web_url);
346 println!(" Code: let response = reqwest::get(\"{}/health\").await?;", web_url);
347 }
348
349 // Pattern 2: Database testing
350 if let Some(db_port) = db_info.host_port_for(5432) {
351 println!(" ๐๏ธ Database Testing:");
352 println!(" Connection: localhost:{}", db_port);
353 println!(" Code: let conn = PgConnection::establish(\"postgresql://user:pass@localhost:{}/db\")?;", db_port);
354 }
355
356 // Pattern 3: Environment variable setup
357 println!(" ๐ง Environment Variables:");
358 if let Some(web_port) = web_info.host_port_for(80) {
359 println!(" export WEB_SERVICE_URL=http://localhost:{}", web_port);
360 }
361 if let Some(db_port) = db_info.host_port_for(5432) {
362 println!(" export DATABASE_URL=postgresql://localhost:{}/testdb", db_port);
363 }
364
365 // Pattern 4: Docker-compose style networking
366 println!(" ๐ณ Container Networking:");
367 println!(" External: Use auto-assigned host ports");
368 println!(" Internal: Use original container ports");
369 println!(" Web -> DB: {}:5432 (container-to-container)", db_info.container_id);
370
371 // Pattern 5: Health checks
372 println!(" โค๏ธ Health Checks:");
373 if let Some(web_url) = web_info.primary_url() {
374 println!(" Health endpoint: {}/health", web_url);
375 println!(" Ready endpoint: {}/ready", web_url);
376 }
377
378 println!("โ
Real-world usage patterns test passed");
379 Ok(())
380 });
381
382 println!("๐ Running container port access tests...");
383 println!();
384
385 // Run the tests
386 let config = TestConfig {
387 html_report: Some("container-port-access-report.html".to_string()),
388 ..Default::default()
389 };
390
391 let exit_code = run_tests_with_config(config);
392
393 println!();
394 if exit_code == 0 {
395 println!("๐ All tests passed! Port access functionality is working correctly.");
396 println!("๐ Check the HTML report for detailed results.");
397 println!();
398 println!("๐ก Key Takeaways:");
399 println!(" โข Use auto_port() to avoid port conflicts");
400 println!(" โข Access host ports with host_port_for(container_port)");
401 println!(" โข Get ready-to-use URLs with url_for_port(container_port)");
402 println!(" โข Use ports_summary() for human-readable port info");
403 println!(" โข ContainerInfo provides complete access to all port information");
404 } else {
405 println!("โ Some tests failed. Check the output above for details.");
406 }
407
408 std::process::exit(exit_code);
409}