Skip to content

Commit 31ac6bd

Browse files
authored
test: add comprehensive unit tests across Platform v2 modules (#3)
Add extensive test coverage across multiple crates: - p2p-consensus: network.rs with 419 lines of tests - challenge-registry: version, lifecycle, health, and error modules - rpc-server: RpcConfig and RpcServer tests - distributed-storage: StorageError type tests - core: Challenge and ChallengeConfig tests All 609+ tests pass with cargo test --workspace
1 parent b50ce52 commit 31ac6bd

File tree

8 files changed

+1753
-0
lines changed

8 files changed

+1753
-0
lines changed

crates/challenge-registry/src/error.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,188 @@ impl From<bincode::Error> for RegistryError {
5959
RegistryError::Serialization(err.to_string())
6060
}
6161
}
62+
63+
#[cfg(test)]
64+
mod tests {
65+
use super::*;
66+
use std::io::{Error as IoError, ErrorKind};
67+
68+
#[test]
69+
fn test_registry_error_display_challenge_not_found() {
70+
let err = RegistryError::ChallengeNotFound("test-challenge".to_string());
71+
assert_eq!(err.to_string(), "Challenge not found: test-challenge");
72+
}
73+
74+
#[test]
75+
fn test_registry_error_display_already_registered() {
76+
let err = RegistryError::AlreadyRegistered("my-challenge".to_string());
77+
assert_eq!(err.to_string(), "Challenge already registered: my-challenge");
78+
}
79+
80+
#[test]
81+
fn test_registry_error_display_version_conflict() {
82+
let err = RegistryError::VersionConflict("v1.0.0 vs v2.0.0".to_string());
83+
assert_eq!(err.to_string(), "Version conflict: v1.0.0 vs v2.0.0");
84+
}
85+
86+
#[test]
87+
fn test_registry_error_display_all_variants() {
88+
let test_cases = vec![
89+
(
90+
RegistryError::ChallengeNotFound("challenge-id".to_string()),
91+
"Challenge not found: challenge-id",
92+
),
93+
(
94+
RegistryError::AlreadyRegistered("existing".to_string()),
95+
"Challenge already registered: existing",
96+
),
97+
(
98+
RegistryError::VersionConflict("mismatch".to_string()),
99+
"Version conflict: mismatch",
100+
),
101+
(
102+
RegistryError::MigrationFailed("migration error".to_string()),
103+
"Migration failed: migration error",
104+
),
105+
(
106+
RegistryError::HealthCheckFailed("health issue".to_string()),
107+
"Health check failed: health issue",
108+
),
109+
(
110+
RegistryError::StatePersistence("persist error".to_string()),
111+
"State persistence error: persist error",
112+
),
113+
(
114+
RegistryError::StateRestoration("restore error".to_string()),
115+
"State restoration error: restore error",
116+
),
117+
(
118+
RegistryError::InvalidConfig("bad config".to_string()),
119+
"Invalid challenge configuration: bad config",
120+
),
121+
(
122+
RegistryError::Serialization("serde error".to_string()),
123+
"Serialization error: serde error",
124+
),
125+
(
126+
RegistryError::Network("connection refused".to_string()),
127+
"Network error: connection refused",
128+
),
129+
(
130+
RegistryError::Internal("unexpected".to_string()),
131+
"Internal error: unexpected",
132+
),
133+
];
134+
135+
for (error, expected_message) in test_cases {
136+
assert_eq!(
137+
error.to_string(),
138+
expected_message,
139+
"Display mismatch for {:?}",
140+
error
141+
);
142+
}
143+
}
144+
145+
#[test]
146+
fn test_from_io_error() {
147+
let io_err = IoError::new(ErrorKind::NotFound, "file not found");
148+
let registry_err: RegistryError = io_err.into();
149+
150+
match registry_err {
151+
RegistryError::Internal(msg) => {
152+
assert!(
153+
msg.contains("file not found"),
154+
"Expected message to contain 'file not found', got: {}",
155+
msg
156+
);
157+
}
158+
other => panic!("Expected Internal variant, got: {:?}", other),
159+
}
160+
}
161+
162+
#[test]
163+
fn test_from_serde_json_error() {
164+
// Create an invalid JSON to trigger a parse error
165+
let invalid_json = "{ invalid json }";
166+
let serde_err = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
167+
let registry_err: RegistryError = serde_err.into();
168+
169+
match registry_err {
170+
RegistryError::Serialization(msg) => {
171+
assert!(
172+
!msg.is_empty(),
173+
"Serialization error message should not be empty"
174+
);
175+
}
176+
other => panic!("Expected Serialization variant, got: {:?}", other),
177+
}
178+
}
179+
180+
#[test]
181+
fn test_from_bincode_error() {
182+
// Create invalid bincode data to trigger an error
183+
let invalid_data: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
184+
let bincode_err: bincode::Error =
185+
bincode::deserialize::<String>(invalid_data).unwrap_err();
186+
let registry_err: RegistryError = bincode_err.into();
187+
188+
match registry_err {
189+
RegistryError::Serialization(msg) => {
190+
assert!(
191+
!msg.is_empty(),
192+
"Serialization error message should not be empty"
193+
);
194+
}
195+
other => panic!("Expected Serialization variant, got: {:?}", other),
196+
}
197+
}
198+
199+
#[test]
200+
fn test_registry_result_type() {
201+
// Test that RegistryResult<T> works as expected with Ok
202+
fn returns_ok() -> RegistryResult<i32> {
203+
Ok(42)
204+
}
205+
assert_eq!(returns_ok().unwrap(), 42);
206+
207+
// Test that RegistryResult<T> works as expected with Err
208+
fn returns_err() -> RegistryResult<i32> {
209+
Err(RegistryError::Internal("test error".to_string()))
210+
}
211+
assert!(returns_err().is_err());
212+
213+
// Test with different types
214+
fn returns_string() -> RegistryResult<String> {
215+
Ok("success".to_string())
216+
}
217+
assert_eq!(returns_string().unwrap(), "success");
218+
}
219+
220+
#[test]
221+
fn test_error_debug_impl() {
222+
let err = RegistryError::ChallengeNotFound("debug-test".to_string());
223+
let debug_str = format!("{:?}", err);
224+
225+
// Debug format should contain the variant name and the inner value
226+
assert!(
227+
debug_str.contains("ChallengeNotFound"),
228+
"Debug should contain variant name, got: {}",
229+
debug_str
230+
);
231+
assert!(
232+
debug_str.contains("debug-test"),
233+
"Debug should contain inner value, got: {}",
234+
debug_str
235+
);
236+
237+
// Test debug for another variant
238+
let err2 = RegistryError::Network("connection timeout".to_string());
239+
let debug_str2 = format!("{:?}", err2);
240+
assert!(
241+
debug_str2.contains("Network"),
242+
"Debug should contain variant name, got: {}",
243+
debug_str2
244+
);
245+
}
246+
}

