focus/crates/focus-harness/tests/session_tests.rs

125 lines
3.4 KiB
Rust

//! 会话树与 JSONL 持久化的单元测试。
//! Unit tests for session trees and JSONL persistence.
use focus_core::model::Message;
use focus_harness::session::{SessionEntry, SessionStore, SessionTree};
use std::path::PathBuf;
fn temp_dir(tag: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!(
"focus-harness-session-{}-{}",
tag,
std::process::id()
));
let _ = std::fs::remove_dir_all(&d);
std::fs::create_dir_all(&d).unwrap();
d
}
#[test]
fn appends_and_loads_entries() {
let dir = temp_dir("append");
let store = SessionStore::new(&dir);
store
.append(
"s1",
&SessionEntry::new("e1".into(), None, Message::user_text("hi")),
)
.unwrap();
store
.append(
"s1",
&SessionEntry::new("e2".into(), Some("e1".into()), Message::user_text("there")),
)
.unwrap();
let entries = store.load("s1").unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].id, "e1");
assert_eq!(entries[0].parent_id, None);
assert_eq!(entries[1].id, "e2");
assert_eq!(entries[1].parent_id.as_deref(), Some("e1"));
assert!(matches!(entries[0].message, Message::User(_)));
}
#[test]
fn next_entry_id_is_incremental() {
let dir = temp_dir("seq");
let store = SessionStore::new(&dir);
assert_eq!(store.next_entry_id("s1").unwrap(), "e1");
store
.append(
"s1",
&SessionEntry::new("e1".into(), None, Message::user_text("a")),
)
.unwrap();
assert_eq!(store.next_entry_id("s1").unwrap(), "e2");
store
.append(
"s1",
&SessionEntry::new("e3".into(), Some("e1".into()), Message::user_text("b")),
)
.unwrap();
// e3 已存在 → 下一个是 e4。
// e3 exists → the next is e4.
assert_eq!(store.next_entry_id("s1").unwrap(), "e4");
}
#[test]
fn lists_sessions() {
let dir = temp_dir("list");
let store = SessionStore::new(&dir);
store
.append(
"alpha",
&SessionEntry::new("e1".into(), None, Message::user_text("a")),
)
.unwrap();
store
.append(
"beta",
&SessionEntry::new("e1".into(), None, Message::user_text("b")),
)
.unwrap();
let ids = store.list_sessions().unwrap();
assert_eq!(ids, vec!["alpha".to_string(), "beta".to_string()]);
}
fn tree() -> SessionTree {
let entries = vec![
SessionEntry::new("e1".into(), None, Message::user_text("root")),
SessionEntry::new(
"e2".into(),
Some("e1".into()),
Message::user_text("child A"),
),
SessionEntry::new(
"e3".into(),
Some("e1".into()),
Message::user_text("child B"),
),
SessionEntry::new(
"e4".into(),
Some("e3".into()),
Message::user_text("grandchild"),
),
];
SessionTree::build(entries)
}
#[test]
fn tree_queries() {
let t = tree();
assert_eq!(t.roots().len(), 1);
assert_eq!(t.roots()[0].id, "e1");
let children = t.children_of("e1");
assert_eq!(children.len(), 2);
let path = t.branch("e4");
let ids: Vec<&str> = path.iter().map(|e| e.id.as_str()).collect();
assert_eq!(ids, vec!["e1", "e3", "e4"]);
let leaves = t.leaf_ids();
assert_eq!(leaves.len(), 2);
assert!(leaves.contains(&"e2"));
assert!(leaves.contains(&"e4"));
}