//! Which Honcho session a turn goes to (M5c spec, section 3): the Boxmaker session's id, then //! `-2`, `-3` for each new speaker, holding every speaker so far; numbered from the //! ledger's decisions alone, so a restart numbers the same. Do not edit. #[path = "support/tmp.rs"] mod tmp; use bxcore::{SessionId, Timestamp}; use memoryd::honcho::ledger::{Action, Item, Ledger, Record, Workspace}; use memoryd::honcho::naming::{HonchoSession, honcho_session, session_name}; use tmp::TempDir; fn sid(s: &str) -> SessionId { SessionId::new(s).unwrap() } fn turn(session: &str, turn: u64) -> Item { Item::Turn { session: sid(session), turn, } } fn spoke(workspace: Workspace, speaker: &str) -> Action { Action::Turn { workspace, speaker: speaker.to_string(), } } fn skip() -> Action { Action::Skip { why: "the turn has no single speaker".to_string(), } } fn hs(name: &str, speakers: &[&str]) -> Option { Some(HonchoSession { name: name.to_string(), speakers: speakers.iter().map(|s| s.to_string()).collect(), }) } /// A ledger with these decisions, in this order. fn ledger(dir: &TempDir, decisions: &[(Item, Action)]) -> Ledger { let path = dir.path().join("honcho.jsonl"); let mut ledger = Ledger::open(&path).unwrap(); for (item, action) in decisions { ledger .append(Record::Decided { time: Timestamp::parse("2026-09-28T10:00:00.000Z").unwrap(), item: item.clone(), action: action.clone(), }) .unwrap(); } ledger } #[test] fn session_names() { assert_eq!(session_name(&sid("chat-1"), 0), "chat-1"); assert_eq!(session_name(&sid("chat-1"), 1), "chat-1"); assert_eq!(session_name(&sid("chat-1"), 2), "chat-1-2"); assert_eq!(session_name(&sid("chat-1"), 12), "chat-1-12"); } #[test] fn each_new_speaker_starts_the_next_session() { let dir = TempDir::new("naming-order"); let decisions = [ (turn("chat-1", 1), spoke(Workspace::Trusted, "kyle")), (turn("chat-2", 1), spoke(Workspace::Trusted, "zed")), (turn("chat-1", 2), spoke(Workspace::Trusted, "kyle")), (turn("chat-1", 3), spoke(Workspace::Untrusted, "ana_b")), (turn("chat-1", 4), skip()), (turn("chat-1", 5), spoke(Workspace::Trusted, "kyle")), (turn("chat-1", 6), spoke(Workspace::Untrusted, "bob")), (turn("chat-1", 7), spoke(Workspace::Trusted, "ana_b")), ]; let l = ledger(&dir, &decisions); let at = |t: u64| honcho_session(l.decisions(), &sid("chat-1"), t); assert_eq!(at(1), hs("chat-1", &["kyle"])); assert_eq!(at(2), hs("chat-1", &["kyle"])); // A new speaker in any workspace counts. assert_eq!(at(3), hs("chat-1-2", &["kyle", "ana_b"])); assert_eq!(at(4), None, "a skipped turn goes nowhere"); assert_eq!(at(5), hs("chat-1-2", &["kyle", "ana_b"])); assert_eq!(at(6), hs("chat-1-3", &["kyle", "ana_b", "bob"])); assert_eq!(at(7), hs("chat-1-3", &["kyle", "ana_b", "bob"])); assert_eq!(at(8), None, "an undecided turn"); // Another session's speakers do not count. assert_eq!( honcho_session(l.decisions(), &sid("chat-2"), 1), hs("chat-2", &["zed"]) ); } #[test] fn the_order_is_the_order_of_decision() { let dir = TempDir::new("naming-decided"); // Turn 2 was decided first (turn 1 waited for brokerd): its speaker is the first. let decisions = [ (turn("chat-1", 2), spoke(Workspace::Trusted, "ana_b")), (turn("chat-1", 1), spoke(Workspace::Trusted, "kyle")), ]; let l = ledger(&dir, &decisions); assert_eq!( honcho_session(l.decisions(), &sid("chat-1"), 2), hs("chat-1", &["ana_b"]) ); assert_eq!( honcho_session(l.decisions(), &sid("chat-1"), 1), hs("chat-1-2", &["ana_b", "kyle"]) ); } #[test] fn a_restart_numbers_the_same() { let dir = TempDir::new("naming-restart"); let decisions = [ (turn("chat-1", 1), spoke(Workspace::Trusted, "kyle")), (turn("chat-1", 2), skip()), (turn("chat-1", 3), spoke(Workspace::Trusted, "ana_b")), ]; let before: Vec> = { let l = ledger(&dir, &decisions); (1..=3) .map(|t| honcho_session(l.decisions(), &sid("chat-1"), t)) .collect() }; let l = Ledger::open(&dir.path().join("honcho.jsonl")).unwrap(); let after: Vec> = (1..=3) .map(|t| honcho_session(l.decisions(), &sid("chat-1"), t)) .collect(); assert_eq!(before, after); assert_eq!(after[2], hs("chat-1-2", &["kyle", "ana_b"])); } #[test] fn decisions_are_every_decided_item_in_order() { let dir = TempDir::new("naming-decisions"); let decisions = [ (turn("chat-1", 2), skip()), (turn("chat-1", 1), spoke(Workspace::Trusted, "kyle")), ]; let l = ledger(&dir, &decisions); let got: Vec<(Item, Action)> = l.decisions().map(|(i, a)| (i.clone(), a.clone())).collect(); assert_eq!(got, decisions.to_vec()); }