use amberfork_align::{AlignParams, DiffParams, ForkParams, LexicalCost, align, diff, find_fork};
use amberfork_model::{MoveKind, Payload, Step, StepKind, test_support};
use proptest::prelude::*;
fn arb_step(idx: usize) -> impl Strategy<Value = Step> {
let kind = prop_oneof![
Just(StepKind::Llm),
Just(StepKind::Tool),
Just(StepKind::Agent),
Just(StepKind::Other),
];
let name = prop_oneof![
Just("planner".to_string()),
Just("web.search".to_string()),
Just("web.fetch".to_string()),
Just("reader".to_string()),
"[a-z]{1,8}",
];
let outputs = prop_oneof![
1 => Just(None),
4 => any::<String>().prop_map(|s| Some(Payload::Text(s))),
];
(kind, name, outputs).prop_map(move |(kind, name, outputs)| {
test_support::step(idx, name)
.kind(kind)
.outputs(outputs)
.build()
})
}
fn arb_run() -> impl Strategy<Value = Vec<Step>> {
prop::collection::vec(any::<u8>(), 0..25).prop_flat_map(|shape| {
shape
.into_iter()
.enumerate()
.map(|(i, _)| arb_step(i))
.collect::<Vec<_>>()
})
}
proptest! {
#[test]
fn self_alignment_is_all_sync_with_no_fork(run in arb_run()) {
let moves =
align(&run, &run, &LexicalCost, &AlignParams::default()).expect("under the size guard");
prop_assert_eq!(moves.len(), run.len());
for (i, m) in moves.iter().enumerate() {
prop_assert_eq!(m.kind, MoveKind::Sync);
prop_assert_eq!((m.a_idx, m.b_idx), (Some(i), Some(i)));
prop_assert_eq!(m.cost, 0.0);
}
prop_assert_eq!(find_fork(&moves, &ForkParams::default()), None);
let strictest = ForkParams { tau: 0.0, ..ForkParams::default() };
prop_assert_eq!(find_fork(&moves, &strictest), None);
let this = test_support::run("self", run).build();
let result =
diff(&this, &this, &LexicalCost, &DiffParams::default()).expect("under the size guard");
prop_assert_eq!(result.fork, None);
prop_assert!(result.attribution.is_none());
}
}