#![cfg(feature = "test-utils")] use std::sync::Arc; use dirigent_archivist::backend::mock::MockBackend; use dirigent_archivist::backend::{ArchiveBackend, HealthStatus}; use dirigent_archivist::coordinator::Archivist; use dirigent_archivist::registry::{ArchiveRegistration, FailureMode, WritePolicy}; use dirigent_archivist::types::SessionMetadata; use uuid::Uuid; fn reg(name: &str, backend: Arc, priority: u32) -> Arc { Arc::new(ArchiveRegistration::new( name.into(), "mock", backend as Arc, true, FailureMode::Required, priority, true, WritePolicy::Inline, None, HealthStatus::Healthy, )) } #[tokio::test] async fn high_priority_backend_serves_first() { let high = Arc::new(MockBackend::new()); let low = Arc::new(MockBackend::new()); let scroll = Uuid::new_v4(); high.put_session(SessionMetadata::stub(scroll)).await.unwrap(); let archivist = Archivist::from_registrations(vec![ reg("high", high.clone(), 0), reg("low", low.clone(), 10), ]); let meta = archivist.get_session_metadata(scroll, None).await; assert!(meta.is_ok(), "expected Ok; got {:?}", meta); assert_eq!(archivist.read_cache_size().await, 1); } #[tokio::test] async fn falls_through_to_lower_priority_when_high_misses() { let high = Arc::new(MockBackend::new()); let low = Arc::new(MockBackend::new()); let scroll = Uuid::new_v4(); low.put_session(SessionMetadata::stub(scroll)).await.unwrap(); let archivist = Archivist::from_registrations(vec![ reg("high", high.clone(), 0), reg("low", low.clone(), 10), ]); let meta = archivist.get_session_metadata(scroll, None).await; assert!(meta.is_ok(), "expected Ok; got {:?}", meta); assert_eq!(archivist.read_cache_size().await, 1); } #[tokio::test] async fn cache_makes_second_read_skip_priority_walk() { let high = Arc::new(MockBackend::new()); let low = Arc::new(MockBackend::new()); let scroll = Uuid::new_v4(); low.put_session(SessionMetadata::stub(scroll)).await.unwrap(); let archivist = Archivist::from_registrations(vec![ reg("high", high.clone(), 0), reg("low", low.clone(), 10), ]); // Prime the cache. let _ = archivist.get_session_metadata(scroll, None).await.unwrap(); // Inject a read failure on `high` to detect whether the second read walks it. // If the cache works, `high` must NOT be touched. high.inject_read_failures(1); let _ = archivist.get_session_metadata(scroll, None).await.unwrap(); let snapshot = archivist.list_archives_with_health().await; let high_status = snapshot.iter().find(|s| s.name == "high").unwrap(); assert!( matches!(high_status.health, HealthStatus::Healthy), "cache should have skipped `high`; got {:?}", high_status.health ); } #[tokio::test] async fn delete_invalidates_cache() { let high = Arc::new(MockBackend::new()); let scroll = Uuid::new_v4(); high.put_session(SessionMetadata::stub(scroll)).await.unwrap(); let archivist = Archivist::from_registrations(vec![reg("high", high.clone(), 0)]); let _ = archivist.get_session_metadata(scroll, None).await.unwrap(); assert_eq!(archivist.read_cache_size().await, 1); archivist.delete_session(scroll, None).await.unwrap(); assert_eq!(archivist.read_cache_size().await, 0); }