#![allow(clippy::missing_docs_in_private_items)]
use super::*;
use crate::routines::available_agents;
use croner::Cron;
#[test]
fn ships_at_least_one_default() {
assert!(!DEFAULT_ROUTINES.is_empty());
}
#[test]
fn first_default_updates_moadim_cargo_package() {
let first = &DEFAULT_ROUTINES[0];
assert_eq!(first.title, "Update moadim cargo package");
assert!(first.prompt.contains("cargo install moadim --force"));
}
#[test]
fn every_schedule_is_a_valid_cron() {
for spec in DEFAULT_ROUTINES {
let normalized = normalize_schedule(spec.schedule);
assert!(
normalized.parse::<Cron>().is_ok(),
"schedule for {:?} is not a valid cron: {normalized:?}",
spec.title
);
}
}
#[test]
fn every_agent_is_a_known_builtin() {
let known = available_agents();
for spec in DEFAULT_ROUTINES {
assert!(
known.iter().any(|agent| agent == spec.agent),
"agent {:?} for routine {:?} is not a built-in agent",
spec.agent,
spec.title
);
}
}
#[test]
fn materialize_stamps_timestamps_and_marks_managed() {
let spec = &DEFAULT_ROUTINES[0];
let routine = materialize(spec, 1234);
assert_eq!(routine.created_at, 1234);
assert_eq!(routine.updated_at, 1234);
assert_eq!(routine.source, "managed");
assert!(routine.enabled);
assert!(routine.last_triggered_at.is_none());
assert!(!routine.id.is_empty());
assert_eq!(routine.schedule, normalize_schedule(spec.schedule));
}
#[test]
fn materialize_assigns_unique_ids() {
let spec = &DEFAULT_ROUTINES[0];
assert_ne!(materialize(spec, 0).id, materialize(spec, 0).id);
}
#[test]
fn reconcile_returns_none_when_up_to_date() {
let spec = &DEFAULT_ROUTINES[0];
let cur = materialize(spec, 100);
assert!(reconcile(spec, &cur, 200).is_none());
}
#[test]
fn reconcile_preserves_disabled_toggle() {
let spec = &DEFAULT_ROUTINES[0];
let mut cur = materialize(spec, 100);
cur.enabled = false;
cur.prompt = "stale prompt".to_string();
let updated = reconcile(spec, &cur, 200).expect("drifted routine should be rewritten");
assert!(
!updated.enabled,
"must not re-enable a user-disabled default"
);
assert_eq!(updated.prompt, spec.prompt, "prompt should be refreshed");
}
#[test]
fn reconcile_refreshes_content_but_keeps_identity() {
let spec = &DEFAULT_ROUTINES[0];
let mut cur = materialize(spec, 100);
cur.schedule = "0 0 * * *".to_string();
let updated = reconcile(spec, &cur, 200).expect("schedule drift should be rewritten");
assert_eq!(updated.schedule, normalize_schedule(spec.schedule));
assert_eq!(updated.id, cur.id);
assert_eq!(updated.created_at, cur.created_at);
assert_eq!(updated.updated_at, 200);
}
#[test]
fn reconcile_keeps_enabled_default_enabled() {
let spec = &DEFAULT_ROUTINES[0];
let mut cur = materialize(spec, 100);
cur.prompt = "stale".to_string();
let updated = reconcile(spec, &cur, 200).expect("drift should be rewritten");
assert!(updated.enabled);
}
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
fn scratch_home() -> std::path::PathBuf {
std::env::temp_dir().join(format!("moadim-defaults-{}", uuid::Uuid::new_v4()))
}
fn with_redirected_home(body: impl FnOnce(&std::path::Path)) {
let home = scratch_home();
std::fs::create_dir_all(&home).unwrap();
let previous = std::env::var_os("HOME");
unsafe {
std::env::set_var("HOME", &home);
}
body(&home);
unsafe {
match previous {
Some(value) => std::env::set_var("HOME", value),
None => std::env::remove_var("HOME"),
}
}
let _ = std::fs::remove_dir_all(&home);
}
fn empty_store() -> RoutineStore {
Arc::new(Mutex::new(HashMap::new()))
}
#[test]
fn ensure_default_routines_seeds_empty_store() {
with_redirected_home(|_home| {
let store = empty_store();
ensure_default_routines(&store);
let seeded = store.lock().unwrap();
let spec = &DEFAULT_ROUTINES[0];
let slug = slugify(spec.title);
let routine = seeded
.values()
.find(|routine| slugify(&routine.title) == slug)
.expect("default routine must be seeded into the store");
assert_eq!(routine.title, spec.title);
assert_eq!(routine.source, "managed");
assert!(routine.enabled);
assert!(crate::paths::routine_dir(&slug).is_dir());
});
}
#[test]
fn ensure_default_routines_skips_up_to_date_existing() {
with_redirected_home(|_home| {
let spec = &DEFAULT_ROUTINES[0];
let existing = materialize(spec, now_secs());
let existing_id = existing.id.clone();
let store = empty_store();
store.lock().unwrap().insert(existing.id.clone(), existing);
ensure_default_routines(&store);
let after = store.lock().unwrap();
assert_eq!(after.len(), 1, "up-to-date default must not be duplicated");
assert!(
after.contains_key(&existing_id),
"the original entry must be preserved unchanged"
);
});
}
#[test]
fn ensure_default_routines_rewrites_drifted_existing() {
with_redirected_home(|_home| {
let spec = &DEFAULT_ROUTINES[0];
let mut existing = materialize(spec, now_secs());
let existing_id = existing.id.clone();
existing.prompt = "stale prompt".to_string();
existing.schedule = "0 0 * * *".to_string();
let store = empty_store();
store.lock().unwrap().insert(existing.id.clone(), existing);
ensure_default_routines(&store);
let after = store.lock().unwrap();
assert_eq!(after.len(), 1, "drifted default must not be duplicated");
let refreshed = after
.get(&existing_id)
.expect("drifted default keeps its id");
assert_eq!(
refreshed.prompt, spec.prompt,
"prompt must be refreshed from the spec"
);
assert_eq!(
refreshed.schedule,
normalize_schedule(spec.schedule),
"schedule must be refreshed from the spec"
);
});
}
#[test]
fn ensure_default_routines_logs_and_skips_on_write_failure() {
with_redirected_home(|_home| {
let spec = &DEFAULT_ROUTINES[0];
let slug = slugify(spec.title);
let routines = crate::paths::routines_dir();
std::fs::create_dir_all(&routines).unwrap();
std::fs::write(routines.join(&slug), "i am a file, not a dir").unwrap();
let store = empty_store();
ensure_default_routines(&store);
assert!(
store.lock().unwrap().is_empty(),
"a write failure must not insert the routine into the store"
);
assert!(routines.join(&slug).is_file());
});
}