use super::*;
use crate::app::Snapshot;
use crate::binding::{Workspace, WorkspaceKind};
use crate::monitor::Verdict;
use tempfile::tempdir;
mod rig;
use rig::rig;
#[test]
fn unarmed_there_is_no_monitor_at_all() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 0, false);
assert!(!rig.ctx.pass(), "nothing to check");
assert_eq!(rig.calls(), 0, "no call, no row, no cost");
assert!(rig.checks().is_empty());
assert_eq!(
rig.ctx.period(),
crate::app::Cadence::default().full_sweep,
"and the tick is the clock's own slowest period"
);
}
#[test]
fn an_armed_workspace_with_no_policy_file_is_not_armed() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 0, true);
std::fs::write(rig.state_root.join(arming::PROMPT_FILE), " \n").expect("emptied");
assert!(!rig.ctx.pass(), "the policy is the mechanism");
assert_eq!(rig.calls(), 0);
}
#[test]
fn a_moved_tip_is_checked_once_and_then_never_again() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "diverged", 0, true);
assert!(rig.ctx.pass(), "the tip has never been checked");
assert_eq!(rig.calls(), 1);
let checks = rig.checks();
assert_eq!(checks.len(), 1);
assert_eq!(checks[0].verdict, Verdict::Diverged);
assert_eq!(checks[0].sha, "tip1");
assert_eq!(checks[0].workspace, crate::nav::ws_key(&rig.ws));
assert!(!rig.ctx.pass(), "an unmoved tip is not due");
assert_eq!(rig.calls(), 1, "one checkpoint, one call");
}
#[test]
fn a_failed_check_leaves_the_tip_unchecked_so_the_next_tick_re_fires() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 70, true);
assert!(rig.ctx.pass());
assert!(
rig.checks().is_empty(),
"a failure is not a verdict and names no sha"
);
let tail = opslog::tail(&rig.state_root, 10);
assert!(
tail.iter().any(
|e| e.argv.first().map(String::as_str) == Some(crate::opslog::YOG_STEP)
&& e.stderr.contains("a-1")
),
"but it is audited: {tail:?}"
);
assert!(
rig.ctx.pass(),
"so the next tick re-fires — that IS the retry"
);
assert_eq!(rig.calls(), 2);
}
#[test]
fn an_armed_key_with_nothing_behind_it_checks_nothing() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 0, true);
let text = arming::arm("", "/nowhere", "haiku").expect("armable");
std::fs::write(rig.state_root.join(CADENCE_YAML), text).expect("settings");
assert!(!rig.ctx.pass());
assert_eq!(rig.calls(), 0);
}
#[test]
fn an_armed_workspace_with_no_derived_tree_checks_nothing() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 0, true);
let mut snap = Snapshot::empty(0);
snap.workspaces = vec![Workspace {
path: rig.ws.clone(),
kind: WorkspaceKind::Named {
name: "otter".to_owned(),
},
}];
crate::state::publish_snapshot(&rig.ctx.cell, Arc::new(snap));
assert!(!rig.ctx.pass());
assert_eq!(rig.calls(), 0);
}
#[test]
fn the_thread_ticks_and_stops() {
let root = tempdir().expect("tempdir");
let rig = rig(root.path(), "tip1", "aligned", 0, true);
let calls = Arc::clone(&rig.calls);
let sentry = Sentry::spawn(rig.ctx);
let deadline = std::time::Instant::now() + Duration::from_secs(5);
while std::time::Instant::now() < deadline {
if *calls
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
> 0
{
break;
}
std::thread::yield_now();
}
drop(sentry);
assert_eq!(
*calls
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner),
1,
"one tick fired one check, and the drop joined the thread"
);
}