use super::Harness;
use crate::nav::ws_key;
use crate::ui_state::SeenKind;
use crate::watch::Mark;
use std::time::Duration;
fn build_named(h: &Harness, id: &str, name: &str) {
h.build_more(id, "fix the gate");
h.fx.name_agent(id, name);
}
#[test]
fn a_fired_start_focuses_the_conversation_it_started() {
let h = Harness::new();
let (clock, mut model) = h.model();
model.focus_workspace(&h.ws);
assert!(model.focus().agent.is_none(), "the placeholder's state");
model.await_conversation(&h.ws, "stench-pug", "fix the gate");
assert!(
model.focus().agent.is_none(),
"the driver has not written the branch yet — nothing to focus"
);
build_named(&h, "c-2", "stench-pug");
model.dirty_handle().mark_all([(h.ws.clone(), Mark::Watch)]);
model.tick(); clock.advance(Duration::from_millis(150));
assert!(model.tick(), "the roster now carries the started root");
assert_eq!(
model.focus().agent.as_deref(),
Some("c-2"),
"the start's own conversation is what renders — no ↓, no click"
);
let tip = model
.tree(&h.ws)
.and_then(|t| t.agents.iter().find(|a| a.agent_id == "c-2").cloned())
.map(|a| a.tip_oid)
.expect("the started root is in the tree");
assert!(
model.is_seen(SeenKind::Stopped, &ws_key(&h.ws), "c-2", &tip),
"arriving on it recorded its watermark"
);
}
#[test]
fn the_claim_is_spent_once_and_leaves_later_focus_alone() {
let h = Harness::new();
let (clock, mut model) = h.model();
build_named(&h, "c-2", "stench-pug");
model.dirty_handle().mark_all([(h.ws.clone(), Mark::Watch)]);
model.tick();
clock.advance(Duration::from_millis(150));
model.tick();
model.await_conversation(&h.ws, "stench-pug", "fix the gate");
model.refresh();
assert_eq!(model.focus().agent.as_deref(), Some("c-2"), "claimed");
model.focus_agent(&h.ws, "c-1");
model.refresh();
assert_eq!(
model.focus().agent.as_deref(),
Some("c-1"),
"the claim was spent on its arrival, not held"
);
}
#[test]
fn a_claim_whose_root_never_appears_moves_nothing() {
let h = Harness::new();
let (_c, mut model) = h.model();
model.focus_agent(&h.ws, "c-1");
model.await_conversation(&h.ws, "never-written", "fix the gate");
model.refresh();
model.await_conversation(
std::path::Path::new("/no/such/ws"),
"stench-pug",
"fix the gate",
);
model.refresh();
assert_eq!(
model.focus().agent.as_deref(),
Some("c-1"),
"an unresolvable claim is inert, never a focus reset"
);
}
#[test]
fn a_message_echoes_without_claiming_the_focus() {
let h = Harness::new();
let (clock, mut model) = h.model();
model.focus_agent(&h.ws, "c-1");
let landed = model
.tree(&h.ws)
.and_then(|t| t.agents.first().map(|a| a.messages))
.expect("the fixture's conversation");
model.await_message(&h.ws, "c-1", "and again");
model.refresh();
let echoed = model
.tree(&h.ws)
.and_then(|t| t.agents.first().cloned())
.expect("the folded row");
assert_eq!(
echoed.pending.len(),
1,
"the send is on the row as the undelivered deposit it is (§5.1 #11)"
);
assert!(
echoed.pending[0].in_memory(),
"and it is yog's word, not disk's"
);
assert_eq!(echoed.pending[0].deposit.body, "and again");
model.focus_workspace(&h.ws);
model.refresh();
assert!(
model.focus().agent.is_none(),
"a follow-up's echo carries no focus claim to spend"
);
let messages = h.ws.join("agents/c-1/messages");
std::fs::create_dir_all(&messages).unwrap();
std::fs::write(
messages.join(format!("{:03}-user.md", landed + 1)),
"and again",
)
.unwrap();
model.dirty_handle().mark_all([(h.ws.clone(), Mark::Watch)]);
model.tick();
clock.advance(Duration::from_millis(150));
assert!(model.tick(), "the derivation carries the message now");
assert!(
model
.tree(&h.ws)
.and_then(|t| t.agents.first())
.is_some_and(|a| a.pending.is_empty()),
"the echo gave its seat up rather than doubling the message"
);
}
#[test]
fn an_idle_frame_refolds_nothing_and_hands_back_the_same_pointer() {
use std::sync::Arc;
let h = Harness::new();
let (_c, mut model) = h.model();
model.refresh();
let quiet = Arc::clone(&model.snap);
model.refresh();
assert!(
Arc::ptr_eq(&quiet, &model.snap),
"nothing pending, nothing published: the same snapshot, not a copy of it"
);
model.await_message(&h.ws, "c-1", "and again");
model.refresh();
let folded = Arc::clone(&model.snap);
assert!(!Arc::ptr_eq(&quiet, &folded), "a new echo is a new fold");
model.refresh();
assert!(
Arc::ptr_eq(&folded, &model.snap),
"and holding the same echo over the same derivation folds nothing again"
);
}