use chrono::{DateTime, Duration, Utc};
use std::sync::Arc;
use parking_lot::Mutex;
use crate::shared::Clock;
#[derive(Debug, Clone)]
pub struct MockClock {
current_time: Arc<Mutex<DateTime<Utc>>>,
}
impl MockClock {
#[must_use]
pub fn new(initial_time: DateTime<Utc>) -> Self {
Self {
current_time: Arc::new(Mutex::new(initial_time)),
}
}
pub fn advance(&self, duration: Duration) {
let mut time = self.current_time.lock();
*time += duration;
}
pub fn advance_secs(&self, secs: i64) {
self.advance(Duration::seconds(secs));
}
pub fn set_time(&self, time: DateTime<Utc>) {
let mut current = self.current_time.lock();
*current = time;
}
}
impl Clock for MockClock {
fn now(&self) -> DateTime<Utc> {
*self.current_time.lock()
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;
#[test]
fn it_should_return_fixed_time_when_not_advanced() {
let fixed_time = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock = MockClock::new(fixed_time);
assert_eq!(clock.now(), fixed_time);
assert_eq!(clock.now(), fixed_time);
assert_eq!(clock.now(), fixed_time);
}
#[test]
fn it_should_advance_time_by_duration() {
let initial = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock = MockClock::new(initial);
clock.advance(Duration::hours(2) + Duration::minutes(30));
let expected = Utc.with_ymd_and_hms(2025, 10, 7, 14, 30, 0).unwrap();
assert_eq!(clock.now(), expected);
}
#[test]
fn it_should_advance_time_by_seconds() {
let initial = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock = MockClock::new(initial);
clock.advance_secs(90);
let expected = Utc.with_ymd_and_hms(2025, 10, 7, 12, 1, 30).unwrap();
assert_eq!(clock.now(), expected);
}
#[test]
fn it_should_set_time_to_specific_point() {
let initial = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock = MockClock::new(initial);
let new_time = Utc.with_ymd_and_hms(2025, 12, 25, 18, 30, 0).unwrap();
clock.set_time(new_time);
assert_eq!(clock.now(), new_time);
}
#[test]
fn it_should_support_multiple_advances() {
let initial = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock = MockClock::new(initial);
clock.advance_secs(30);
clock.advance_secs(30);
clock.advance_secs(30);
let expected = Utc.with_ymd_and_hms(2025, 10, 7, 12, 1, 30).unwrap();
assert_eq!(clock.now(), expected);
}
#[test]
fn it_should_be_clonable() {
let initial = Utc.with_ymd_and_hms(2025, 10, 7, 12, 0, 0).unwrap();
let clock1 = MockClock::new(initial);
let clock2 = clock1.clone();
clock1.advance_secs(60);
assert_eq!(clock1.now(), clock2.now());
}
}