1use rust_test_harness::{test, before_each, after_each, run_tests_with_config, TestConfig, ContainerConfig};
10use std::time::Duration;
11
12fn main() {
13 println!("๐ Container Port and URL Access Example");
14 println!("========================================");
15 println!();
16
17 let web_container = ContainerConfig::new("nginx:alpine")
19 .auto_port(80) .auto_port(443) .env("NGINX_HOST", "localhost")
22 .name("web_service")
23 .ready_timeout(Duration::from_secs(15));
24
25 let db_container = ContainerConfig::new("postgres:13-alpine")
27 .auto_port(5432) .env("POSTGRES_PASSWORD", "testpass")
29 .env("POSTGRES_DB", "testdb")
30 .name("test_database")
31 .ready_timeout(Duration::from_secs(20));
32
33 let api_container = ContainerConfig::new("httpd:alpine")
35 .port(8080, 80) .auto_port(9090) .auto_port(9091) .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 let web_before = web_container.clone();
62 let db_before = db_container.clone();
63 let api_before = api_container.clone();
64
65 before_each(move |ctx| {
67 println!("๐ before_each: Starting containers and capturing port info...");
68
69 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 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 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 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 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 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("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 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 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 println!(" ๐ All service URLs:");
181 for (i, url) in web_info.urls.iter().enumerate() {
182 println!(" {}. {}", i + 1, url);
183 }
184
185 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 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("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 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 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 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 assert!(db_port > 0, "Database port should be valid");
232 println!("โ
Database connection info test passed");
233
234 Ok(())
235 });
236
237 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 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 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 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 println!(" ๐ Complete port summary: {}", api_info.ports_summary());
273
274 println!("โ
Mixed port configuration test passed");
275 Ok(())
276 });
277
278 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 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 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 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 let summary = web_info.ports_summary();
307 println!(" โ
ports_summary(): {}", summary);
308 assert!(summary.contains("->"), "Summary should contain port mappings");
309
310 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("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 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 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 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 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 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 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}