Skip to main content

sui_dockerfile_wrapper/
cache.rs

1//! The cache seam this crate consumes.
2//!
3//! This crate does **not** invent a new cache abstraction — it consumes
4//! [`sui_cache::storage::StorageBackend`] exactly as `sui cache serve`
5//! does. A node is "cached" when its `content_hash` has a narinfo entry;
6//! the narinfo *content* is the image reference the node's cache write
7//! produced, so a cache hit can pull the already-built image instead of
8//! rebuilding it. Real callers construct the backend via
9//! [`sui_cache::storage::build_backend`] against a
10//! [`sui_cache::BackendConfig`] (Postgres L2 + Redis L1, per
11//! `sui-supercacheci`); tests use [`MockCacheBackend`], an in-memory
12//! implementation of the same trait — the identical shape the
13//! `sui-store` crate's `TestStore` uses to prove `Store`'s default
14//! methods without a real database.
15
16use std::collections::BTreeMap;
17use std::sync::Mutex;
18
19use async_trait::async_trait;
20use sui_cache::storage::StorageBackend;
21use sui_cache::CacheError;
22
23/// An in-memory [`StorageBackend`] for unit tests. Never touches a
24/// filesystem, Redis, or Postgres — narinfo entries live in a
25/// `Mutex<BTreeMap>` for the duration of the test.
26#[derive(Default)]
27pub struct MockCacheBackend {
28    narinfos: Mutex<BTreeMap<String, String>>,
29}
30
31impl MockCacheBackend {
32    #[must_use]
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Pre-populate a cache entry — the constructor tests use to set up a
38    /// full or partial cache-hit fixture.
39    ///
40    /// # Panics
41    ///
42    /// Panics only if the internal mutex is poisoned (a prior panic
43    /// while holding the lock) — never in normal test use.
44    #[must_use]
45    pub fn with_entry(self, hash: &str, image_ref: &str) -> Self {
46        self.narinfos
47            .lock()
48            .expect("mock mutex poisoned")
49            .insert(hash.to_string(), image_ref.to_string());
50        self
51    }
52}
53
54#[async_trait]
55impl StorageBackend for MockCacheBackend {
56    async fn get_narinfo(&self, hash: &str) -> Result<Option<String>, CacheError> {
57        Ok(self.narinfos.lock().expect("mock mutex poisoned").get(hash).cloned())
58    }
59
60    async fn put_narinfo(&self, hash: &str, content: &str) -> Result<(), CacheError> {
61        self.narinfos
62            .lock()
63            .expect("mock mutex poisoned")
64            .insert(hash.to_string(), content.to_string());
65        Ok(())
66    }
67
68    async fn get_nar(&self, _path: &str) -> Result<Option<Vec<u8>>, CacheError> {
69        Ok(None)
70    }
71
72    async fn put_nar(&self, _path: &str, _data: &[u8]) -> Result<(), CacheError> {
73        Ok(())
74    }
75
76    async fn delete(&self, hash: &str) -> Result<(), CacheError> {
77        self.narinfos.lock().expect("mock mutex poisoned").remove(hash);
78        Ok(())
79    }
80
81    async fn list_narinfos(&self) -> Result<Vec<String>, CacheError> {
82        Ok(self.narinfos.lock().expect("mock mutex poisoned").keys().cloned().collect())
83    }
84}