zoe_client/system_check/
storage.rs1use super::{SystemCheckConfig, TestInfo, TestResult};
8use crate::{Client, services::MessagesManagerTrait};
9use serde::{Deserialize, Serialize};
10use std::time::{SystemTime, UNIX_EPOCH};
11use tracing::{debug, info};
12use zoe_wire_protocol::{
13 Content, KeyPair, Kind, Message, MessageFull, MessageV0, MessageV0Header, Tag,
14};
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18pub struct SystemCheckTestMessage {
19 pub test_id: String,
21 pub timestamp: u64,
23 pub data: Vec<u8>,
25 pub checksum: u32,
27}
28
29impl SystemCheckTestMessage {
30 pub fn new(test_id: String, data_size: usize) -> Self {
32 use rand::{RngCore, SeedableRng};
33 let mut rng = rand::rngs::StdRng::from_entropy();
34 let data: Vec<u8> = (0..data_size).map(|_| rng.next_u32() as u8).collect();
35 let checksum = crc32fast::hash(&data);
36 let timestamp = SystemTime::now()
37 .duration_since(UNIX_EPOCH)
38 .unwrap()
39 .as_secs();
40
41 Self {
42 test_id,
43 timestamp,
44 data,
45 checksum,
46 }
47 }
48
49 pub fn verify_integrity(&self) -> bool {
51 crc32fast::hash(&self.data) == self.checksum
52 }
53}
54
55pub async fn run_tests(client: &Client, config: &SystemCheckConfig) -> Vec<TestInfo> {
57 let mut tests = Vec::new();
58
59 tests.push(test_message_storage(client, config).await);
61
62 tests.push(test_message_integrity(client, config).await);
64
65 tests
66}
67
68async fn test_message_storage(client: &Client, config: &SystemCheckConfig) -> TestInfo {
70 let mut test = TestInfo::new("Message Storage");
71
72 debug!(
73 "Testing message storage with {} messages...",
74 config.storage_test_count
75 );
76
77 let mut stored_messages = Vec::new();
78
79 for i in 0..config.storage_test_count {
81 let test_message = SystemCheckTestMessage::new(
82 format!("storage_test_{i}"),
83 256, );
85
86 let serialized_content = match postcard::to_stdvec(&test_message) {
88 Ok(data) => data,
89 Err(e) => {
90 let error = format!("Failed to serialize test message {i}: {e}");
91 return test.with_result(TestResult::Failed { error });
92 }
93 };
94
95 use rand::SeedableRng;
97 let mut rng = rand::rngs::StdRng::from_entropy();
98 let temp_keypair = KeyPair::generate_ed25519(&mut rng);
99
100 let message = Message::MessageV0(MessageV0 {
101 header: MessageV0Header {
102 sender: temp_keypair.public_key(),
103 when: SystemTime::now()
104 .duration_since(UNIX_EPOCH)
105 .unwrap()
106 .as_secs(),
107 kind: Kind::Ephemeral(3600), tags: vec![Tag::Protected],
109 },
110 content: Content::Raw(serialized_content),
111 });
112
113 let message_full = match MessageFull::new(message, &temp_keypair) {
114 Ok(msg) => msg,
115 Err(e) => {
116 let error = format!("Failed to create MessageFull for test message {i}: {e}");
117 return test.with_result(TestResult::Failed { error });
118 }
119 };
120
121 match client.message_manager().publish(message_full).await {
122 Ok(publish_result) => {
123 debug!("Stored message {} with result: {:?}", i, publish_result);
124 stored_messages.push((publish_result, test_message));
125 }
126 Err(e) => {
127 let error = format!("Failed to store test message {i}: {e}");
128 return test.with_result(TestResult::Failed { error });
129 }
130 }
131 }
132
133 test.add_detail(format!(
134 "Successfully stored {} test messages",
135 stored_messages.len()
136 ));
137
138 info!("Message storage test completed successfully");
142 test.with_result(TestResult::Passed)
143}
144
145async fn test_message_integrity(client: &Client, _config: &SystemCheckConfig) -> TestInfo {
147 let mut test = TestInfo::new("Message Integrity");
148
149 debug!("Testing message integrity...");
150
151 let test_message = SystemCheckTestMessage::new(
153 "integrity_test".to_string(),
154 1024, );
156
157 if !test_message.verify_integrity() {
159 let error = "Test message failed integrity check before storage".to_string();
160 return test.with_result(TestResult::Failed { error });
161 }
162
163 let serialized_content = match postcard::to_stdvec(&test_message) {
165 Ok(data) => data,
166 Err(e) => {
167 let error = format!("Failed to serialize integrity test message: {e}");
168 return test.with_result(TestResult::Failed { error });
169 }
170 };
171
172 use rand::SeedableRng;
174 let mut rng = rand::rngs::StdRng::from_entropy();
175 let temp_keypair = KeyPair::generate_ed25519(&mut rng);
176
177 let message = Message::MessageV0(MessageV0 {
178 header: MessageV0Header {
179 sender: temp_keypair.public_key(),
180 when: SystemTime::now()
181 .duration_since(UNIX_EPOCH)
182 .unwrap()
183 .as_secs(),
184 kind: Kind::Ephemeral(3600), tags: vec![Tag::Protected],
186 },
187 content: Content::Raw(serialized_content),
188 });
189
190 let message_full = match MessageFull::new(message, &temp_keypair) {
191 Ok(msg) => msg,
192 Err(e) => {
193 let error = format!("Failed to create MessageFull for integrity test: {e}");
194 return test.with_result(TestResult::Failed { error });
195 }
196 };
197
198 match client.message_manager().publish(message_full).await {
199 Ok(publish_result) => {
200 test.add_detail(format!(
201 "Stored integrity test message with result: {publish_result:?}"
202 ));
203 test.add_detail(format!("Data size: {} bytes", test_message.data.len()));
204 test.add_detail(format!("Checksum: {:08x}", test_message.checksum));
205
206 info!("Message integrity test completed successfully");
207 test.with_result(TestResult::Passed)
208 }
209 Err(e) => {
210 let error = format!("Failed to store integrity test message: {e}");
211 test.with_result(TestResult::Failed { error })
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::Client;
220 use tempfile::TempDir;
221
222 async fn create_test_client() -> (Client, TempDir) {
223 let temp_dir = TempDir::new().unwrap();
224 let media_storage_path = temp_dir.path().join("blobs");
225 let db_storage_path = temp_dir.path().join("db");
226
227 let client = {
228 let mut builder = Client::builder();
229 builder.media_storage_dir_pathbuf(media_storage_path);
230 builder.db_storage_dir_pathbuf(db_storage_path);
231 builder.autoconnect(false);
232 builder.build().await.unwrap()
233 };
234
235 (client, temp_dir)
236 }
237
238 #[test]
239 fn test_system_check_test_message_creation() {
240 let message = SystemCheckTestMessage::new("test_id".to_string(), 100);
241
242 assert_eq!(message.test_id, "test_id");
243 assert_eq!(message.data.len(), 100);
244 assert!(message.verify_integrity());
245 }
246
247 #[test]
248 fn test_system_check_test_message_integrity() {
249 let mut message = SystemCheckTestMessage::new("test_id".to_string(), 50);
250
251 assert!(message.verify_integrity());
253
254 if !message.data.is_empty() {
256 message.data[0] = message.data[0].wrapping_add(1);
257 }
258
259 assert!(!message.verify_integrity());
261 }
262
263 #[test]
264 fn test_system_check_test_message_serialization() {
265 let message = SystemCheckTestMessage::new("test_id".to_string(), 100);
266
267 let serialized = postcard::to_stdvec(&message).unwrap();
269 let deserialized: SystemCheckTestMessage = postcard::from_bytes(&serialized).unwrap();
270
271 assert_eq!(message, deserialized);
272 assert!(deserialized.verify_integrity());
273 }
274
275 #[tokio::test]
276 async fn test_storage_tests_structure() {
277 let (client, _temp_dir) = create_test_client().await;
278 let config = SystemCheckConfig::default();
279
280 let results = run_tests(&client, &config).await;
281
282 assert_eq!(results.len(), 2);
284
285 let test_names: Vec<_> = results.iter().map(|t| &t.name).collect();
286 assert!(test_names.contains(&&"Message Storage".to_string()));
287 assert!(test_names.contains(&&"Message Integrity".to_string()));
288 }
289}