zoe_client/system_check/
synchronization.rs

1//! Synchronization Tests
2//!
3//! This module contains tests that verify synchronization between
4//! local storage and relay servers after establishing connections.
5//! These tests ensure that offline-created data properly syncs
6//! with the server infrastructure.
7
8use super::{SystemCheckConfig, TestInfo, TestResult};
9use crate::Client;
10use crate::services::MessagesManagerTrait;
11use std::time::{Duration, SystemTime, UNIX_EPOCH};
12use tokio::time::sleep;
13use tracing::{debug, info, warn};
14use zoe_wire_protocol::{
15    Content, KeyPair, Kind, Message, MessageFull, MessageV0, MessageV0Header, Tag,
16};
17
18/// Test message for sync verification
19#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20pub struct SyncTestMessage {
21    pub sync_id: String,
22    pub message_type: String,
23    pub created_offline: bool,
24    pub data: Vec<u8>,
25    pub checksum: u32,
26    pub timestamp: u64,
27}
28
29impl SyncTestMessage {
30    pub fn new_offline(sync_id: String, data_size: usize) -> Self {
31        use rand::{RngCore, SeedableRng};
32        let mut rng = rand::rngs::StdRng::from_entropy();
33        let data: Vec<u8> = (0..data_size).map(|_| rng.next_u32() as u8).collect();
34        let checksum = crc32fast::hash(&data);
35        let timestamp = SystemTime::now()
36            .duration_since(UNIX_EPOCH)
37            .unwrap()
38            .as_secs();
39
40        Self {
41            sync_id,
42            message_type: "sync_test".to_string(),
43            created_offline: true,
44            data,
45            checksum,
46            timestamp,
47        }
48    }
49
50    pub fn verify_integrity(&self) -> bool {
51        crc32fast::hash(&self.data) == self.checksum
52    }
53}
54
55/// Run all synchronization tests
56pub async fn run_tests(client: &Client, config: &SystemCheckConfig) -> Vec<TestInfo> {
57    let mut tests = Vec::new();
58
59    info!("🔄 Running synchronization verification tests");
60
61    // Test message synchronization
62    tests.push(test_message_sync_verification(client, config).await);
63
64    // Test connection stability during sync
65    tests.push(test_sync_connection_stability(client, config).await);
66
67    tests
68}
69
70/// Test that offline messages can be verified after connection
71async fn test_message_sync_verification(client: &Client, _config: &SystemCheckConfig) -> TestInfo {
72    let mut test = TestInfo::new("Message Sync Verification");
73
74    debug!("Testing message synchronization after connection establishment...");
75
76    // First, verify we have an active connection
77    if !client.has_connected_relays().await {
78        let error = "No relay connections available for sync verification".to_string();
79        return test.with_result(TestResult::Failed { error });
80    }
81
82    test.add_detail("✓ Relay connection confirmed for sync testing");
83
84    // Create a new message that should sync with the server
85    let sync_message = SyncTestMessage::new_offline(
86        "sync_verification_test".to_string(),
87        512, // Medium size for sync testing
88    );
89
90    let serialized_content = match postcard::to_stdvec(&sync_message) {
91        Ok(data) => data,
92        Err(e) => {
93            let error = format!("Failed to serialize sync test message: {e}");
94            return test.with_result(TestResult::Failed { error });
95        }
96    };
97
98    use rand::SeedableRng;
99    let mut rng = rand::rngs::StdRng::from_entropy();
100    let temp_keypair = KeyPair::generate_ed25519(&mut rng);
101
102    let message = Message::MessageV0(MessageV0 {
103        header: MessageV0Header {
104            sender: temp_keypair.public_key(),
105            when: SystemTime::now()
106                .duration_since(UNIX_EPOCH)
107                .unwrap()
108                .as_secs(),
109            kind: Kind::Ephemeral(3600), // Use ephemeral for sync testing
110            tags: vec![Tag::Protected],
111        },
112        content: Content::Raw(serialized_content),
113    });
114
115    let message_full = match MessageFull::new(message, &temp_keypair) {
116        Ok(msg) => msg,
117        Err(e) => {
118            let error = format!("Failed to create sync test MessageFull: {e}");
119            return test.with_result(TestResult::Failed { error });
120        }
121    };
122
123    // Publish the message (should sync with relay)
124    let message_manager = client.message_manager();
125    match message_manager.publish(message_full).await {
126        Ok(_) => {
127            test.add_detail("✓ Published message for sync verification");
128        }
129        Err(e) => {
130            let error = format!("Failed to publish sync test message: {e}");
131            return test.with_result(TestResult::Failed { error });
132        }
133    }
134
135    // Wait a moment for potential synchronization
136    sleep(Duration::from_millis(500)).await;
137
138    // Verify message integrity
139    if sync_message.verify_integrity() {
140        test.add_detail("✓ Message data integrity maintained during sync");
141    } else {
142        let error = "Message integrity check failed after sync".to_string();
143        return test.with_result(TestResult::Failed { error });
144    }
145
146    // Verify storage is accessible
147    let _storage = client.storage();
148    test.add_detail("✓ Local storage accessible after sync operation");
149
150    info!("Message sync verification completed successfully");
151    test.with_result(TestResult::Passed)
152}
153
154/// Test connection stability during synchronization operations
155async fn test_sync_connection_stability(client: &Client, _config: &SystemCheckConfig) -> TestInfo {
156    let mut test = TestInfo::new("Sync Connection Stability");
157
158    debug!("Testing connection stability during sync operations...");
159
160    // Verify initial connection state
161    if !client.has_connected_relays().await {
162        let error = "No relay connections available for stability testing".to_string();
163        return test.with_result(TestResult::Failed { error });
164    }
165
166    let initial_status = match client.get_relay_status().await {
167        Ok(status) => status,
168        Err(e) => {
169            let error = format!("Failed to get initial relay status: {e}");
170            return test.with_result(TestResult::Failed { error });
171        }
172    };
173
174    let connected_count = initial_status
175        .iter()
176        .filter(|s| matches!(s.status, crate::RelayConnectionStatus::Connected { .. }))
177        .count();
178
179    test.add_detail(format!(
180        "✓ Initial connection state: {connected_count} relays connected"
181    ));
182
183    // Perform several sync-like operations to test stability
184    for i in 0..3 {
185        // Create a small test message
186        let test_message = SyncTestMessage::new_offline(format!("stability_test_{i}"), 128);
187
188        let serialized_content = match postcard::to_stdvec(&test_message) {
189            Ok(data) => data,
190            Err(e) => {
191                warn!("Failed to serialize stability test message {}: {}", i, e);
192                continue;
193            }
194        };
195
196        use rand::SeedableRng;
197        let mut rng = rand::rngs::StdRng::from_entropy();
198        let temp_keypair = KeyPair::generate_ed25519(&mut rng);
199
200        let message = Message::MessageV0(MessageV0 {
201            header: MessageV0Header {
202                sender: temp_keypair.public_key(),
203                when: SystemTime::now()
204                    .duration_since(UNIX_EPOCH)
205                    .unwrap()
206                    .as_secs(),
207                kind: Kind::Ephemeral(300), // Short-lived for stability testing
208                tags: vec![Tag::Protected],
209            },
210            content: Content::Raw(serialized_content),
211        });
212
213        let message_full = match MessageFull::new(message, &temp_keypair) {
214            Ok(msg) => msg,
215            Err(e) => {
216                warn!("Failed to create stability test MessageFull {}: {}", i, e);
217                continue;
218            }
219        };
220
221        // Publish and check connection stability
222        let message_manager = client.message_manager();
223        match message_manager.publish(message_full).await {
224            Ok(_) => {
225                test.add_detail(format!("✓ Stability test {i} completed"));
226            }
227            Err(e) => {
228                warn!("Stability test {} failed: {}", i, e);
229                // Don't fail the entire test for individual message failures
230            }
231        }
232
233        // Brief pause between operations
234        sleep(Duration::from_millis(100)).await;
235    }
236
237    // Verify connection is still stable after operations
238    if client.has_connected_relays().await {
239        test.add_detail("✓ Connection remained stable during sync operations");
240
241        // Get final status
242        match client.get_relay_status().await {
243            Ok(final_status) => {
244                let final_connected = final_status
245                    .iter()
246                    .filter(|s| matches!(s.status, crate::RelayConnectionStatus::Connected { .. }))
247                    .count();
248
249                test.add_detail(format!(
250                    "✓ Final connection state: {final_connected} relays connected"
251                ));
252
253                if final_connected >= connected_count {
254                    test.add_detail("✓ Connection stability maintained");
255                } else {
256                    test.add_detail("âš  Some connections were lost during testing");
257                }
258            }
259            Err(e) => {
260                warn!("Failed to get final relay status: {}", e);
261            }
262        }
263    } else {
264        let error = "Connection lost during sync stability testing".to_string();
265        return test.with_result(TestResult::Failed { error });
266    }
267
268    info!("Sync connection stability test completed successfully");
269    test.with_result(TestResult::Passed)
270}