use chrono::{NaiveTime, Weekday};
use chrono_tz::Asia::Shanghai;
use scheduler::{
InMemoryStateStore, Job, JobTimeWindow, Schedule, Scheduler, SchedulerConfig, Task,
TimeWindowSegment, WindowedIntervalSchedule,
};
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use std::time::Duration;
fn all_day_window() -> JobTimeWindow {
JobTimeWindow {
timezone: Some(Shanghai),
weekdays: Vec::new(),
segments: vec![TimeWindowSegment::new(
NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
NaiveTime::from_hms_opt(23, 59, 59).unwrap(),
)],
}
}
#[tokio::test]
async fn windowed_interval_runs_through_scheduler_public_api() {
let scheduler = Scheduler::new(SchedulerConfig::default(), InMemoryStateStore::new());
let invocations = Arc::new(AtomicUsize::new(0));
let seen = invocations.clone();
let schedule = WindowedIntervalSchedule::new(Some(Duration::from_secs(60)))
.with_window(all_day_window(), Some(Duration::from_millis(20)));
let report = scheduler
.run(
Job::without_deps(
"windowed-fast",
Schedule::WindowedInterval(schedule),
Task::from_async(move |_| {
let seen = seen.clone();
async move {
seen.fetch_add(1, Ordering::SeqCst);
Ok(())
}
}),
)
.with_max_runs(2),
)
.await
.unwrap();
assert_eq!(invocations.load(Ordering::SeqCst), 2);
assert_eq!(report.history.len(), 2);
assert!(report.last_skip_reason.is_none());
assert!(report.state.next_run_at.is_none());
}
#[tokio::test]
async fn windowed_interval_all_disabled_exits_without_running() {
let scheduler = Scheduler::new(SchedulerConfig::default(), InMemoryStateStore::new());
let invocations = Arc::new(AtomicUsize::new(0));
let seen = invocations.clone();
let schedule = WindowedIntervalSchedule::new(None).with_window(all_day_window(), None);
let report = scheduler
.run(Job::without_deps(
"windowed-disabled",
Schedule::WindowedInterval(schedule),
Task::from_async(move |_| {
let seen = seen.clone();
async move {
seen.fetch_add(1, Ordering::SeqCst);
Ok(())
}
}),
))
.await
.unwrap();
assert_eq!(invocations.load(Ordering::SeqCst), 0);
assert!(report.history.is_empty());
assert!(report.state.next_run_at.is_none());
}
#[test]
fn market_window_example_uses_public_types() {
let market_open = JobTimeWindow {
timezone: Some(Shanghai),
weekdays: vec![
Weekday::Mon,
Weekday::Tue,
Weekday::Wed,
Weekday::Thu,
Weekday::Fri,
],
segments: vec![
TimeWindowSegment::new(
NaiveTime::from_hms_opt(9, 30, 0).unwrap(),
NaiveTime::from_hms_opt(11, 30, 0).unwrap(),
),
TimeWindowSegment::new(
NaiveTime::from_hms_opt(13, 0, 0).unwrap(),
NaiveTime::from_hms_opt(15, 0, 0).unwrap(),
),
],
};
let schedule = WindowedIntervalSchedule::new(Some(Duration::from_secs(30 * 60)))
.with_window(market_open, Some(Duration::from_secs(30)));
assert!(matches!(
Schedule::WindowedInterval(schedule),
Schedule::WindowedInterval(_)
));
}