1use anyhow::Result;
2use async_trait::async_trait;
3use aws_sdk_s3::primitives::DateTime;
4use aws_smithy_types_convert::date_time::DateTimeExt;
5use tracing::debug;
6
7use crate::config::FilterConfig;
8use crate::pipeline::filter::{ObjectFilter, ObjectFilterBase};
9use crate::pipeline::stage::Stage;
10use crate::storage::e_tag_verify::normalize_e_tag;
11use crate::types::{ObjectKey, ObjectKeyMap, S3syncObject, sha1_digest_from_key};
12
13pub struct TargetModifiedFilter<'a> {
14 base: ObjectFilterBase<'a>,
15}
16
17const FILTER_NAME: &str = "TargetModifiedFilter";
18
19impl TargetModifiedFilter<'_> {
20 pub fn new(base: Stage, target_key_map: Option<ObjectKeyMap>) -> Self {
21 Self {
22 base: ObjectFilterBase {
23 base,
24 target_key_map,
25 name: FILTER_NAME,
26 },
27 }
28 }
29}
30
31#[async_trait]
32impl ObjectFilter for TargetModifiedFilter<'_> {
33 async fn filter(&self) -> Result<()> {
34 if self.base.base.config.filter_config.check_size {
35 self.base.filter(is_modified_from_size).await
36 } else if self.base.base.config.filter_config.check_etag
37 && !self.base.base.config.transfer_config.auto_chunksize
38 {
39 self.base.filter(is_modified_from_e_tag).await
40 } else if self
41 .base
42 .base
43 .config
44 .filter_config
45 .check_checksum_algorithm
46 .is_some()
47 || (self.base.base.config.filter_config.check_etag
48 && self.base.base.config.transfer_config.auto_chunksize)
49 {
50 self.base.filter(always_modified).await
52 } else {
53 self.base.filter(is_modified_from_timestamp).await
54 }
55 }
56}
57
58fn is_modified_from_timestamp(
59 object: &S3syncObject,
60 _: &FilterConfig,
61 target_key_map: &ObjectKeyMap,
62) -> bool {
63 let target_key_map_map = target_key_map.lock().unwrap();
64 let key = object.key();
65 let source_last_modified_date = object.last_modified();
66
67 let result = target_key_map_map.get(&ObjectKey::KeySHA1Digest(sha1_digest_from_key(key)));
68 if let Some(entry) = result {
69 return filter_last_modified(key, source_last_modified_date, &entry.last_modified);
70 }
71
72 let result = target_key_map_map.get(&ObjectKey::KeyString(key.to_string()));
73 if let Some(entry) = result {
74 return filter_last_modified(key, source_last_modified_date, &entry.last_modified);
75 }
76
77 true
78}
79
80fn filter_last_modified(
81 key: &str,
82 source_last_modified_date: &DateTime,
83 target_last_modified_date: &DateTime,
84) -> bool {
85 let modified =
86 is_source_last_modified_date_newer(source_last_modified_date, target_last_modified_date);
87 if !modified {
88 let source_last_modified =
89 DateTime::from_millis(source_last_modified_date.to_millis().unwrap())
90 .to_chrono_utc()
91 .unwrap()
92 .to_rfc3339();
93 let target_last_modified =
94 DateTime::from_millis(target_last_modified_date.to_millis().unwrap())
95 .to_chrono_utc()
96 .unwrap()
97 .to_rfc3339();
98
99 debug!(
100 name = FILTER_NAME,
101 source_last_modified = source_last_modified,
102 target_last_modified = target_last_modified,
103 key = key,
104 "object filtered."
105 );
106 }
107
108 modified
109}
110
111fn is_source_last_modified_date_newer(
112 source_last_modified_date: &DateTime,
113 target_last_modified_date: &DateTime,
114) -> bool {
115 target_last_modified_date.secs() < source_last_modified_date.secs()
117}
118
119fn is_modified_from_size(
120 object: &S3syncObject,
121 _: &FilterConfig,
122 target_key_map: &ObjectKeyMap,
123) -> bool {
124 let target_key_map_map = target_key_map.lock().unwrap();
125 let key = object.key();
126
127 let result = target_key_map_map.get(&ObjectKey::KeySHA1Digest(sha1_digest_from_key(key)));
128 if let Some(entry) = result {
129 let modified = entry.content_length != object.size();
130 if !modified {
131 debug!(
132 name = FILTER_NAME,
133 content_length = entry.content_length,
134 key = key,
135 "object filtered(same size)."
136 );
137 }
138 return modified;
139 }
140
141 let result = target_key_map_map.get(&ObjectKey::KeyString(key.to_string()));
142 if let Some(entry) = result {
143 let modified = entry.content_length != object.size();
144 if !modified {
145 debug!(
146 name = FILTER_NAME,
147 content_length = entry.content_length,
148 key = key,
149 "object filtered(same size)."
150 );
151 }
152 return modified;
153 }
154
155 true
156}
157
158fn is_modified_from_e_tag(
159 object: &S3syncObject,
160 _: &FilterConfig,
161 target_key_map: &ObjectKeyMap,
162) -> bool {
163 let locked_target_key_map = target_key_map.lock().unwrap();
164 let key = object.key();
165
166 let mut result =
167 locked_target_key_map.get(&ObjectKey::KeySHA1Digest(sha1_digest_from_key(key)));
168 if result.is_none() {
169 result = locked_target_key_map.get(&ObjectKey::KeyString(key.to_string()));
170 }
171
172 if let Some(entry) = result {
173 let source_e_tag = normalize_e_tag(&Some(object.e_tag().unwrap().to_string()));
174 let target_e_tag = normalize_e_tag(&Some(entry.e_tag.clone().unwrap()));
175
176 let source_last_modified =
177 DateTime::from_millis(object.last_modified().to_millis().unwrap())
178 .to_chrono_utc()
179 .unwrap()
180 .to_rfc3339();
181 let target_last_modified = DateTime::from_millis(entry.last_modified.to_millis().unwrap())
182 .to_chrono_utc()
183 .unwrap()
184 .to_rfc3339();
185
186 if source_e_tag == target_e_tag {
187 debug!(
188 name = FILTER_NAME,
189 source_e_tag = source_e_tag,
190 target_e_tag = target_e_tag,
191 source_last_modified = source_last_modified,
192 target_last_modified = target_last_modified,
193 source_size = object.size(),
194 target_size = entry.content_length,
195 key = key,
196 "object filtered. ETags are the same."
197 );
198 return false;
199 } else {
200 debug!(
201 name = FILTER_NAME,
202 source_e_tag = source_e_tag,
203 target_e_tag = target_e_tag,
204 source_last_modified = source_last_modified,
205 target_last_modified = target_last_modified,
206 source_size = object.size(),
207 target_size = entry.content_length,
208 key = key,
209 "ETags are different."
210 );
211 }
212
213 return true;
214 }
215
216 true
217}
218
219fn always_modified(_: &S3syncObject, _: &FilterConfig, _: &ObjectKeyMap) -> bool {
220 true
221}
222
223#[cfg(test)]
224mod tests {
225 use std::collections::HashMap;
226 use std::sync::Mutex;
227
228 use super::*;
229 use crate::callback::filter_manager::FilterManager;
230 use crate::config::FilterConfig;
231 use crate::types::{ObjectEntry, S3syncObject};
232 use aws_sdk_s3::primitives::DateTime;
233 use aws_sdk_s3::types::Object;
234 use tracing_subscriber::EnvFilter;
235
236 #[tokio::test]
237 async fn not_modified_sha1() {
238 init_dummy_tracing_subscriber();
239
240 let object = S3syncObject::NotVersioning(
241 Object::builder()
242 .key("test")
243 .last_modified(DateTime::from_secs(1))
244 .build(),
245 );
246
247 let config = FilterConfig {
248 before_time: None,
249 after_time: None,
250 remove_modified_filter: false,
251 check_size: false,
252 check_etag: false,
253 check_mtime_and_etag: false,
254 check_checksum_algorithm: None,
255 check_mtime_and_additional_checksum: None,
256 include_regex: None,
257 exclude_regex: None,
258 include_content_type_regex: None,
259 exclude_content_type_regex: None,
260 include_metadata_regex: None,
261 exclude_metadata_regex: None,
262 include_tag_regex: None,
263 exclude_tag_regex: None,
264 larger_size: None,
265 smaller_size: None,
266 filter_manager: FilterManager::new(),
267 };
268
269 assert!(is_modified_from_timestamp(
270 &object,
271 &config,
272 &ObjectKeyMap::new(Mutex::new(HashMap::new()))
273 ));
274 }
275
276 #[tokio::test]
277 async fn modified_sha1() {
278 init_dummy_tracing_subscriber();
279
280 let object = S3syncObject::NotVersioning(
281 Object::builder()
282 .key("test")
283 .last_modified(DateTime::from_secs(1))
284 .build(),
285 );
286
287 let config = FilterConfig {
288 before_time: None,
289 after_time: None,
290 remove_modified_filter: false,
291 check_size: false,
292 check_etag: false,
293 check_mtime_and_etag: false,
294 check_checksum_algorithm: None,
295 check_mtime_and_additional_checksum: None,
296 include_regex: None,
297 exclude_regex: None,
298 include_content_type_regex: None,
299 exclude_content_type_regex: None,
300 include_metadata_regex: None,
301 exclude_metadata_regex: None,
302 include_tag_regex: None,
303 exclude_tag_regex: None,
304 larger_size: None,
305 smaller_size: None,
306 filter_manager: FilterManager::new(),
307 };
308
309 let mut key_map = HashMap::new();
310 key_map.insert(
311 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
312 ObjectEntry {
313 last_modified: DateTime::from_secs(1),
314 content_length: 1,
315 e_tag: None,
316 },
317 );
318
319 assert!(!is_modified_from_timestamp(
320 &object,
321 &config,
322 &ObjectKeyMap::new(Mutex::new(key_map))
323 ));
324 }
325
326 #[tokio::test]
327 async fn is_modified_true() {
328 init_dummy_tracing_subscriber();
329
330 let object = S3syncObject::NotVersioning(
331 Object::builder()
332 .key("test")
333 .last_modified(DateTime::from_secs(1))
334 .build(),
335 );
336
337 let config = FilterConfig {
338 before_time: None,
339 after_time: None,
340 remove_modified_filter: false,
341 check_size: false,
342 check_etag: false,
343 check_mtime_and_etag: false,
344 check_checksum_algorithm: None,
345 check_mtime_and_additional_checksum: None,
346 include_regex: None,
347 exclude_regex: None,
348 include_metadata_regex: None,
349 exclude_metadata_regex: None,
350 include_content_type_regex: None,
351 exclude_content_type_regex: None,
352 include_tag_regex: None,
353 exclude_tag_regex: None,
354 larger_size: None,
355 smaller_size: None,
356 filter_manager: FilterManager::new(),
357 };
358
359 assert!(is_modified_from_timestamp(
360 &object,
361 &config,
362 &ObjectKeyMap::new(Mutex::new(HashMap::new()))
363 ));
364 }
365
366 #[tokio::test]
367 async fn is_modified_false() {
368 init_dummy_tracing_subscriber();
369
370 let object = S3syncObject::NotVersioning(
371 Object::builder()
372 .key("test")
373 .last_modified(DateTime::from_secs(1))
374 .build(),
375 );
376
377 let config = FilterConfig {
378 before_time: None,
379 after_time: None,
380 remove_modified_filter: false,
381 check_size: false,
382 check_etag: false,
383 check_mtime_and_etag: false,
384 check_checksum_algorithm: None,
385 check_mtime_and_additional_checksum: None,
386 include_regex: None,
387 exclude_regex: None,
388 include_content_type_regex: None,
389 exclude_content_type_regex: None,
390 include_metadata_regex: None,
391 exclude_metadata_regex: None,
392 include_tag_regex: None,
393 exclude_tag_regex: None,
394 larger_size: None,
395 smaller_size: None,
396 filter_manager: FilterManager::new(),
397 };
398
399 let mut key_map = HashMap::new();
400 key_map.insert(
401 ObjectKey::KeyString("test".to_string()),
402 ObjectEntry {
403 last_modified: DateTime::from_secs(1),
404 content_length: 1,
405 e_tag: None,
406 },
407 );
408
409 assert!(!is_modified_from_timestamp(
410 &object,
411 &config,
412 &ObjectKeyMap::new(Mutex::new(key_map))
413 ));
414 }
415
416 #[test]
417 fn filter_modified_false() {
418 init_dummy_tracing_subscriber();
419
420 assert!(!filter_last_modified(
421 "key",
422 &DateTime::from_secs(0),
423 &DateTime::from_secs(1)
424 ));
425 assert!(!filter_last_modified(
426 "key",
427 &DateTime::from_secs(1),
428 &DateTime::from_secs(1)
429 ));
430 }
431
432 #[test]
433 fn filter_modified_true() {
434 init_dummy_tracing_subscriber();
435
436 assert!(filter_last_modified(
437 "key",
438 &DateTime::from_secs(1),
439 &DateTime::from_secs(0)
440 ));
441 }
442
443 #[tokio::test]
444 async fn size_modified_sha1_empty() {
445 init_dummy_tracing_subscriber();
446
447 let object = S3syncObject::NotVersioning(Object::builder().key("test").size(1).build());
448
449 let config = FilterConfig {
450 before_time: None,
451 after_time: None,
452 remove_modified_filter: false,
453 check_size: true,
454 check_etag: false,
455 check_mtime_and_etag: false,
456 check_checksum_algorithm: None,
457 check_mtime_and_additional_checksum: None,
458 include_regex: None,
459 exclude_regex: None,
460 include_content_type_regex: None,
461 exclude_content_type_regex: None,
462 include_metadata_regex: None,
463 exclude_metadata_regex: None,
464 include_tag_regex: None,
465 exclude_tag_regex: None,
466 larger_size: None,
467 smaller_size: None,
468 filter_manager: FilterManager::new(),
469 };
470
471 assert!(is_modified_from_size(
472 &object,
473 &config,
474 &ObjectKeyMap::new(Mutex::new(HashMap::new()))
475 ));
476 }
477
478 #[tokio::test]
479 async fn size_not_modified_sha1() {
480 init_dummy_tracing_subscriber();
481
482 let object = S3syncObject::NotVersioning(Object::builder().key("test").size(1).build());
483
484 let config = FilterConfig {
485 before_time: None,
486 after_time: None,
487 remove_modified_filter: false,
488 check_size: true,
489 check_etag: false,
490 check_mtime_and_etag: false,
491 check_checksum_algorithm: None,
492 check_mtime_and_additional_checksum: None,
493 include_regex: None,
494 exclude_regex: None,
495 include_content_type_regex: None,
496 exclude_content_type_regex: None,
497 include_metadata_regex: None,
498 exclude_metadata_regex: None,
499 include_tag_regex: None,
500 exclude_tag_regex: None,
501 larger_size: None,
502 smaller_size: None,
503 filter_manager: FilterManager::new(),
504 };
505
506 let mut key_map = HashMap::new();
507 key_map.insert(
508 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
509 ObjectEntry {
510 last_modified: DateTime::from_secs(99),
511 content_length: 1,
512 e_tag: None,
513 },
514 );
515
516 assert!(!is_modified_from_size(
517 &object,
518 &config,
519 &ObjectKeyMap::new(Mutex::new(key_map))
520 ));
521 }
522
523 #[tokio::test]
524 async fn size_modified_sha1() {
525 init_dummy_tracing_subscriber();
526
527 let object = S3syncObject::NotVersioning(Object::builder().key("test").size(1).build());
528
529 let config = FilterConfig {
530 before_time: None,
531 after_time: None,
532 remove_modified_filter: false,
533 check_size: true,
534 check_etag: false,
535 check_mtime_and_etag: false,
536 check_checksum_algorithm: None,
537 check_mtime_and_additional_checksum: None,
538 include_regex: None,
539 exclude_regex: None,
540 include_content_type_regex: None,
541 exclude_content_type_regex: None,
542 include_metadata_regex: None,
543 exclude_metadata_regex: None,
544 include_tag_regex: None,
545 exclude_tag_regex: None,
546 larger_size: None,
547 smaller_size: None,
548 filter_manager: FilterManager::new(),
549 };
550
551 let mut key_map = HashMap::new();
552 key_map.insert(
553 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
554 ObjectEntry {
555 last_modified: DateTime::from_secs(99),
556 content_length: 2,
557 e_tag: None,
558 },
559 );
560
561 assert!(is_modified_from_size(
562 &object,
563 &config,
564 &ObjectKeyMap::new(Mutex::new(key_map))
565 ));
566 }
567
568 #[tokio::test]
569 async fn size_not_modified_key() {
570 init_dummy_tracing_subscriber();
571
572 let object = S3syncObject::NotVersioning(Object::builder().key("test").size(1).build());
573
574 let config = FilterConfig {
575 before_time: None,
576 after_time: None,
577 remove_modified_filter: false,
578 check_size: true,
579 check_etag: false,
580 check_mtime_and_etag: false,
581 check_checksum_algorithm: None,
582 check_mtime_and_additional_checksum: None,
583 include_regex: None,
584 exclude_regex: None,
585 include_content_type_regex: None,
586 exclude_content_type_regex: None,
587 include_metadata_regex: None,
588 exclude_metadata_regex: None,
589 include_tag_regex: None,
590 exclude_tag_regex: None,
591 larger_size: None,
592 smaller_size: None,
593 filter_manager: FilterManager::new(),
594 };
595
596 let mut key_map = HashMap::new();
597 key_map.insert(
598 ObjectKey::KeyString("test".to_string()),
599 ObjectEntry {
600 last_modified: DateTime::from_secs(99),
601 content_length: 1,
602 e_tag: None,
603 },
604 );
605
606 assert!(!is_modified_from_size(
607 &object,
608 &config,
609 &ObjectKeyMap::new(Mutex::new(key_map))
610 ));
611 }
612
613 #[tokio::test]
614 async fn size_modified_key() {
615 init_dummy_tracing_subscriber();
616
617 let object = S3syncObject::NotVersioning(Object::builder().key("test").size(1).build());
618
619 let config = FilterConfig {
620 before_time: None,
621 after_time: None,
622 remove_modified_filter: false,
623 check_size: true,
624 check_etag: false,
625 check_mtime_and_etag: false,
626 check_checksum_algorithm: None,
627 check_mtime_and_additional_checksum: None,
628 include_regex: None,
629 exclude_regex: None,
630 include_content_type_regex: None,
631 exclude_content_type_regex: None,
632 include_metadata_regex: None,
633 exclude_metadata_regex: None,
634 include_tag_regex: None,
635 exclude_tag_regex: None,
636 larger_size: None,
637 smaller_size: None,
638 filter_manager: FilterManager::new(),
639 };
640
641 let mut key_map = HashMap::new();
642 key_map.insert(
643 ObjectKey::KeyString("test".to_string()),
644 ObjectEntry {
645 last_modified: DateTime::from_secs(99),
646 content_length: 2,
647 e_tag: None,
648 },
649 );
650
651 assert!(is_modified_from_size(
652 &object,
653 &config,
654 &ObjectKeyMap::new(Mutex::new(key_map))
655 ));
656 }
657
658 fn init_dummy_tracing_subscriber() {
659 let _ = tracing_subscriber::fmt()
660 .with_env_filter(
661 EnvFilter::try_from_default_env()
662 .or_else(|_| EnvFilter::try_new("trace"))
663 .unwrap(),
664 )
665 .try_init();
666 }
667
668 #[tokio::test]
669 async fn size_not_modified_e_tag() {
670 init_dummy_tracing_subscriber();
671
672 let object = S3syncObject::NotVersioning(
673 Object::builder()
674 .key("test")
675 .size(1)
676 .e_tag("0dd7cd23c492b5a3a62672b4049bb1ed")
677 .last_modified(DateTime::from_secs(99))
678 .build(),
679 );
680
681 let config = FilterConfig {
682 before_time: None,
683 after_time: None,
684 remove_modified_filter: false,
685 check_size: false,
686 check_etag: true,
687 check_mtime_and_etag: false,
688 check_checksum_algorithm: None,
689 check_mtime_and_additional_checksum: None,
690 include_regex: None,
691 exclude_regex: None,
692 include_content_type_regex: None,
693 exclude_content_type_regex: None,
694 include_metadata_regex: None,
695 exclude_metadata_regex: None,
696 include_tag_regex: None,
697 exclude_tag_regex: None,
698 larger_size: None,
699 smaller_size: None,
700 filter_manager: FilterManager::new(),
701 };
702
703 let mut key_map = HashMap::new();
704 key_map.insert(
705 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
706 ObjectEntry {
707 last_modified: DateTime::from_secs(99),
708 content_length: 1,
709 e_tag: Some("0dd7cd23c492b5a3a62672b4049bb1ed".to_string()),
710 },
711 );
712
713 assert!(!is_modified_from_e_tag(
714 &object,
715 &config,
716 &ObjectKeyMap::new(Mutex::new(key_map))
717 ));
718 }
719
720 #[tokio::test]
721 async fn size_not_modified_e_tag_normalize_source() {
722 init_dummy_tracing_subscriber();
723
724 let object = S3syncObject::NotVersioning(
725 Object::builder()
726 .key("test")
727 .size(1)
728 .e_tag("\"0dd7cd23c492b5a3a62672b4049bb1ed\"")
729 .last_modified(DateTime::from_secs(99))
730 .build(),
731 );
732
733 let config = FilterConfig {
734 before_time: None,
735 after_time: None,
736 remove_modified_filter: false,
737 check_size: false,
738 check_etag: true,
739 check_mtime_and_etag: false,
740 check_checksum_algorithm: None,
741 check_mtime_and_additional_checksum: None,
742 include_regex: None,
743 exclude_regex: None,
744 include_content_type_regex: None,
745 exclude_content_type_regex: None,
746 include_metadata_regex: None,
747 exclude_metadata_regex: None,
748 include_tag_regex: None,
749 exclude_tag_regex: None,
750 larger_size: None,
751 smaller_size: None,
752 filter_manager: FilterManager::new(),
753 };
754
755 let mut key_map = HashMap::new();
756 key_map.insert(
757 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
758 ObjectEntry {
759 last_modified: DateTime::from_secs(99),
760 content_length: 1,
761 e_tag: Some("0dd7cd23c492b5a3a62672b4049bb1ed".to_string()),
762 },
763 );
764
765 assert!(!is_modified_from_e_tag(
766 &object,
767 &config,
768 &ObjectKeyMap::new(Mutex::new(key_map))
769 ));
770 }
771
772 #[tokio::test]
773 async fn size_not_modified_e_tag_normalize_target() {
774 init_dummy_tracing_subscriber();
775
776 let object = S3syncObject::NotVersioning(
777 Object::builder()
778 .key("test")
779 .size(1)
780 .e_tag("0dd7cd23c492b5a3a62672b4049bb1ed")
781 .last_modified(DateTime::from_secs(99))
782 .build(),
783 );
784
785 let config = FilterConfig {
786 before_time: None,
787 after_time: None,
788 remove_modified_filter: false,
789 check_size: false,
790 check_etag: true,
791 check_mtime_and_etag: false,
792 check_checksum_algorithm: None,
793 check_mtime_and_additional_checksum: None,
794 include_regex: None,
795 exclude_regex: None,
796 include_content_type_regex: None,
797 exclude_content_type_regex: None,
798 include_metadata_regex: None,
799 exclude_metadata_regex: None,
800 include_tag_regex: None,
801 exclude_tag_regex: None,
802 larger_size: None,
803 smaller_size: None,
804 filter_manager: FilterManager::new(),
805 };
806
807 let mut key_map = HashMap::new();
808 key_map.insert(
809 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
810 ObjectEntry {
811 last_modified: DateTime::from_secs(99),
812 content_length: 1,
813 e_tag: Some("\"0dd7cd23c492b5a3a62672b4049bb1ed\"".to_string()),
814 },
815 );
816
817 assert!(!is_modified_from_e_tag(
818 &object,
819 &config,
820 &ObjectKeyMap::new(Mutex::new(key_map))
821 ));
822 }
823
824 #[tokio::test]
825 async fn size_modified_e_tag() {
826 init_dummy_tracing_subscriber();
827
828 let object = S3syncObject::NotVersioning(
829 Object::builder()
830 .key("test")
831 .size(1)
832 .e_tag("add7cd23c492b5a3a62672b4049bb1ed")
833 .last_modified(DateTime::from_secs(99))
834 .build(),
835 );
836
837 let config = FilterConfig {
838 before_time: None,
839 after_time: None,
840 remove_modified_filter: false,
841 check_size: false,
842 check_etag: true,
843 check_mtime_and_etag: false,
844 check_checksum_algorithm: None,
845 check_mtime_and_additional_checksum: None,
846 include_regex: None,
847 exclude_regex: None,
848 include_content_type_regex: None,
849 exclude_content_type_regex: None,
850 include_metadata_regex: None,
851 exclude_metadata_regex: None,
852 include_tag_regex: None,
853 exclude_tag_regex: None,
854 larger_size: None,
855 smaller_size: None,
856 filter_manager: FilterManager::new(),
857 };
858
859 let mut key_map = HashMap::new();
860 key_map.insert(
861 ObjectKey::KeySHA1Digest(sha1_digest_from_key("test")),
862 ObjectEntry {
863 last_modified: DateTime::from_secs(99),
864 content_length: 1,
865 e_tag: Some("0dd7cd23c492b5a3a62672b4049bb1ed".to_string()),
866 },
867 );
868
869 assert!(is_modified_from_e_tag(
870 &object,
871 &config,
872 &ObjectKeyMap::new(Mutex::new(key_map))
873 ));
874 }
875}