Skip to main content

ddd_toolkit_core/adapter/
clock.rs

1use crate::port::clock::Clock;
2
3/// A [`Clock`] backed by [`chrono::Utc::now`].
4///
5/// # Examples
6///
7/// ```
8/// use ddd_toolkit_core::adapter::clock::SystemClock;
9/// use ddd_toolkit_core::port::clock::Clock;
10///
11/// let clock = SystemClock;
12///
13/// assert!(clock.now() <= chrono::Utc::now());
14/// ```
15#[derive(Debug, Clone, Copy, Default)]
16pub struct SystemClock;
17
18impl Clock for SystemClock {
19    fn now(&self) -> chrono::DateTime<chrono::Utc> {
20        chrono::Utc::now()
21    }
22}
23
24#[cfg(test)]
25mod test {
26    use super::*;
27
28    // Wall-clock time (chrono::Utc::now(), which SystemClock wraps) is not
29    // guaranteed monotonic: NTP slew/step corrections can move it backward
30    // between two calls. Assert closeness to a reference instant instead of
31    // strict before/after ordering, so the test isn't flaky under clock
32    // adjustments while still catching a grossly wrong (e.g. fixed/mocked)
33    // implementation.
34    fn assert_close_to_now(
35        now: chrono::DateTime<chrono::Utc>,
36        reference: chrono::DateTime<chrono::Utc>,
37    ) {
38        let diff = (now - reference).num_milliseconds().abs();
39        assert!(
40            diff < 5_000,
41            "expected {now} to be within 5s of {reference}, was {diff}ms apart"
42        );
43    }
44
45    #[test]
46    fn now_returns_time_close_to_wall_clock() {
47        let reference = chrono::Utc::now();
48        let now = SystemClock.now();
49
50        assert_close_to_now(now, reference);
51    }
52
53    #[test]
54    fn now_via_dyn_clock_trait_object_returns_time() {
55        let clock: Box<dyn Clock> = Box::new(SystemClock);
56
57        let reference = chrono::Utc::now();
58        let now = clock.now();
59
60        assert_close_to_now(now, reference);
61    }
62
63    #[test]
64    fn debug_formats_struct_name() {
65        assert_eq!(format!("{:?}", SystemClock), "SystemClock");
66    }
67
68    #[test]
69    fn copy_and_clone_produce_usable_instances() {
70        let original = SystemClock;
71        let copied = original;
72        #[allow(clippy::clone_on_copy)]
73        let cloned = original.clone();
74
75        let reference = chrono::Utc::now();
76        let now_copied = copied.now();
77        let now_cloned = cloned.now();
78
79        assert_close_to_now(now_copied, reference);
80        assert_close_to_now(now_cloned, reference);
81    }
82
83    #[test]
84    fn default_constructs_usable_system_clock() {
85        #[allow(clippy::default_constructed_unit_structs)]
86        let clock = SystemClock::default();
87
88        let reference = chrono::Utc::now();
89        let now = clock.now();
90
91        assert_close_to_now(now, reference);
92    }
93
94    #[test]
95    fn system_clock_is_send_and_sync() {
96        fn assert_send_sync<T: Send + Sync>() {}
97
98        assert_send_sync::<SystemClock>();
99    }
100}