mithril_aggregator/services/snapshotter/
test_doubles.rs1use async_trait::async_trait;
2use std::fs;
3use std::fs::File;
4use std::path::{Path, PathBuf};
5use std::sync::RwLock;
6
7use mithril_common::StdResult;
8use mithril_common::entities::{CompressionAlgorithm, ImmutableFileNumber};
9
10use crate::services::Snapshotter;
11use crate::tools::file_archiver::FileArchive;
12
13pub struct DumbSnapshotter {
15 last_snapshot: RwLock<Option<FileArchive>>,
16 compression_algorithm: CompressionAlgorithm,
17 archive_size: u64,
18}
19
20impl DumbSnapshotter {
21 pub fn new(compression_algorithm: CompressionAlgorithm) -> Self {
25 Self {
26 last_snapshot: RwLock::new(None),
27 compression_algorithm,
28 archive_size: 0,
29 }
30 }
31
32 pub fn with_archive_size(mut self, size: u64) -> Self {
34 self.archive_size = size;
35 self
36 }
37
38 pub fn get_last_snapshot(&self) -> StdResult<Option<FileArchive>> {
40 let value = self.last_snapshot.read().unwrap().as_ref().cloned();
41
42 Ok(value)
43 }
44}
45
46impl Default for DumbSnapshotter {
47 fn default() -> Self {
48 Self {
49 last_snapshot: RwLock::new(None),
50 compression_algorithm: CompressionAlgorithm::Gzip,
51 archive_size: 0,
52 }
53 }
54}
55
56#[async_trait]
57impl Snapshotter for DumbSnapshotter {
58 async fn snapshot_all_completed_immutables(
59 &self,
60 archive_name_without_extension: &str,
61 ) -> StdResult<FileArchive> {
62 let mut value = self.last_snapshot.write().unwrap();
63 let snapshot = FileArchive::new(
64 PathBuf::from(format!(
65 "{archive_name_without_extension}.{}",
66 self.compression_algorithm.tar_file_extension()
67 )),
68 self.archive_size,
69 0,
70 self.compression_algorithm,
71 );
72 *value = Some(snapshot.clone());
73
74 Ok(snapshot)
75 }
76
77 async fn snapshot_ancillary(
78 &self,
79 _immutable_file_number: ImmutableFileNumber,
80 archive_name_without_extension: &str,
81 ) -> StdResult<FileArchive> {
82 self.snapshot_all_completed_immutables(archive_name_without_extension)
83 .await
84 }
85
86 async fn snapshot_immutable_trio(
87 &self,
88 _immutable_file_number: ImmutableFileNumber,
89 archive_name_without_extension: &str,
90 ) -> StdResult<FileArchive> {
91 self.snapshot_all_completed_immutables(archive_name_without_extension)
92 .await
93 }
94
95 async fn compute_immutable_files_total_uncompressed_size(
96 &self,
97 _up_to_immutable_file_number: ImmutableFileNumber,
98 ) -> StdResult<u64> {
99 Ok(0)
100 }
101
102 fn compression_algorithm(&self) -> CompressionAlgorithm {
103 self.compression_algorithm
104 }
105}
106
107pub struct FakeSnapshotter {
109 work_dir: PathBuf,
110 compression_algorithm: CompressionAlgorithm,
111}
112
113impl FakeSnapshotter {
114 pub fn new<T: AsRef<Path>>(work_dir: T) -> Self {
116 Self {
117 work_dir: work_dir.as_ref().to_path_buf(),
118 compression_algorithm: CompressionAlgorithm::Gzip,
119 }
120 }
121
122 pub fn with_compression_algorithm(
124 mut self,
125 compression_algorithm: CompressionAlgorithm,
126 ) -> Self {
127 self.compression_algorithm = compression_algorithm;
128 self
129 }
130}
131
132#[async_trait]
133impl Snapshotter for FakeSnapshotter {
134 async fn snapshot_all_completed_immutables(
135 &self,
136 archive_name_without_extension: &str,
137 ) -> StdResult<FileArchive> {
138 let fake_archive_path = self.work_dir.join(format!(
139 "{archive_name_without_extension}.{}",
140 self.compression_algorithm.tar_file_extension()
141 ));
142 if let Some(archive_dir) = fake_archive_path.parent() {
143 fs::create_dir_all(archive_dir).unwrap();
144 }
145 File::create(&fake_archive_path).unwrap();
146
147 Ok(FileArchive::new(
148 fake_archive_path,
149 0,
150 0,
151 self.compression_algorithm,
152 ))
153 }
154
155 async fn snapshot_ancillary(
156 &self,
157 _immutable_file_number: ImmutableFileNumber,
158 archive_name_without_extension: &str,
159 ) -> StdResult<FileArchive> {
160 self.snapshot_all_completed_immutables(archive_name_without_extension)
161 .await
162 }
163
164 async fn snapshot_immutable_trio(
165 &self,
166 _immutable_file_number: ImmutableFileNumber,
167 archive_name_without_extension: &str,
168 ) -> StdResult<FileArchive> {
169 self.snapshot_all_completed_immutables(archive_name_without_extension)
170 .await
171 }
172
173 async fn compute_immutable_files_total_uncompressed_size(
174 &self,
175 _up_to_immutable_file_number: ImmutableFileNumber,
176 ) -> StdResult<u64> {
177 Ok(0)
178 }
179
180 fn compression_algorithm(&self) -> CompressionAlgorithm {
181 self.compression_algorithm
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use mithril_common::temp_dir_create;
188
189 use super::*;
190
191 mod dumb_snapshotter {
192 use super::*;
193
194 #[test]
195 fn return_parametrized_compression_algorithm() {
196 let snapshotter = DumbSnapshotter::new(CompressionAlgorithm::Zstandard);
197 assert_eq!(
198 CompressionAlgorithm::Zstandard,
199 snapshotter.compression_algorithm()
200 );
201 }
202
203 #[tokio::test]
204 async fn test_dumb_snapshotter_snapshot_return_archive_named_with_compression_algorithm_and_size_of_0()
205 {
206 let snapshotter = DumbSnapshotter::new(CompressionAlgorithm::Gzip);
207
208 let snapshot = snapshotter
209 .snapshot_all_completed_immutables("archive_full_immutables")
210 .await
211 .unwrap();
212 assert_eq!(
213 PathBuf::from("archive_full_immutables.tar.gz"),
214 *snapshot.get_file_path()
215 );
216 assert_eq!(0, snapshot.get_archive_size());
217
218 let snapshot = snapshotter.snapshot_ancillary(3, "archive_ancillary").await.unwrap();
219 assert_eq!(
220 PathBuf::from("archive_ancillary.tar.gz"),
221 *snapshot.get_file_path()
222 );
223 assert_eq!(0, snapshot.get_archive_size());
224
225 let snapshot = snapshotter
226 .snapshot_immutable_trio(4, "archive_immutable_trio")
227 .await
228 .unwrap();
229 assert_eq!(
230 PathBuf::from("archive_immutable_trio.tar.gz"),
231 *snapshot.get_file_path()
232 );
233 assert_eq!(0, snapshot.get_archive_size());
234 }
235
236 #[tokio::test]
237 async fn test_dumb_snapshotter() {
238 let snapshotter = DumbSnapshotter::new(CompressionAlgorithm::Zstandard);
239 assert!(
240 snapshotter
241 .get_last_snapshot()
242 .expect(
243 "Dumb snapshotter::get_last_snapshot should not fail when no last snapshot."
244 )
245 .is_none()
246 );
247
248 {
249 let full_immutables_snapshot = snapshotter
250 .snapshot_all_completed_immutables("whatever")
251 .await
252 .expect("Dumb snapshotter::snapshot_all_completed_immutables should not fail.");
253 assert_eq!(
254 Some(full_immutables_snapshot),
255 snapshotter.get_last_snapshot().expect(
256 "Dumb snapshotter::get_last_snapshot should not fail when some last snapshot."
257 )
258 );
259 }
260 {
261 let ancillary_snapshot = snapshotter
262 .snapshot_ancillary(3, "whatever")
263 .await
264 .expect("Dumb snapshotter::snapshot_ancillary should not fail.");
265 assert_eq!(
266 Some(ancillary_snapshot),
267 snapshotter.get_last_snapshot().expect(
268 "Dumb snapshotter::get_last_snapshot should not fail when some last snapshot."
269 )
270 );
271 }
272 {
273 let immutable_snapshot = snapshotter
274 .snapshot_immutable_trio(4, "whatever")
275 .await
276 .expect("Dumb snapshotter::snapshot_immutable_trio should not fail.");
277 assert_eq!(
278 Some(immutable_snapshot),
279 snapshotter.get_last_snapshot().expect(
280 "Dumb snapshotter::get_last_snapshot should not fail when some last snapshot."
281 )
282 );
283 }
284 }
285
286 #[tokio::test]
287 async fn set_dumb_snapshotter_archive_size() {
288 let snapshotter = DumbSnapshotter::new(CompressionAlgorithm::Gzip);
289
290 let snapshot = snapshotter
292 .snapshot_all_completed_immutables("whatever")
293 .await
294 .unwrap();
295 assert_eq!(0, snapshot.get_archive_size());
296
297 let snapshotter = snapshotter.with_archive_size(42);
298 let snapshot = snapshotter
299 .snapshot_all_completed_immutables("whatever")
300 .await
301 .unwrap();
302 assert_eq!(42, snapshot.get_archive_size());
303 }
304 }
305
306 mod fake_snapshotter {
307 use super::*;
308
309 #[test]
310 fn return_parametrized_compression_algorithm() {
311 let snapshotter = FakeSnapshotter::new("whatever")
312 .with_compression_algorithm(CompressionAlgorithm::Zstandard);
313 assert_eq!(
314 CompressionAlgorithm::Zstandard,
315 snapshotter.compression_algorithm()
316 );
317 }
318
319 #[tokio::test]
320 async fn test_fake_snapshotter() {
321 let test_dir = temp_dir_create!();
322 let fake_snapshotter = FakeSnapshotter::new(&test_dir)
323 .with_compression_algorithm(CompressionAlgorithm::Gzip);
324
325 for filename in ["direct_child", "one_level_subdir/child", "two_levels/subdir/child"] {
326 {
327 let full_immutables_snapshot = fake_snapshotter
328 .snapshot_all_completed_immutables(filename)
329 .await
330 .unwrap();
331
332 assert_eq!(
333 full_immutables_snapshot.get_file_path(),
334 &test_dir.join(filename).with_extension("tar.gz")
335 );
336 assert!(full_immutables_snapshot.get_file_path().is_file());
337 }
338 {
339 let ancillary_snapshot =
340 fake_snapshotter.snapshot_ancillary(3, filename).await.unwrap();
341
342 assert_eq!(
343 ancillary_snapshot.get_file_path(),
344 &test_dir.join(filename).with_extension("tar.gz")
345 );
346 assert!(ancillary_snapshot.get_file_path().is_file());
347 }
348 {
349 let immutable_snapshot =
350 fake_snapshotter.snapshot_immutable_trio(5, filename).await.unwrap();
351
352 assert_eq!(
353 immutable_snapshot.get_file_path(),
354 &test_dir.join(filename).with_extension("tar.gz")
355 );
356 assert!(immutable_snapshot.get_file_path().is_file());
357 }
358 }
359 }
360 }
361}