backend/storage/
testing.rs1#![allow(dead_code)]
10
11use rand::Rng;
12use samod::storage::{Storage, StorageKey};
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::LazyLock;
16
17pub fn payload_a() -> Vec<u8> {
18 vec![0, 1, 127, 99, 154, 235]
19}
20
21pub fn payload_b() -> Vec<u8> {
22 vec![1, 76, 160, 53, 57, 10, 230]
23}
24
25pub fn payload_c() -> Vec<u8> {
26 vec![2, 111, 74, 131, 236, 96, 142, 193]
27}
28
29static LARGE_PAYLOAD: LazyLock<Vec<u8>> = LazyLock::new(|| {
30 let mut vec = vec![0u8; 100000];
31 rand::thread_rng().fill(&mut vec[..]);
32 vec
33});
34
35pub fn large_payload() -> Vec<u8> {
36 LARGE_PAYLOAD.clone()
37}
38
39pub trait StorageTestFixture: Sized + Send {
41 type Storage: Storage + Send + Sync + 'static;
43
44 fn setup() -> impl std::future::Future<Output = Self> + Send;
46
47 fn storage(&self) -> &Self::Storage;
49
50 fn teardown(self) -> impl std::future::Future<Output = ()> + Send {
52 async {}
53 }
54}
55
56async fn run_test<F, TestFn>(test_fn: TestFn)
58where
59 F: StorageTestFixture,
60 TestFn: for<'a> FnOnce(&'a F::Storage) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> + Send,
61{
62 let fixture = F::setup().await;
63 test_fn(fixture.storage()).await;
64 fixture.teardown().await;
65}
66
67pub async fn run_storage_adapter_tests<F: StorageTestFixture>() {
69 run_test::<F, _>(|a| Box::pin(test_load_should_return_none_if_no_data(a))).await;
70 run_test::<F, _>(|a| Box::pin(test_save_and_load_should_return_data_that_was_saved(a))).await;
71 run_test::<F, _>(|a| Box::pin(test_save_and_load_should_work_with_composite_keys(a))).await;
72 run_test::<F, _>(|a| Box::pin(test_save_and_load_should_work_with_large_payload(a))).await;
73 run_test::<F, _>(|a| Box::pin(test_load_range_should_return_empty_if_no_data(a))).await;
74 run_test::<F, _>(|a| Box::pin(test_save_and_load_range_should_return_all_matching_data(a)))
75 .await;
76 run_test::<F, _>(|a| Box::pin(test_save_and_load_range_should_only_load_matching_values(a)))
77 .await;
78 run_test::<F, _>(|a| Box::pin(test_save_and_remove_should_be_empty_after_removing(a))).await;
79 run_test::<F, _>(|a| Box::pin(test_save_and_save_should_overwrite(a))).await;
80}
81
82pub async fn test_load_should_return_none_if_no_data<S: Storage>(adapter: &S) {
84 let actual = adapter
85 .load(StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap())
86 .await;
87
88 assert_eq!(actual, None);
89}
90
91pub async fn test_save_and_load_should_return_data_that_was_saved<S: Storage>(adapter: &S) {
93 let key = StorageKey::from_parts(["storage-adapter-id"]).unwrap();
94 adapter.put(key.clone(), payload_a()).await;
95
96 let actual = adapter.load(key).await;
97
98 assert_eq!(actual, Some(payload_a()));
99}
100
101pub async fn test_save_and_load_should_work_with_composite_keys<S: Storage>(adapter: &S) {
102 let key = StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap();
103 adapter.put(key.clone(), payload_a()).await;
104
105 let actual = adapter.load(key).await;
106
107 assert_eq!(actual, Some(payload_a()));
108}
109
110pub async fn test_save_and_load_should_work_with_large_payload<S: Storage>(adapter: &S) {
111 let key = StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap();
112 adapter.put(key.clone(), large_payload()).await;
113
114 let actual = adapter.load(key).await;
115
116 assert_eq!(actual, Some(large_payload()));
117}
118
119pub async fn test_load_range_should_return_empty_if_no_data<S: Storage>(adapter: &S) {
121 let result = adapter.load_range(StorageKey::from_parts(["AAAAA"]).unwrap()).await;
122
123 assert_eq!(result.len(), 0);
124}
125
126pub async fn test_save_and_load_range_should_return_all_matching_data<S: Storage>(adapter: &S) {
128 let key_a = StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap();
129 let key_b = StorageKey::from_parts(["AAAAA", "snapshot", "yyyyy"]).unwrap();
130 let key_c = StorageKey::from_parts(["AAAAA", "sync-state", "zzzzz"]).unwrap();
131
132 adapter.put(key_a.clone(), payload_a()).await;
133 adapter.put(key_b.clone(), payload_b()).await;
134 adapter.put(key_c.clone(), payload_c()).await;
135
136 let result = adapter.load_range(StorageKey::from_parts(["AAAAA"]).unwrap()).await;
137
138 assert_eq!(result.len(), 3);
139 assert_eq!(result.get(&key_a), Some(&payload_a()));
140 assert_eq!(result.get(&key_b), Some(&payload_b()));
141 assert_eq!(result.get(&key_c), Some(&payload_c()));
142
143 let sync_result = adapter
144 .load_range(StorageKey::from_parts(["AAAAA", "sync-state"]).unwrap())
145 .await;
146
147 assert_eq!(sync_result.len(), 2);
148 assert_eq!(sync_result.get(&key_a), Some(&payload_a()));
149 assert_eq!(sync_result.get(&key_c), Some(&payload_c()));
150}
151
152pub async fn test_save_and_load_range_should_only_load_matching_values<S: Storage>(adapter: &S) {
153 let key_a = StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap();
154 let key_c = StorageKey::from_parts(["BBBBB", "sync-state", "zzzzz"]).unwrap();
155
156 adapter.put(key_a.clone(), payload_a()).await;
157 adapter.put(key_c.clone(), payload_c()).await;
158
159 let actual = adapter.load_range(StorageKey::from_parts(["AAAAA"]).unwrap()).await;
160
161 assert_eq!(actual.len(), 1);
162 assert_eq!(actual.get(&key_a), Some(&payload_a()));
163}
164
165pub async fn test_save_and_remove_should_be_empty_after_removing<S: Storage>(adapter: &S) {
167 let key = StorageKey::from_parts(["AAAAA", "snapshot", "xxxxx"]).unwrap();
168 adapter.put(key.clone(), payload_a()).await;
169 adapter.delete(key.clone()).await;
170
171 let range_result = adapter.load_range(StorageKey::from_parts(["AAAAA"]).unwrap()).await;
172 assert_eq!(range_result.len(), 0);
173
174 let load_result = adapter.load(key).await;
175 assert_eq!(load_result, None);
176}
177
178pub async fn test_save_and_save_should_overwrite<S: Storage>(adapter: &S) {
180 let key = StorageKey::from_parts(["AAAAA", "sync-state", "xxxxx"]).unwrap();
181 adapter.put(key.clone(), payload_a()).await;
182 adapter.put(key.clone(), payload_b()).await;
183
184 let result = adapter
185 .load_range(StorageKey::from_parts(["AAAAA", "sync-state"]).unwrap())
186 .await;
187
188 assert_eq!(result.len(), 1);
189 assert_eq!(result.get(&key), Some(&payload_b()));
190}