1pub use repolith_core::cache::{Cache, CacheError, Result};
8
9pub mod sqlite;
11pub use sqlite::SqliteCache;
12
13#[cfg(test)]
14mod tests {
15 use super::*;
16 use async_trait::async_trait;
17 use repolith_core::types::{ActionId, BuildEvent, Sha256};
18
19 struct MockCache {
20 events: std::collections::HashMap<ActionId, BuildEvent>,
21 }
22
23 impl MockCache {
24 fn new() -> Self {
25 Self {
26 events: std::collections::HashMap::new(),
27 }
28 }
29 }
30
31 #[async_trait]
32 impl Cache for MockCache {
33 async fn last_build(&self, id: &ActionId) -> Option<BuildEvent> {
34 self.events.get(id).cloned()
35 }
36
37 async fn record(&mut self, event: BuildEvent) -> Result<()> {
38 let id = match &event {
39 BuildEvent::Success { id, .. } | BuildEvent::Failed { id, .. } => id.clone(),
40 };
41 self.events.insert(id, event);
42 Ok(())
43 }
44 }
45
46 #[tokio::test]
47 async fn test_mock_cache() {
48 let mut cache = MockCache::new();
49 let id = ActionId("test-action".to_string());
50
51 assert!(cache.last_build(&id).await.is_none());
53
54 let event = BuildEvent::Success {
56 id: id.clone(),
57 input: Sha256([0; 32]),
58 output: Sha256([1; 32]),
59 ms: 100,
60 };
61
62 cache.record(event.clone()).await.unwrap();
63
64 let retrieved = cache.last_build(&id).await.unwrap();
66 match retrieved {
67 BuildEvent::Success { ms, .. } => assert_eq!(ms, 100),
68 BuildEvent::Failed { .. } => panic!("Expected Success event"),
69 }
70 }
71
72 #[test]
73 fn test_cache_error_display() {
74 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
75 let err1 = CacheError::Io(io_err);
76 assert_eq!(err1.to_string(), "io: file missing");
77
78 let err2 = CacheError::Backend("db locked".to_string());
79 assert_eq!(err2.to_string(), "backend: db locked");
80 }
81
82 #[test]
83 fn test_cache_is_object_safe() {
84 let _cache: Box<dyn Cache> = Box::new(MockCache::new());
86 }
87}