zoe_client/system_check/
connectivity.rs1use super::{SystemCheckConfig, TestInfo, TestResult};
8use crate::Client;
9use tracing::{debug, info};
10
11pub async fn run_tests(client: &Client, _config: &SystemCheckConfig) -> Vec<TestInfo> {
13 let mut tests = Vec::new();
14
15 tests.push(test_basic_connectivity(client).await);
17
18 tests.push(test_relay_connection_status(client).await);
20
21 tests
22}
23
24async fn test_basic_connectivity(client: &Client) -> TestInfo {
26 let mut test = TestInfo::new("Basic Connectivity");
27
28 debug!("Testing basic connectivity...");
29
30 match client.has_connected_relays().await {
32 true => {
33 if let Ok(relay_statuses) = client.get_relay_status().await {
35 for status in &relay_statuses {
36 if let crate::RelayConnectionStatus::Connected { connected_address } =
37 &status.status
38 {
39 test.add_detail(format!(
40 "✓ QUIC connection established to {connected_address}"
41 ));
42
43 let client_key = client.public_key();
45 test.add_detail(format!(
46 "✓ Client identity: {} ({})",
47 hex::encode(client_key.id()),
48 client_key.algorithm()
49 ));
50
51 let server_key = &status.info.relay_address.public_key;
53 test.add_detail(format!(
54 "✓ Server identity: {} ({})",
55 hex::encode(server_key.id()),
56 server_key.algorithm()
57 ));
58
59 let relay_connections = client.relay_connections.read().await;
61 if let Some(_relay_client) = relay_connections.get(&status.info.relay_id) {
62 test.add_detail("✓ Protocol version negotiated successfully");
65 test.add_detail("✓ ML-DSA handshake completed");
66 }
67
68 let _storage = client.storage();
70 test.add_detail("✓ Client storage initialized");
72
73 let _message_manager = client.message_manager();
75 test.add_detail("✓ Message manager ready");
76
77 let _blob_service = client.blob_service();
79 test.add_detail("✓ Blob service ready");
80
81 break; }
83 }
84 }
85
86 info!("Basic connectivity test passed");
87 test.with_result(TestResult::Passed)
88 }
89 false => {
90 let error = "No relay connections established".to_string();
91 test.with_result(TestResult::Failed { error })
92 }
93 }
94}
95
96async fn test_relay_connection_status(client: &Client) -> TestInfo {
98 let mut test = TestInfo::new("Relay Connection Status");
99
100 debug!("Testing relay connection status...");
101
102 match client.get_relay_status().await {
103 Ok(relay_statuses) => {
104 if relay_statuses.is_empty() {
105 let error = "No relay configurations found".to_string();
106 test.with_result(TestResult::Failed { error })
107 } else {
108 let connected_count = relay_statuses
109 .iter()
110 .filter(|status| {
111 matches!(
112 status.status,
113 crate::RelayConnectionStatus::Connected { .. }
114 )
115 })
116 .count();
117 let failed_count = relay_statuses
118 .iter()
119 .filter(|status| {
120 matches!(status.status, crate::RelayConnectionStatus::Failed { .. })
121 })
122 .count();
123 let connecting_count = relay_statuses
124 .iter()
125 .filter(|status| {
126 matches!(status.status, crate::RelayConnectionStatus::Connecting)
127 })
128 .count();
129 let disconnected_count = relay_statuses
130 .iter()
131 .filter(|status| {
132 matches!(
133 status.status,
134 crate::RelayConnectionStatus::Disconnected { .. }
135 )
136 })
137 .count();
138
139 test.add_detail(format!(
140 "✓ Total relays configured: {}",
141 relay_statuses.len()
142 ));
143 test.add_detail(format!("✓ Connected relays: {connected_count}"));
144
145 if failed_count > 0 {
146 test.add_detail(format!("⚠ Failed relays: {failed_count}"));
147 }
148 if connecting_count > 0 {
149 test.add_detail(format!("⏳ Connecting relays: {connecting_count}"));
150 }
151 if disconnected_count > 0 {
152 test.add_detail(format!("⏸ Disconnected relays: {disconnected_count}"));
153 }
154
155 if connected_count > 0 {
156 for status in &relay_statuses {
157 match &status.status {
158 crate::RelayConnectionStatus::Connected { connected_address } => {
159 test.add_detail(format!(
160 "✓ Connected to: {} ({})",
161 status.info.relay_address.display_name(),
162 connected_address
163 ));
164
165 let addresses = status.info.relay_address.all_addresses();
167 if addresses.len() > 1 {
168 test.add_detail(format!(
169 " Available addresses: {}",
170 addresses
171 .iter()
172 .map(|addr| addr.to_string())
173 .collect::<Vec<_>>()
174 .join(", ")
175 ));
176 }
177 }
178 crate::RelayConnectionStatus::Failed { error } => {
179 test.add_detail(format!(
180 "✗ Failed: {} - {}",
181 status.info.relay_address.display_name(),
182 error
183 ));
184 }
185 crate::RelayConnectionStatus::Connecting => {
186 test.add_detail(format!(
187 "⏳ Connecting: {}",
188 status.info.relay_address.display_name()
189 ));
190 }
191 crate::RelayConnectionStatus::Disconnected { error } => {
192 let error_msg = error
193 .as_ref()
194 .map(|e| format!(" - {e}"))
195 .unwrap_or_default();
196 test.add_detail(format!(
197 "⏸ Disconnected: {}{}",
198 status.info.relay_address.display_name(),
199 error_msg
200 ));
201 }
202 }
203 }
204 test.with_result(TestResult::Passed)
205 } else {
206 let error = "No relays are currently connected".to_string();
207 test.with_result(TestResult::Failed { error })
208 }
209 }
210 }
211 Err(e) => {
212 let error = format!("Failed to get relay status: {e}");
213 test.with_result(TestResult::Failed { error })
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::Client;
222 use tempfile::TempDir;
223
224 async fn create_test_client() -> (Client, TempDir) {
225 let temp_dir = TempDir::new().unwrap();
226 let media_storage_path = temp_dir.path().join("blobs");
227 let db_storage_path = temp_dir.path().join("db");
228
229 let client = {
230 let mut builder = Client::builder();
231 builder.media_storage_dir_pathbuf(media_storage_path);
232 builder.db_storage_dir_pathbuf(db_storage_path);
233 builder.autoconnect(false);
234 builder.build().await.unwrap()
235 };
236
237 (client, temp_dir)
238 }
239
240 #[tokio::test]
241 async fn test_connectivity_with_no_relays() {
242 let (client, _temp_dir) = create_test_client().await;
243 let config = SystemCheckConfig::default();
244
245 let results = run_tests(&client, &config).await;
246
247 assert_eq!(results.len(), 2);
249
250 assert!(results.iter().all(|test| test.result.is_failed()));
252 }
253
254 #[tokio::test]
255 async fn test_connectivity_test_names() {
256 let (client, _temp_dir) = create_test_client().await;
257 let config = SystemCheckConfig::default();
258
259 let results = run_tests(&client, &config).await;
260
261 let test_names: Vec<_> = results.iter().map(|t| &t.name).collect();
262 assert!(test_names.contains(&&"Basic Connectivity".to_string()));
263 assert!(test_names.contains(&&"Relay Connection Status".to_string()));
264 }
265}