use aion_package::{
CarryContract, InvariantContract, ReportContract, ToleranceContract, WorkloopContract,
};
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn the_compilers_retire_entry_is_the_entry_the_engine_probes_for() {
assert_eq!(
aion_awl::RETIRE_ENTRY,
aion::workloop::RETIRE_ENTRY,
"the compiler emits `{}` and the engine looks for `{}`; a workloop \
compiled by this toolchain could not be retired by this engine",
aion_awl::RETIRE_ENTRY,
aion::workloop::RETIRE_ENTRY
);
assert_eq!(
aion_awl::RETIRE_ARITY,
aion::workloop::RETIRE_ARITY,
"the retire entry's arity is load-bearing: the engine spawns it with \
exactly one term (the loop's carry), and its existence probe matches \
by name at ANY arity"
);
}
#[test]
fn a_compiled_workloop_declaration_converts_to_an_armable_spec() -> TestResult {
let spec = aion::workloop::spec_from_contract("tick_watch", &contract())?;
assert_eq!(
spec.arming().cadence_period(),
Some(std::time::Duration::from_secs(1_500)),
"the declared cadence must reach the arming the sweep fires on"
);
assert_eq!(spec.arming().signals(), ["poke"]);
assert_eq!(spec.retention(), std::time::Duration::from_secs(1_209_600));
let invariant = spec
.invariants()
.first()
.ok_or("the spec carries no invariant")?;
assert_eq!(invariant.name, "serving");
assert_eq!(invariant.record_type, "Board");
assert_eq!(
invariant.confirms,
vec![String::from("start")],
"the declared confirming route is what turns a taken route into a green \
health sample; an invariant with none alarms at every window forever"
);
assert_eq!(invariant.tolerance.consecutive_windows(), Some(3));
assert_eq!(
spec.carry().defaults().get("seen"),
Some(&serde_json::json!([])),
"the declared carry default is what generation 1 is seeded from"
);
Ok(())
}
#[test]
fn a_declaration_the_engine_cannot_act_on_is_refused_by_name() -> TestResult {
let mut unarmed = contract();
unarmed.cadence_seconds = None;
unarmed.arms.clear();
let refusal = aion::workloop::spec_from_contract("tick_watch", &unarmed)
.err()
.ok_or("a loop nothing wakes was accepted")?
.to_string();
assert!(
refusal.contains("tick_watch") && refusal.contains("neither a cadence nor a signal"),
"the refusal must name the declaration and what is missing: {refusal}"
);
let mut untolerated = contract();
untolerated.invariants[0].tolerances.clear();
let refusal = aion::workloop::spec_from_contract("tick_watch", &untolerated)
.err()
.ok_or("an invariant with no tolerance was accepted")?
.to_string();
assert!(
refusal.contains("serving") && refusal.contains("no tolerance"),
"the refusal must name the INVARIANT, not just the loop: {refusal}"
);
let mut unconfirmable = contract();
unconfirmable.invariants[0].confirms = None;
let refusal = aion::workloop::spec_from_contract("tick_watch", &unconfirmable)
.err()
.ok_or("an invariant nothing can confirm was accepted")?
.to_string();
assert!(
refusal.contains("serving") && refusal.contains("confirming route"),
"the refusal must name the invariant and the missing clause: {refusal}"
);
let mut unretained = contract();
unretained.retention_seconds = 0;
assert!(
aion::workloop::spec_from_contract("tick_watch", &unretained).is_err(),
"a zero retention window prunes everything and must be refused"
);
aion::workloop::spec_from_contract("tick_watch", &contract())?;
Ok(())
}
fn contract() -> WorkloopContract {
WorkloopContract {
cadence_seconds: Some(1_500),
arms: vec![String::from("poke")],
carries: vec![CarryContract {
name: String::from("seen"),
schema: serde_json::json!({"type":"array","items":{"type":"string"}}),
default: serde_json::json!([]),
}],
invariants: vec![InvariantContract {
name: String::from("serving"),
record_type: String::from("Board"),
schema: serde_json::json!({"type":"object"}),
tolerances: vec![ToleranceContract::Windows { count: 3 }],
confirms: Some(String::from("start")),
}],
retention_seconds: 1_209_600,
detached: Vec::new(),
reports: vec![ReportContract {
name: String::from("board"),
schema: serde_json::json!({"type":"object"}),
}],
has_retire_body: true,
}
}