zoe_client/system_check/
offline_storage.rs

1//! Offline Storage Tests
2//!
3//! This module contains tests that verify local storage functionality
4//! without requiring a relay connection. These tests validate that
5//! the client can store and retrieve messages locally.
6
7use super::{SystemCheckConfig, TestInfo, TestResult};
8use crate::Client;
9use crate::services::MessagesManagerTrait;
10use std::collections::HashMap;
11use std::time::{SystemTime, UNIX_EPOCH};
12use tracing::{debug, info};
13use zoe_wire_protocol::{
14    Content, KeyPair, Kind, Message, MessageFull, MessageV0, MessageV0Header, Tag,
15};
16
17/// Test message structure for offline storage verification
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19pub struct OfflineTestMessage {
20    pub test_id: String,
21    pub message_type: String,
22    pub data: Vec<u8>,
23    pub checksum: u32,
24    pub created_at: u64,
25}
26
27impl OfflineTestMessage {
28    pub fn new(test_id: String, data_size: usize) -> Self {
29        use rand::{RngCore, SeedableRng};
30        let mut rng = rand::rngs::StdRng::from_entropy();
31        let data: Vec<u8> = (0..data_size).map(|_| rng.next_u32() as u8).collect();
32        let checksum = crc32fast::hash(&data);
33        let created_at = SystemTime::now()
34            .duration_since(UNIX_EPOCH)
35            .unwrap()
36            .as_secs();
37
38        Self {
39            test_id,
40            message_type: "offline_test".to_string(),
41            data,
42            checksum,
43            created_at,
44        }
45    }
46
47    pub fn verify_integrity(&self) -> bool {
48        crc32fast::hash(&self.data) == self.checksum
49    }
50}
51
52/// Run all offline storage tests
53pub async fn run_tests(client: &Client, config: &SystemCheckConfig) -> Vec<TestInfo> {
54    let mut tests = Vec::new();
55
56    info!("🔧 Running offline storage tests (no relay connection required)");
57
58    // Test local message storage
59    tests.push(test_offline_message_storage(client, config).await);
60
61    // Test message persistence across operations
62    tests.push(test_message_persistence(client, config).await);
63
64    tests
65}
66
67/// Test offline message storage and retrieval
68async fn test_offline_message_storage(client: &Client, config: &SystemCheckConfig) -> TestInfo {
69    let mut test = TestInfo::new("Offline Message Storage");
70
71    debug!(
72        "Testing offline message storage with {} messages...",
73        config.offline_message_count
74    );
75
76    let mut stored_messages = HashMap::new();
77
78    // Store test messages locally
79    for i in 0..config.offline_message_count {
80        let test_message = OfflineTestMessage::new(
81            format!("offline_storage_test_{i}"),
82            256, // Small data size for offline tests
83        );
84
85        // Create a proper MessageFull for local storage
86        let serialized_content = match postcard::to_stdvec(&test_message) {
87            Ok(data) => data,
88            Err(e) => {
89                let error = format!("Failed to serialize test message {i}: {e}");
90                return test.with_result(TestResult::Failed { error });
91            }
92        };
93
94        // Create a temporary keypair for the test message
95        use rand::SeedableRng;
96        let mut rng = rand::rngs::StdRng::from_entropy();
97        let temp_keypair = KeyPair::generate_ed25519(&mut rng);
98
99        let message = Message::MessageV0(MessageV0 {
100            header: MessageV0Header {
101                sender: temp_keypair.public_key(),
102                when: SystemTime::now()
103                    .duration_since(UNIX_EPOCH)
104                    .unwrap()
105                    .as_secs(),
106                kind: Kind::Ephemeral(3600), // 1 hour timeout
107                tags: vec![Tag::Protected],
108            },
109            content: Content::Raw(serialized_content),
110        });
111
112        let message_full = match MessageFull::new(message, &temp_keypair) {
113            Ok(msg) => msg,
114            Err(e) => {
115                let error = format!("Failed to create MessageFull for test message {i}: {e}");
116                return test.with_result(TestResult::Failed { error });
117            }
118        };
119
120        // Store the message locally (this should work without relay connection)
121        let message_manager = client.message_manager();
122        match message_manager.publish(message_full.clone()).await {
123            Ok(_) => {
124                stored_messages.insert(test_message.test_id.clone(), test_message);
125                test.add_detail(format!("✓ Stored offline message {i}"));
126            }
127            Err(e) => {
128                let error = format!("Failed to store offline message {i}: {e}");
129                return test.with_result(TestResult::Failed { error });
130            }
131        }
132    }
133
134    test.add_detail(format!(
135        "✓ Successfully stored {} messages offline",
136        stored_messages.len()
137    ));
138
139    // Verify we can access the storage directly
140    let _storage = client.storage();
141    test.add_detail("✓ Local storage accessible");
142
143    info!("Offline message storage test completed successfully");
144    test.with_result(TestResult::Passed)
145}
146
147/// Test message persistence across operations
148async fn test_message_persistence(client: &Client, _config: &SystemCheckConfig) -> TestInfo {
149    let mut test = TestInfo::new("Message Persistence");
150
151    debug!("Testing message persistence in local storage...");
152
153    // Create a test message with unique identifier
154    let test_message = OfflineTestMessage::new("persistence_test".to_string(), 128);
155
156    let serialized_content = match postcard::to_stdvec(&test_message) {
157        Ok(data) => data,
158        Err(e) => {
159            let error = format!("Failed to serialize persistence test message: {e}");
160            return test.with_result(TestResult::Failed { error });
161        }
162    };
163
164    use rand::SeedableRng;
165    let mut rng = rand::rngs::StdRng::from_entropy();
166    let temp_keypair = KeyPair::generate_ed25519(&mut rng);
167
168    let message = Message::MessageV0(MessageV0 {
169        header: MessageV0Header {
170            sender: temp_keypair.public_key(),
171            when: SystemTime::now()
172                .duration_since(UNIX_EPOCH)
173                .unwrap()
174                .as_secs(),
175            kind: Kind::Ephemeral(86400), // Use long-lived ephemeral for persistence simulation
176            tags: vec![Tag::Protected],
177        },
178        content: Content::Raw(serialized_content),
179    });
180
181    let message_full = match MessageFull::new(message, &temp_keypair) {
182        Ok(msg) => msg,
183        Err(e) => {
184            let error = format!("Failed to create persistent MessageFull: {e}");
185            return test.with_result(TestResult::Failed { error });
186        }
187    };
188
189    // Store the persistent message
190    let message_manager = client.message_manager();
191    match message_manager.publish(message_full).await {
192        Ok(_) => {
193            test.add_detail("✓ Stored persistent message");
194        }
195        Err(e) => {
196            let error = format!("Failed to store persistent message: {e}");
197            return test.with_result(TestResult::Failed { error });
198        }
199    }
200
201    // Verify storage is working
202    let _storage = client.storage();
203    test.add_detail("✓ Storage persistence verified");
204
205    // Verify the test message integrity
206    if test_message.verify_integrity() {
207        test.add_detail("✓ Message data integrity verified");
208    } else {
209        let error = "Message data integrity check failed".to_string();
210        return test.with_result(TestResult::Failed { error });
211    }
212
213    info!("Message persistence test completed successfully");
214    test.with_result(TestResult::Passed)
215}