mongodb_integration/
mongodb_integration.rs

1//! MongoDB integration example demonstrating Docker-based testing with rust-test-harness
2//! 
3//! This shows real-world database testing patterns:
4//! 1. Testing with real Docker containers managed by hooks
5//! 2. Database operations (CRUD)
6//! 3. Error handling and edge cases
7//! 4. Setup and teardown with hooks
8
9use rust_test_harness::{test, run_tests_with_config, TestConfig, ContainerConfig, before_each};
10use std::time::Duration;
11
12// Mock MongoDB client for demonstration
13struct MongoClient {
14    container_id: String,
15    connected: bool,
16}
17
18impl MongoClient {
19    fn new(container_id: String) -> Self {
20        Self {
21            container_id,
22            connected: false,
23        }
24    }
25    
26    fn connect(&mut self) -> Result<(), String> {
27        // Simulate connection
28        std::thread::sleep(Duration::from_millis(20));
29        self.connected = true;
30        Ok(())
31    }
32    
33    fn disconnect(&mut self) -> Result<(), String> {
34        // Simulate disconnection
35        std::thread::sleep(Duration::from_millis(10));
36        self.connected = false;
37        Ok(())
38    }
39    
40    fn insert_document(&self, collection: &str, document: &str) -> Result<(), String> {
41        if !self.connected {
42            return Err("Not connected to MongoDB".to_string());
43        }
44        
45        // Simulate document insertion
46        std::thread::sleep(Duration::from_millis(5));
47        println!("๐Ÿ“„ Inserted document into collection '{}': {}", collection, document);
48        Ok(())
49    }
50    
51    fn find_documents(&self, collection: &str, query: &str) -> Result<Vec<String>, String> {
52        if !self.connected {
53            return Err("Not connected to MongoDB".to_string());
54        }
55        
56        // Simulate document retrieval
57        std::thread::sleep(Duration::from_millis(3));
58        println!("๐Ÿ” Found documents in collection '{}' with query: {}", collection, query);
59        
60        // Return mock documents
61        Ok(vec![
62            format!("Document 1 matching: {}", query),
63            format!("Document 2 matching: {}", query),
64        ])
65    }
66}
67
68fn main() {
69    println!("๐Ÿณ MongoDB Integration Example with Container Hooks");
70    println!("==================================================");
71    println!();
72    
73    // Define container configuration
74    let mongo_container = ContainerConfig::new("mongo:5.0")
75        .port(27017, 27017)
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!("  Ports: {:?}", mongo_container.ports);
84    println!("  Environment: {:?}", mongo_container.env);
85    println!("  Name: {:?}", mongo_container.name);
86    println!("  Ready Timeout: {:?}", mongo_container.ready_timeout);
87    println!();
88    
89    // Clone for before_each hook
90    let mongo_container_before = mongo_container.clone();
91    
92    // Register before_each hook to start container
93    before_each(move |ctx| {
94        println!("๐Ÿ”„ before_each: Starting MongoDB container...");
95        
96        // Start the container
97        let container_id = mongo_container_before.start()
98            .map_err(|e| format!("Failed to start container: {}", e))?;
99        ctx.set_data("mongo_container_id", container_id.clone());
100        
101        println!("โœ… before_each: MongoDB container {} started", container_id);
102        Ok(())
103    });
104    
105    // Register after_each hook to stop container
106    let mongo_container_after = mongo_container.clone();
107    rust_test_harness::after_each(move |ctx| {
108        println!("๐Ÿ”„ after_each: Stopping MongoDB container...");
109        
110        // Get container ID from context
111        let container_id = ctx.get_data::<String>("mongo_container_id")
112            .expect("Container ID should be available");
113        
114        // Stop the container
115        mongo_container_after.stop(&container_id)
116            .map_err(|e| format!("Failed to stop container: {}", e))?;
117        
118        println!("โœ… after_each: MongoDB container {} stopped", container_id);
119        Ok(())
120    });
121    
122    // Test 1: Basic MongoDB operations
123    test("test_mongodb_basic_operations", |ctx| {
124        println!("๐Ÿงช Running test: test_mongodb_basic_operations");
125        
126        // Get container ID from context
127        let container_id = ctx.get_data::<String>("mongo_container_id")
128            .expect("Container ID should be available")
129            .to_string();
130        
131        // Create MongoDB client
132        let mut client = MongoClient::new(container_id);
133        
134        // Connect to MongoDB
135        client.connect()?;
136        
137        // Insert a document
138        client.insert_document("users", r#"{"name": "John Doe", "email": "john@example.com"}"#)?;
139        
140        // Find documents
141        let documents = client.find_documents("users", r#"{"name": "John Doe"}"#)?;
142        assert_eq!(documents.len(), 2);
143        
144        // Disconnect
145        client.disconnect()?;
146        
147        println!("โœ… test_mongodb_basic_operations passed");
148        Ok(())
149    });
150    
151    // Test 2: Multiple operations
152    test("test_mongodb_multiple_operations", |ctx| {
153        println!("๐Ÿงช Running test: test_mongodb_multiple_operations");
154        
155        // Get container ID from context
156        let container_id = ctx.get_data::<String>("mongo_container_id")
157            .expect("Container ID should be available")
158            .to_string();
159        
160        // Create MongoDB client
161        let mut client = MongoClient::new(container_id);
162        
163        // Connect to MongoDB
164        client.connect()?;
165        
166        // Insert multiple documents
167        client.insert_document("products", r#"{"name": "Laptop", "price": 999.99}"#)?;
168        client.insert_document("products", r#"{"name": "Mouse", "price": 29.99}"#)?;
169        client.insert_document("products", r#"{"name": "Keyboard", "price": 79.99}"#)?;
170        
171        // Find documents
172        let documents = client.find_documents("products", r#"{"price": {"$gt": 50}}"#)?;
173        assert_eq!(documents.len(), 2); // Laptop and Keyboard
174        
175        // Disconnect
176        client.disconnect()?;
177        
178        println!("โœ… test_mongodb_multiple_operations passed");
179        Ok(())
180    });
181    
182    // Test 3: Error handling
183    test("test_mongodb_error_handling", |ctx| {
184        println!("๐Ÿงช Running test: test_mongodb_error_handling");
185        
186        // Get container ID from context
187        let container_id = ctx.get_data::<String>("mongo_container_id")
188            .expect("Container ID should be available")
189            .to_string();
190        
191        // Create MongoDB client
192        let client = MongoClient::new(container_id);
193        
194        // Try to insert without connecting (should fail)
195        let result = client.insert_document("users", r#"{"name": "Test"}"#);
196        assert!(result.is_err());
197        assert_eq!(result.unwrap_err(), "Not connected to MongoDB");
198        
199        println!("โœ… test_mongodb_error_handling passed");
200        Ok(())
201    });
202    
203    // Test 4: Performance testing
204    test("test_mongodb_performance", |ctx| {
205        println!("๐Ÿงช Running test: test_mongodb_performance");
206        
207        // Get container ID from context
208        let container_id = ctx.get_data::<String>("mongo_container_id")
209            .expect("Container ID should be available")
210            .to_string();
211        
212        // Create MongoDB client
213        let mut client = MongoClient::new(container_id);
214        
215        // Connect to MongoDB
216        client.connect()?;
217        
218        // Simulate bulk operations
219        for i in 0..100 {
220            client.insert_document("bulk_data", &format!(r#"{{"index": {}, "data": "bulk_item_{}"}}"#, i, i))?;
221        }
222        
223        // Simulate bulk retrieval
224        let documents = client.find_documents("bulk_data", r#"{"index": {"$lt": 50}}"#)?;
225        assert_eq!(documents.len(), 2); // Mock always returns 2
226        
227        // Disconnect
228        client.disconnect()?;
229        
230        println!("โœ… test_mongodb_performance passed");
231        Ok(())
232    });
233    
234    println!("\n๐Ÿš€ Running MongoDB integration tests...");
235    println!("   Each test will get a fresh MongoDB container via before_each");
236    println!("   Each test will clean up its container via after_each");
237    println!();
238    
239    // Run tests with container hooks enabled
240    let config = TestConfig {
241        skip_hooks: Some(false),
242        ..Default::default()
243    };
244    
245    let result = run_tests_with_config(config);
246    
247    println!("\n๐Ÿ“Š Test Results:");
248    if result == 0 {
249        println!("โœ… All MongoDB integration tests passed!");
250        println!("๐ŸŽฏ Container lifecycle management working correctly");
251        println!("๐Ÿณ Each test got its own MongoDB container");
252        println!("๐Ÿงน Each test cleaned up its container properly");
253    } else {
254        println!("โŒ Some tests failed");
255    }
256    
257    println!("\n๐Ÿ’ก Key Benefits of this approach:");
258    println!("   โ€ข Clean separation of concerns");
259    println!("   โ€ข Each test gets a fresh container");
260    println!("   โ€ข Automatic cleanup via after_each");
261    println!("   โ€ข Easy to configure containers");
262    println!("   โ€ข No complex global state management");
263}