cloudiful-scheduler 0.4.7

Single-job async scheduling library for background work with optional Valkey-backed state.
Documentation
use super::align_to_window;
use crate::{JobTimeWindow, TimeWindowAlignment, TimeWindowSegment};
use chrono::{NaiveTime, TimeZone, Utc, Weekday};

fn utc_segment(start_hour: u32, end_hour: u32) -> JobTimeWindow {
    JobTimeWindow {
        timezone: Some(chrono_tz::UTC),
        weekdays: Vec::new(),
        segments: vec![TimeWindowSegment::new(
            NaiveTime::from_hms_opt(start_hour, 0, 0).unwrap(),
            NaiveTime::from_hms_opt(end_hour, 0, 0).unwrap(),
        )],
    }
}

#[test]
fn disabled_alignment_returns_candidate_unchanged() {
    let candidate = Utc.with_ymd_and_hms(2026, 5, 8, 8, 0, 0).unwrap();
    let aligned = align_to_window(
        Some(candidate),
        TimeWindowAlignment::Disabled,
        Some(&utc_segment(9, 10)),
        chrono_tz::UTC,
    );

    assert_eq!(aligned, Some(candidate));
}

#[test]
fn candidate_inside_window_is_unchanged() {
    let candidate = Utc.with_ymd_and_hms(2026, 5, 8, 9, 30, 0).unwrap();
    let aligned = align_to_window(
        Some(candidate),
        TimeWindowAlignment::AlignToNextWindow,
        Some(&utc_segment(9, 10)),
        chrono_tz::UTC,
    );

    assert_eq!(aligned, Some(candidate));
}

#[test]
fn candidate_outside_window_moves_to_next_start() {
    let candidate = Utc.with_ymd_and_hms(2026, 5, 8, 10, 15, 0).unwrap();
    let aligned = align_to_window(
        Some(candidate),
        TimeWindowAlignment::AlignToNextWindow,
        Some(&utc_segment(9, 10)),
        chrono_tz::UTC,
    );

    assert_eq!(
        aligned,
        Some(Utc.with_ymd_and_hms(2026, 5, 9, 9, 0, 0).unwrap())
    );
}

#[test]
fn weekday_only_window_moves_to_next_allowed_day() {
    let candidate = Utc.with_ymd_and_hms(2026, 5, 9, 10, 15, 0).unwrap();
    let window = JobTimeWindow {
        timezone: Some(chrono_tz::UTC),
        weekdays: vec![Weekday::Mon],
        segments: Vec::new(),
    };

    let aligned = align_to_window(
        Some(candidate),
        TimeWindowAlignment::AlignToNextWindow,
        Some(&window),
        chrono_tz::UTC,
    );

    assert_eq!(
        aligned,
        Some(Utc.with_ymd_and_hms(2026, 5, 11, 0, 0, 0).unwrap())
    );
}