Files
boxmaker/crates/memoryd/tests/honcho_naming.rs
T

157 lines
5.2 KiB
Rust

//! Which Honcho session a turn goes to (M5c spec, section 3): the Boxmaker session's id, then
//! `<id>-2`, `<id>-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<HonchoSession> {
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();
// Every ledger begins with when sync began (M5c-1 task 15).
ledger
.append(Record::Began {
time: Timestamp::parse("2026-09-28T09:00:00.000Z").unwrap(),
})
.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<Option<HonchoSession>> = {
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<Option<HonchoSession>> = (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());
}