crates/challenge-registry/src/health.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,182 @@ mod tests {
259259
// 100 * 0.8 + 200 * 0.2 = 80 + 40 = 120
260260
assert!((health.avg_response_time_ms - 120.0).abs() < 0.01);
261261
}
262+
263+
#[test]
264+
fn test_health_status_default() {
265+
let status = HealthStatus::default();
266+
assert_eq!(status, HealthStatus::Unknown);
267+
}
268+
269+
#[test]
270+
fn test_challenge_health_new() {
271+
let challenge_id = ChallengeId::new();
272+
let health = ChallengeHealth::new(challenge_id);
273+
274+
assert_eq!(health.challenge_id, challenge_id);
275+
assert_eq!(health.status, HealthStatus::Unknown);
276+
assert_eq!(health.last_check_at, 0);
277+
assert_eq!(health.consecutive_failures, 0);
278+
assert_eq!(health.avg_response_time_ms, 0.0);
279+
assert!(health.metrics.is_empty());
280+
}
281+
282+
#[test]
283+
fn test_challenge_health_metrics() {
284+
let mut health = ChallengeHealth::new(ChallengeId::new());
285+
286+
health.metrics.insert("cpu_usage".to_string(), 45.5);
287+
health.metrics.insert("memory_mb".to_string(), 512.0);
288+
health.metrics.insert("requests_per_sec".to_string(), 1000.0);
289+
290+
assert_eq!(health.metrics.len(), 3);
291+
assert_eq!(health.metrics.get("cpu_usage"), Some(&45.5));
292+
assert_eq!(health.metrics.get("memory_mb"), Some(&512.0));
293+
assert_eq!(health.metrics.get("requests_per_sec"), Some(&1000.0));
294+
assert_eq!(health.metrics.get("nonexistent"), None);
295+
}
296+
297+
#[test]
298+
fn test_health_config_default() {
299+
let config = HealthConfig::default();
300+
301+
assert_eq!(config.check_interval, Duration::from_secs(30));
302+
assert_eq!(config.check_timeout, Duration::from_secs(5));
303+
assert_eq!(config.failure_threshold, 3);
304+
assert_eq!(config.recovery_threshold, 2);
305+
}
306+
307+
#[test]
308+
fn test_health_config_custom() {
309+
let config = HealthConfig {
310+
check_interval: Duration::from_secs(60),
311+
check_timeout: Duration::from_secs(10),
312+
failure_threshold: 5,
313+
recovery_threshold: 3,
314+
};
315+
316+
assert_eq!(config.check_interval, Duration::from_secs(60));
317+
assert_eq!(config.check_timeout, Duration::from_secs(10));
318+
assert_eq!(config.failure_threshold, 5);
319+
assert_eq!(config.recovery_threshold, 3);
320+
}
321+
322+
#[test]
323+
fn test_health_monitor_with_config() {
324+
let config = HealthConfig {
325+
check_interval: Duration::from_secs(120),
326+
check_timeout: Duration::from_secs(15),
327+
failure_threshold: 10,
328+
recovery_threshold: 5,
329+
};
330+
331+
let monitor = HealthMonitor::with_config(config);
332+
let monitor_config = monitor.config();
333+
334+
assert_eq!(monitor_config.check_interval, Duration::from_secs(120));
335+
assert_eq!(monitor_config.check_timeout, Duration::from_secs(15));
336+
assert_eq!(monitor_config.failure_threshold, 10);
337+
assert_eq!(monitor_config.recovery_threshold, 5);
338+
}
339+
340+
#[test]
341+
fn test_health_monitor_get_all_health() {
342+
let monitor = HealthMonitor::new();
343+
let id1 = ChallengeId::new();
344+
let id2 = ChallengeId::new();
345+
let id3 = ChallengeId::new();
346+
347+
assert!(monitor.get_all_health().is_empty());
348+
349+
monitor.register(id1);
350+
monitor.register(id2);
351+
monitor.register(id3);
352+
353+
let all_health = monitor.get_all_health();
354+
assert_eq!(all_health.len(), 3);
355+
356+
let ids: Vec<ChallengeId> = all_health.iter().map(|h| h.challenge_id).collect();
357+
assert!(ids.contains(&id1));
358+
assert!(ids.contains(&id2));
359+
assert!(ids.contains(&id3));
360+
}
361+
362+
#[test]
363+
fn test_health_monitor_update_health() {
364+
let monitor = HealthMonitor::new();
365+
let id = ChallengeId::new();
366+
367+
monitor.register(id);
368+
let health = monitor.get_health(&id).expect("should be registered");
369+
assert_eq!(health.status, HealthStatus::Unknown);
370+
371+
monitor.update_health(&id, HealthStatus::Healthy);
372+
let health = monitor.get_health(&id).expect("should be registered");
373+
assert_eq!(health.status, HealthStatus::Healthy);
374+
assert!(health.last_check_at > 0);
375+
376+
monitor.update_health(&id, HealthStatus::Degraded("high latency".to_string()));
377+
let health = monitor.get_health(&id).expect("should be registered");
378+
assert_eq!(
379+
health.status,
380+
HealthStatus::Degraded("high latency".to_string())
381+
);
382+
383+
monitor.update_health(&id, HealthStatus::Unhealthy("connection lost".to_string()));
384+
let health = monitor.get_health(&id).expect("should be registered");
385+
assert_eq!(
386+
health.status,
387+
HealthStatus::Unhealthy("connection lost".to_string())
388+
);
389+
}
390+
391+
#[test]
392+
fn test_health_status_variants() {
393+
let unknown = HealthStatus::Unknown;
394+
let healthy = HealthStatus::Healthy;
395+
let degraded = HealthStatus::Degraded("slow response".to_string());
396+
let unhealthy = HealthStatus::Unhealthy("service down".to_string());
397+
398+
assert_eq!(unknown, HealthStatus::Unknown);
399+
assert_eq!(healthy, HealthStatus::Healthy);
400+
assert_eq!(
401+
degraded,
402+
HealthStatus::Degraded("slow response".to_string())
403+
);
404+
assert_eq!(
405+
unhealthy,
406+
HealthStatus::Unhealthy("service down".to_string())
407+
);
408+
409+
assert_ne!(unknown, healthy);
410+
assert_ne!(healthy, degraded);
411+
assert_ne!(degraded, unhealthy);
412+
413+
let degraded_clone = degraded.clone();
414+
assert_eq!(degraded, degraded_clone);
415+
}
416+
417+
#[test]
418+
fn test_challenge_health_consecutive_successes() {
419+
let mut health = ChallengeHealth::new(ChallengeId::new());
420+
421+
health.record_failure("error 1".to_string());
422+
health.record_failure("error 2".to_string());
423+
assert_eq!(health.consecutive_failures, 2);
424+
assert!(matches!(health.status, HealthStatus::Degraded(_)));
425+
426+
health.record_success(50.0);
427+
assert_eq!(health.consecutive_failures, 0);
428+
assert_eq!(health.status, HealthStatus::Healthy);
429+
430+
health.record_failure("error 3".to_string());
431+
health.record_failure("error 4".to_string());
432+
health.record_failure("error 5".to_string());
433+
assert_eq!(health.consecutive_failures, 3);
434+
assert!(matches!(health.status, HealthStatus::Unhealthy(_)));
435+
436+
health.record_success(75.0);
437+
assert_eq!(health.consecutive_failures, 0);
438+
assert_eq!(health.status, HealthStatus::Healthy);
439+
}
262440
}

0 commit comments

Comments
 (0)