pub struct ContainerConfig {
pub image: String,
pub ports: Vec<(u16, u16)>,
pub auto_ports: Vec<u16>,
pub env: Vec<(String, String)>,
pub name: Option<String>,
pub ready_timeout: Duration,
pub auto_cleanup: bool,
}Fields§
§image: String§ports: Vec<(u16, u16)>§auto_ports: Vec<u16>§env: Vec<(String, String)>§name: Option<String>§ready_timeout: Duration§auto_cleanup: boolImplementations§
Source§impl ContainerConfig
impl ContainerConfig
Sourcepub fn new(image: &str) -> Self
pub fn new(image: &str) -> Self
Examples found in repository?
examples/user_specified_ports.rs (line 18)
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 (line 74)
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/auto_port_demo.rs (line 18)
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 (line 18)
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}Sourcepub fn port(self, host_port: u16, container_port: u16) -> Self
pub fn port(self, host_port: u16, container_port: u16) -> Self
Examples found in repository?
examples/user_specified_ports.rs (line 19)
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/auto_port_demo.rs (line 40)
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 (line 35)
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}Sourcepub fn env(self, key: &str, value: &str) -> Self
pub fn env(self, key: &str, value: &str) -> Self
Examples found in repository?
examples/user_specified_ports.rs (line 20)
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 (line 76)
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/auto_port_demo.rs (line 20)
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 (line 21)
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}Sourcepub fn name(self, name: &str) -> Self
pub fn name(self, name: &str) -> Self
Examples found in repository?
examples/user_specified_ports.rs (line 22)
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 (line 78)
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/auto_port_demo.rs (line 21)
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 (line 22)
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}Sourcepub fn ready_timeout(self, timeout: Duration) -> Self
pub fn ready_timeout(self, timeout: Duration) -> Self
Examples found in repository?
examples/user_specified_ports.rs (line 23)
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 (line 79)
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/auto_port_demo.rs (line 22)
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 (line 23)
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}Sourcepub fn auto_port(self, container_port: u16) -> Self
pub fn auto_port(self, container_port: u16) -> Self
Add a port that should be automatically assigned an available host port
Examples found in repository?
examples/user_specified_ports.rs (line 34)
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 (line 75)
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/auto_port_demo.rs (line 19)
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 (line 19)
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}Sourcepub fn no_auto_cleanup(self) -> Self
pub fn no_auto_cleanup(self) -> Self
Disable automatic cleanup (containers will persist after tests)
Sourcepub fn start(&self) -> Result<ContainerInfo, Box<dyn Error + Send + Sync>>
pub fn start(&self) -> Result<ContainerInfo, Box<dyn Error + Send + Sync>>
Start a container with this configuration using Docker API
Examples found in repository?
examples/user_specified_ports.rs (line 55)
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 (line 98)
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/auto_port_demo.rs (line 80)
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 (line 70)
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}Sourcepub fn stop(
&self,
container_id: &str,
) -> Result<(), Box<dyn Error + Send + Sync>>
pub fn stop( &self, container_id: &str, ) -> Result<(), Box<dyn Error + Send + Sync>>
Stop a container by ID using Docker API
Examples found in repository?
examples/user_specified_ports.rs (line 99)
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 (line 124)
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/auto_port_demo.rs (line 134)
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 (line 130)
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}Trait Implementations§
Source§impl Clone for ContainerConfig
impl Clone for ContainerConfig
Source§fn clone(&self) -> ContainerConfig
fn clone(&self) -> ContainerConfig
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreAuto Trait Implementations§
impl Freeze for ContainerConfig
impl RefUnwindSafe for ContainerConfig
impl Send for ContainerConfig
impl Sync for ContainerConfig
impl Unpin for ContainerConfig
impl UnwindSafe for ContainerConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more