#[path = "agent_fake/lab.rs"]
mod lab;
use lab::{Event, Lab};
fn present(lab: &mut Lab) -> anyhow::Result<()> {
lab.watch("presence")?;
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("presence") && value["present"] == true))?;
Ok(())
}
#[test]
fn offers_two_accepts_one_and_streams_only_permitted_text() -> anyhow::Result<()> {
let mut lab = Lab::new("stream-one", &["--version", "1"])?;
present(&mut lab)?;
lab.watch("stream")?;
lab.send("inbox", "synthetic input")?;
let event =
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "prompt"))?;
if let Event::Control(value) = event {
assert_eq!(value["text"], "synthetic input");
}
let event = lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("stream") && value["type"] == "tool_call"))?;
if let Event::Network(_, value) = event {
assert_eq!(value["title"], "Synthetic review");
let encoded = serde_json::to_string(&value)?;
for forbidden in ["rawInput", "rawOutput", "private", "file contents"] {
assert!(!encoded.contains(forbidden));
}
}
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
#[test]
fn v2_prompt_ack_does_not_release_mail_until_idle() -> anyhow::Result<()> {
let mut lab = Lab::new("stream-two", &["--version", "2", "--hold"])?;
present(&mut lab)?;
lab.watch("stream")?;
lab.send("inbox", "first")?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "prompt" && value["turn"] == 1))?;
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("stream") && value["type"] == "prompt_accepted"))?;
lab.send("inbox", "second")?;
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("stream") && value["type"] == "prompt_queued"))?;
lab.command("finish")?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "turn_ended" && value["turn"] == 1))?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "prompt" && value["turn"] == 2))?;
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
#[test]
fn version_three_is_refused_before_a_session_and_revoked() -> anyhow::Result<()> {
let lab = Lab::new("version-three", &["--version", "3"])?;
assert_eq!(lab.finish(1)?, 1);
Ok(())
}
#[test]
fn thirty_third_unread_prompt_is_refused_and_cancel_reaches_the_peer() -> anyhow::Result<()> {
let mut lab = Lab::new("queue", &["--hold"])?;
present(&mut lab)?;
lab.watch("stream")?;
lab.send("inbox", "active")?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "prompt"))?;
for n in 0..33 {
lab.send("inbox", &format!("mail {n}"))?;
}
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("stream") && value["type"] == "host_refusal" && value["reason"] == "prompt_queue 32 reached: 32 unread prompts"))?;
lab.send("cancel", "cancel the active turn")?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "cancel"))?;
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
fn permission(scenario: &str, count: usize, decision: &str, selected: &str) -> anyhow::Result<()> {
let count_text = count.to_string();
let mut lab = Lab::new(scenario, &["--burst", &count_text])?;
present(&mut lab)?;
lab.watch("state")?;
lab.send("inbox", "synthetic permission turn")?;
let waiting = lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("state") && value["state"] == "waiting" && value["count"] == count))?;
if let Event::Network(_, value) = waiting {
assert!(
value["words"]
.as_str()
.is_some_and(|words| words.contains("Synthetic review"))
);
}
lab.decide_and_pull(decision, count)?;
let answered = lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "permission_answer" && value["count"] == count))?;
if let Event::Control(value) = answered {
assert_eq!(value["outcome"]["optionId"], selected);
}
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
#[test]
fn an_approved_draft_selects_the_narrow_option() -> anyhow::Result<()> {
permission("approve", 1, "approved", "yes")
}
#[test]
fn a_refused_draft_selects_the_narrow_refusal() -> anyhow::Result<()> {
permission("refuse", 1, "refused", "no")
}
#[test]
fn seven_hundred_twenty_nine_drafts_are_answered_without_a_pipe_stall() -> anyhow::Result<()> {
permission("burst", 729, "approved", "yes")
}
fn exits(scenario: &str, args: &[&str], state: &str) -> anyhow::Result<()> {
let mut lab = Lab::new(scenario, args)?;
present(&mut lab)?;
lab.watch("presence")?;
lab.watch("state")?;
lab.expect_exit(3);
lab.send("inbox", "exit fixture")?;
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("presence") && value["present"] == false && value["exit_status"] == 3))?;
lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("state") && value["session_state"] == state))?;
assert_eq!(lab.finish(3)?, 1);
Ok(())
}
#[test]
fn exit_three_is_gone_and_resumable_only_when_advertised() -> anyhow::Result<()> {
exits("exit-load", &["--exit", "3"], "dormant")
}
#[test]
fn exit_three_without_load_session_is_ended() -> anyhow::Result<()> {
exits("exit-no-load", &["--exit", "3", "--no-load"], "ended")
}
#[test]
fn sigterm_revokes_once_and_closes_presence() -> anyhow::Result<()> {
let mut lab = Lab::new("sigterm", &[])?;
present(&mut lab)?;
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
fn streamed(lab: &mut Lab, kind: &str) -> anyhow::Result<serde_json::Value> {
match lab.until(|event| matches!(event, Event::Network(subject, value) if subject.rsplit('.').next() == Some("stream") && value["type"] == kind))? {
Event::Network(_, value) => Ok(value),
_ => anyhow::bail!("no {kind} on the stream"),
}
}
#[test]
fn a_config_change_reaches_the_harness_and_a_refused_one_is_said() -> anyhow::Result<()> {
let mut lab = Lab::new("config", &["--version", "1"])?;
present(&mut lab)?;
lab.watch("stream")?;
lab.send_value(
"inbox",
&serde_json::json!({"config":{"option":"model","value":"fake-large"}}),
)?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "config_set" && value["value"] == "fake-large"))?;
let options = streamed(&mut lab, "config_options")?;
assert_eq!(
options["options"][0]["currentValue"], "fake-large",
"{options}"
);
lab.send_value(
"inbox",
&serde_json::json!({"config":{"option":"model","value":"fake-none"}}),
)?;
let failed = streamed(&mut lab, "config_failed")?;
assert_eq!(failed["code"], -32602, "{failed}");
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}
#[test]
fn the_harness_session_list_is_streamed_newest_first() -> anyhow::Result<()> {
let mut lab = Lab::new("sessions", &["--version", "1"])?;
present(&mut lab)?;
lab.watch("stream")?;
lab.send_value("inbox", &serde_json::json!({"session":{"list":true}}))?;
lab.until(|event| matches!(event, Event::Control(value) if value["event"] == "session_list"))?;
let listed = streamed(&mut lab, "sessions")?;
assert_eq!(listed["current"], "fake-session", "{listed}");
assert_eq!(listed["sessions"][0]["id"], "fake-session", "{listed}");
assert_eq!(listed["sessions"][1]["id"], "fake-earlier", "{listed}");
lab.terminate()?;
assert_eq!(lab.finish(143)?, 1);
Ok(())
}