Rust · 7994 bytes Raw Blame History
1 use anyhow::Result;
2 use tempfile::tempdir;
3 use tokio::time::{timeout, Duration};
4
5 use crate::config::Config;
6 use crate::node_manager::{NodeManager, DistributionStrategy};
7
8 /// Integration tests for Phase 1.3 Network & Storage Integration
9 ///
10 /// These tests verify that the networking and storage layers work together
11 /// correctly for peer-to-peer chunk sharing and file distribution.
12
13 #[tokio::test]
14 async fn test_node_manager_initialization() -> Result<()> {
15 // Test that NodeManager can be created and started
16 let config = Config::default();
17 let temp_dir = tempdir()?;
18
19 let mut node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
20
21 // Should be able to start (though networking may fail without proper setup)
22 let start_result = timeout(Duration::from_secs(5), node_manager.start()).await;
23
24 // Even if start fails due to network setup, the initialization should have worked
25 match start_result {
26 Ok(Ok(())) => {
27 // Success! Now shutdown
28 node_manager.shutdown().await?;
29 }
30 Ok(Err(_)) => {
31 // Expected - networking setup might fail in test environment
32 // But the important part is that NodeManager initialized
33 }
34 Err(_) => {
35 // Timeout - also expected in test environment
36 }
37 }
38
39 Ok(())
40 }
41
42 #[tokio::test]
43 async fn test_node_status_retrieval() -> Result<()> {
44 // Test that we can get node status
45 let config = Config::default();
46 let temp_dir = tempdir()?;
47
48 let node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
49
50 let status = node_manager.get_node_status().await;
51
52 // Verify status contains expected fields
53 assert_eq!(status.version, env!("CARGO_PKG_VERSION"));
54 assert_eq!(status.chunks_served, 0);
55 assert_eq!(status.chunks_retrieved, 0);
56 assert!(status.storage_capacity > 0);
57
58 Ok(())
59 }
60
61 #[tokio::test]
62 async fn test_file_storage_integration() -> Result<()> {
63 // Test that we can store files through the NodeManager
64 let config = Config::default();
65 let temp_dir = tempdir()?;
66
67 let node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
68
69 let file_id = "test-integration-file";
70 let filename = "integration_test.txt";
71 let test_data = b"Hello, ZephyrFS Integration! This tests the full stack.";
72
73 // Store file with local-only strategy
74 let file_hash = node_manager.store_file(
75 file_id,
76 test_data,
77 filename,
78 DistributionStrategy::LocalOnly
79 ).await?;
80
81 assert!(!file_hash.is_empty());
82
83 // Retrieve the file
84 let retrieved = node_manager.retrieve_file(file_id).await?;
85 assert!(retrieved.is_some());
86 assert_eq!(retrieved.unwrap(), test_data);
87
88 // Verify node status shows the stored file
89 let status = node_manager.get_node_status().await;
90 assert!(status.storage_used > 0);
91 assert!(status.file_count > 0);
92
93 // Delete the file
94 let deleted = node_manager.delete_file(file_id).await?;
95 assert!(deleted);
96
97 // Verify file is gone
98 let retrieved_after_delete = node_manager.retrieve_file(file_id).await?;
99 assert!(retrieved_after_delete.is_none());
100
101 Ok(())
102 }
103
104 #[tokio::test]
105 async fn test_chunk_level_operations() -> Result<()> {
106 // Test direct chunk operations through the integrated system
107 let config = Config::default();
108 let temp_dir = tempdir()?;
109
110 let node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
111
112 let chunk_id = "test-chunk-integration";
113 let chunk_data = b"This is test chunk data for integration testing.";
114
115 // For this test, we'll use the store_file method and then test chunk retrieval
116 // This tests the integration without accessing private fields
117 let temp_file_id = "temp-file-for-chunk-test";
118 let file_hash = node_manager.store_file(
119 temp_file_id,
120 chunk_data,
121 "chunk_test.bin",
122 DistributionStrategy::LocalOnly
123 ).await?;
124 assert!(!file_hash.is_empty());
125
126 // Retrieve file to verify it was stored
127 let retrieved_file = node_manager.retrieve_file(temp_file_id).await?;
128 assert!(retrieved_file.is_some());
129 assert_eq!(retrieved_file.unwrap(), chunk_data);
130
131 // Test peer chunk requests (should work locally)
132 let peer_chunk_result = node_manager.request_chunk_from_peers(chunk_id, &file_hash).await;
133 // This should return None since we don't have actual peers, but shouldn't error
134 assert!(peer_chunk_result.is_ok());
135
136 Ok(())
137 }
138
139 #[tokio::test]
140 async fn test_storage_capacity_integration() -> Result<()> {
141 // Test that storage capacity management works with the integrated system
142 let mut config = Config::default();
143 // Set very small capacity for testing
144 config.storage.max_storage = 1024; // 1KB
145
146 let temp_dir = tempdir()?;
147 let node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
148
149 // Get initial node status which includes storage info
150 let initial_status = node_manager.get_node_status().await;
151 assert_eq!(initial_status.storage_capacity, 1024);
152 assert_eq!(initial_status.storage_used, 0);
153
154 // Try to store a large file that exceeds capacity
155 let large_data = vec![0u8; 2048]; // 2KB - larger than capacity
156
157 let result = node_manager.store_file(
158 "large-file",
159 &large_data,
160 "large.bin",
161 DistributionStrategy::LocalOnly
162 ).await;
163
164 // Should fail due to capacity limits
165 assert!(result.is_err());
166
167 // Verify capacity info is still correct
168 let final_status = node_manager.get_node_status().await;
169 assert_eq!(final_status.storage_used, 0); // Nothing should have been stored
170
171 Ok(())
172 }
173
174 #[tokio::test]
175 async fn test_multiple_file_operations() -> Result<()> {
176 // Test storing multiple files and ensuring they're properly isolated
177 let config = Config::default();
178 let temp_dir = tempdir()?;
179
180 let node_manager = NodeManager::new(config, temp_dir.path().to_path_buf()).await?;
181
182 // Store multiple files
183 let files: Vec<(&str, &str, &[u8])> = vec![
184 ("file1", "test1.txt", b"First file content"),
185 ("file2", "test2.txt", b"Second file content with different data"),
186 ("file3", "test3.txt", b"Third file"),
187 ];
188
189 let mut stored_hashes = Vec::new();
190
191 for (file_id, filename, data) in &files {
192 let hash = node_manager.store_file(
193 file_id,
194 *data,
195 filename,
196 DistributionStrategy::LocalOnly
197 ).await?;
198 stored_hashes.push(hash);
199 }
200
201 // Verify all files can be retrieved correctly
202 for (file_id, _filename, expected_data) in &files {
203 let retrieved = node_manager.retrieve_file(file_id).await?;
204 assert!(retrieved.is_some());
205 assert_eq!(&retrieved.unwrap(), *expected_data);
206 }
207
208 // Verify node status shows correct metrics
209 let status = node_manager.get_node_status().await;
210 assert_eq!(status.file_count, 3);
211 assert!(status.storage_used > 0);
212 assert!(status.chunk_count > 0); // Should have some chunks
213
214 // Delete one file and verify others remain
215 let deleted = node_manager.delete_file("file2").await?;
216 assert!(deleted);
217
218 // Verify file2 is gone but others remain
219 assert!(node_manager.retrieve_file("file2").await?.is_none());
220 assert!(node_manager.retrieve_file("file1").await?.is_some());
221 assert!(node_manager.retrieve_file("file3").await?.is_some());
222
223 Ok(())
224 }
225
226 // Integration tests can be run individually with:
227 // cargo test test_node_manager_initialization
228 // cargo test test_node_status_retrieval
229 // cargo test test_file_storage_integration
230 // cargo test test_chunk_level_operations
231 // cargo test test_storage_capacity_integration
232 // cargo test test_multiple_file_operations
233