use super::pipeline_tests::{client_lines, sync_run};
use super::runner::Scenario;
use super::scenarios;
fn k2_ep(fused: bool) -> Scenario {
let mut sc = scenarios::verify_k(if fused { "k2_fused" } else { "k2_unfused" }, 1);
sc.cfg.ep = true;
sc.cfg.k2_fused_wire = fused;
sc
}
fn verify_ticks(lines: &[String]) -> Vec<Vec<String>> {
let mut ticks: Vec<Vec<String>> = vec![Vec::new()];
for l in lines {
if l.starts_with("-- tick") {
ticks.push(Vec::new());
} else {
ticks.last_mut().unwrap().push(l.clone());
}
}
ticks
.into_iter()
.filter(|t| t.iter().any(|l| l.starts_with("decode_verify_graphed(")))
.collect()
}
fn field<'a>(line: &'a str, key: &str) -> &'a str {
let rest = line.split(&format!("{key}=")).nth(1).expect(key);
rest.split([',', ')']).next().unwrap()
}
fn tokens(line: &str) -> &str {
let rest = line.split("tokens=[").nth(1).expect("tokens");
rest.split(']').next().unwrap()
}
fn ep_lines(tick: &[String]) -> Vec<&String> {
tick.iter().filter(|l| l.starts_with("ep_")).collect()
}
#[test]
fn the_fused_wire_leaves_every_stream_unchanged() {
let on = sync_run(&k2_ep(true));
let off = sync_run(&k2_ep(false));
assert_eq!(client_lines(&on), client_lines(&off));
assert!(!client_lines(&on).is_empty());
}
#[test]
fn a_fused_k2_step_sends_one_announcement_and_one_verdict() {
let ticks = verify_ticks(&sync_run(&k2_ep(true)));
assert!(ticks.len() >= 4, "too few K=2 steps: {}", ticks.len());
for t in &ticks {
let a = t
.iter()
.position(|l| l.starts_with("ep_announce_k2_fused("))
.unwrap();
let v = t
.iter()
.position(|l| l.starts_with("ep_send_k2_verdict("))
.unwrap();
let ep = ep_lines(&t[a..=v]);
assert_eq!(ep.len(), 2, "tick sends {ep:?}");
assert!(
!t.iter()
.any(|l| l.contains("cmd=0xfffffff2") || l.contains("cmd=0xfffffff5")),
"an unfused K=2 or propose command in {t:?}"
);
assert!(ep[0].starts_with("ep_announce_k2_fused("));
assert!(ep[1].starts_with("ep_send_k2_verdict("));
let v = t
.iter()
.position(|l| l.starts_with("decode_verify_graphed("))
.unwrap();
assert!(t[v - 1].starts_with("ep_announce_k2_fused("), "{t:?}");
assert_eq!(
tokens(&t[v - 1]),
tokens(&t[v]),
"announced tokens differ from the verified ones"
);
}
for t in verify_ticks(&sync_run(&k2_ep(false))) {
assert!(ep_lines(&t).len() >= 4, "unfused control: {t:?}");
assert!(!t.iter().any(|l| l.starts_with("ep_announce_k2_fused(")));
}
}
#[test]
fn the_verdict_precedes_the_commit_on_accept_and_reject() {
let ticks = verify_ticks(&sync_run(&k2_ep(true)));
let mut rejects = 0;
for t in &ticks {
let v = t
.iter()
.position(|l| l.starts_with("ep_send_k2_verdict("))
.unwrap();
if t[v].contains("accepted=false") {
rejects += 1;
}
let c = t
.iter()
.position(|l| l.starts_with("commit_accepted_prefix("));
if let Some(c) = c {
assert!(v < c, "commit before the verdict: {t:?}");
} else {
assert_eq!(
field(&t[v], "drafts"),
"0",
"a proposing step with no commit: {t:?}"
);
}
}
assert!(rejects > 0, "the scenario rejected no draft");
}
#[test]
fn the_verdict_is_the_propose_rank_0_runs() {
let ticks = verify_ticks(&sync_run(&k2_ep(true)));
let mut finished = 0;
for t in &ticks {
let vi = t
.iter()
.position(|l| l.starts_with("ep_send_k2_verdict("))
.unwrap();
let verdict = &t[vi];
let after = &t[vi + 1..];
let proposes: Vec<&String> = after
.iter()
.filter(|l| l.starts_with("run_mtp_propose_announced("))
.collect();
for (i, l) in after.iter().enumerate() {
if l.starts_with("run_mtp_propose_multi(") {
assert!(
i > 0 && after[i - 1].starts_with("run_mtp_propose_announced("),
"{t:?}"
);
}
}
if field(verdict, "drafts") == "0" {
finished += 1;
assert!(
proposes.is_empty(),
"proposed after a finishing verdict: {t:?}"
);
} else {
assert_eq!(proposes.len(), 1, "{t:?}");
assert_eq!(field(proposes[0], "token"), field(verdict, "next"));
assert_eq!(field(proposes[0], "pos"), field(verdict, "pos"));
let accepted = field(verdict, "accepted") == "true";
let saved = format!("save_hidden_for_mtp({}, ", usize::from(accepted));
assert!(after.iter().any(|l| l.starts_with(&saved)), "{t:?}");
}
}
assert_eq!(
finished, 1,
"the EOS step should be the one verdict without drafts"
);
assert!(
ticks.iter().any(|t| t
.iter()
.any(|l| l.contains("ep_send_k2_verdict(accepted=false"))),
"the scenario rejected no draft"
);
}