use durare::{
DurableContext, DurableEngine, InMemoryProvider, ListFilter, Result, ScheduleFilter,
ScheduledInput, StateProvider,
};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
static RUNS: AtomicUsize = AtomicUsize::new(0);
#[durare::workflow(schedule = "* * * * * *")]
async fn cron_tick(_ctx: DurableContext, _tick: ScheduledInput) -> Result<()> {
RUNS.fetch_add(1, Ordering::SeqCst);
Ok(())
}
#[tokio::test]
async fn cron_fires_once_per_tick_across_executors() -> Result<()> {
let provider = Arc::new(InMemoryProvider::new());
let a = DurableEngine::new(provider.clone()).await?;
let b = DurableEngine::new(provider.clone()).await?;
a.launch().await?;
b.launch().await?;
tokio::time::sleep(Duration::from_millis(2200)).await;
a.shutdown(Duration::from_secs(1)).await?;
b.shutdown(Duration::from_secs(1)).await?;
let rows = provider
.list_workflows(&ListFilter {
workflow_id_prefix: vec!["sched-".to_string()],
..Default::default()
})
.await?;
let runs = RUNS.load(Ordering::SeqCst);
assert!(
runs >= 1,
"the cron schedule should have fired at least once"
);
assert_eq!(
runs,
rows.len(),
"each tick must run exactly once across both executors"
);
assert!(rows.iter().all(|r| r.id.starts_with("sched-cron_tick-")));
assert!(
provider
.list_schedules(&ScheduleFilter::default())
.await?
.is_empty(),
"macro schedules must not be persisted to the schedule table"
);
Ok(())
}
#[tokio::test]
async fn lists_registered_and_scheduled_workflows() -> Result<()> {
let mut engine = DurableEngine::new(Arc::new(InMemoryProvider::new())).await?;
engine.register("manual_noop", |_ctx: DurableContext, _: ()| async move {
Ok::<_, durare::Error>(())
});
let all = engine.list_registered_workflows();
assert!(all
.iter()
.any(|w| w.name == "manual_noop" && w.cron_schedule.is_none()));
let tick = all
.iter()
.find(|w| w.name == "cron_tick")
.expect("cron_tick is auto-registered in this binary");
assert_eq!(tick.cron_schedule.as_deref(), Some("* * * * * *"));
let names: Vec<&str> = all.iter().map(|w| w.name.as_str()).collect();
let mut sorted = names.clone();
sorted.sort();
assert_eq!(names, sorted);
let scheduled = engine.list_scheduled_workflows();
assert!(scheduled.iter().all(|w| w.cron_schedule.is_some()));
assert!(scheduled.iter().any(|w| w.name == "cron_tick"));
assert!(!scheduled.iter().any(|w| w.name == "manual_noop"));
Ok(())
}