Skip to main content

max / makenotwork

3.1 KB · 96 lines History Blame Raw
1 //! In-memory storage backend for integration tests.
2
3 use makenotwork::error::{AppError, Result};
4 use makenotwork::storage::StorageBackend;
5 use std::collections::HashMap;
6 use std::sync::Mutex;
7
8 /// In-memory implementation of `StorageBackend` for tests.
9 /// Files are stored in a `HashMap<String, Vec<u8>>` behind a `Mutex`.
10 pub struct InMemoryStorage {
11 objects: Mutex<HashMap<String, Vec<u8>>>,
12 bucket: String,
13 }
14
15 #[allow(dead_code)]
16 impl InMemoryStorage {
17 pub fn new() -> Self {
18 InMemoryStorage {
19 objects: Mutex::new(HashMap::new()),
20 bucket: "test-bucket".to_string(),
21 }
22 }
23
24 /// Pre-populate a file so that subsequent `object_exists` / `download_object`
25 /// calls see it. Useful for testing confirm_upload flows.
26 pub fn put(&self, key: &str, data: Vec<u8>) {
27 self.objects.lock().unwrap().insert(key.to_string(), data);
28 }
29
30 /// Retrieve stored bytes for a key. Panics if not found.
31 pub fn get(&self, key: &str) -> Vec<u8> {
32 self.objects.lock().unwrap().get(key).cloned().expect("key not found in storage")
33 }
34 }
35
36 #[async_trait::async_trait]
37 impl StorageBackend for InMemoryStorage {
38 async fn presign_upload(&self, s3_key: &str, _content_type: &str, _expiry_secs: Option<u64>, _cache_control: Option<&str>, _max_bytes: Option<i64>) -> Result<String> {
39 Ok(format!("http://test-storage/{}", s3_key))
40 }
41
42 async fn presign_download(&self, s3_key: &str, _expiry_secs: Option<u64>) -> Result<String> {
43 if self.objects.lock().unwrap().contains_key(s3_key) {
44 Ok(format!("http://test-storage/{}", s3_key))
45 } else {
46 Err(AppError::Storage(format!("Object not found: {}", s3_key)))
47 }
48 }
49
50 async fn object_exists(&self, s3_key: &str) -> Result<bool> {
51 Ok(self.objects.lock().unwrap().contains_key(s3_key))
52 }
53
54 async fn object_size(&self, s3_key: &str) -> Result<Option<i64>> {
55 Ok(self.objects.lock().unwrap().get(s3_key).map(|v| v.len() as i64))
56 }
57
58 async fn download_object(&self, s3_key: &str) -> Result<Vec<u8>> {
59 self.objects
60 .lock()
61 .unwrap()
62 .get(s3_key)
63 .cloned()
64 .ok_or_else(|| AppError::Storage(format!("Object not found: {}", s3_key)))
65 }
66
67 async fn download_stream(&self, s3_key: &str) -> Result<s3_storage::ByteStream> {
68 let bytes = self
69 .objects
70 .lock()
71 .unwrap()
72 .get(s3_key)
73 .cloned()
74 .ok_or_else(|| AppError::Storage(format!("Object not found: {}", s3_key)))?;
75 Ok(s3_storage::ByteStream::from(bytes))
76 }
77
78 async fn upload_object(&self, s3_key: &str, _content_type: &str, data: Vec<u8>, _cache_control: Option<&str>) -> Result<()> {
79 self.objects.lock().unwrap().insert(s3_key.to_string(), data);
80 Ok(())
81 }
82
83 async fn delete_object(&self, s3_key: &str) -> Result<()> {
84 self.objects.lock().unwrap().remove(s3_key);
85 Ok(())
86 }
87
88 async fn check_connectivity(&self) -> std::result::Result<(), String> {
89 Ok(())
90 }
91
92 fn bucket(&self) -> &str {
93 &self.bucket
94 }
95 }
96