loopd: write the next epoch from a compaction summary
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
//! Tests for writing the next epoch. Do not edit.
|
||||
|
||||
mod support;
|
||||
|
||||
use bxcore::{Epoch, LogRecord, SessionId};
|
||||
use loopd::baseline::Baseline;
|
||||
use loopd::compact;
|
||||
use loopd::memory::{MemorySource, NoMemory};
|
||||
use loopd::session::Session;
|
||||
use loopd::tools::Registry;
|
||||
use support::Home;
|
||||
|
||||
struct Block(&'static str);
|
||||
|
||||
impl MemorySource for Block {
|
||||
fn block(&self) -> Result<String, String> {
|
||||
Ok(self.0.to_string())
|
||||
}
|
||||
fn updated(&self, _session: &SessionId, _turn: u64) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn id(s: &str) -> SessionId {
|
||||
SessionId::new(s).unwrap()
|
||||
}
|
||||
|
||||
fn session(home: &Home) -> (loopd::config::Config, Session) {
|
||||
let cfg = home.config(std::path::Path::new("/tmp/unused.sock"));
|
||||
let baseline = Baseline::assemble(&cfg, &Registry::m2b()).unwrap();
|
||||
let s = Session::create(&home.dir, id("a"), baseline, cfg.slots.main).unwrap();
|
||||
(cfg, s)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_writes_epoch_end_a_new_baseline_and_a_new_log() {
|
||||
let home = Home::new();
|
||||
let (cfg, mut s) = session(&home);
|
||||
s.append(LogRecord::User {
|
||||
time: bxcore::Timestamp::parse("2026-09-18T08:00:00.000Z").unwrap(),
|
||||
content: "hi".to_string(),
|
||||
speaker: None,
|
||||
})
|
||||
.unwrap();
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&NoMemory,
|
||||
"the sum".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(s.epoch(), Epoch(1));
|
||||
assert_eq!(s.carry(), Some("the sum"));
|
||||
assert_eq!(s.slot(), cfg.slots.main);
|
||||
assert_eq!(s.turns(), 1, "the old user still counts");
|
||||
assert_eq!(
|
||||
s.records()
|
||||
.iter()
|
||||
.filter(|r| matches!(r, LogRecord::User { .. }))
|
||||
.count(),
|
||||
0
|
||||
);
|
||||
|
||||
let old = home.records("a");
|
||||
assert!(
|
||||
matches!(old.last(), Some(LogRecord::EpochEnd { next, summary, .. }) if *next == Epoch(1) && summary == "the sum")
|
||||
);
|
||||
assert!(home.dir.join("sessions/a/1.baseline.json").is_file());
|
||||
let new_log = home.read("sessions/a/1.jsonl");
|
||||
assert!(new_log.contains("\"epoch\":1"), "{new_log}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_new_baseline_takes_a_fresh_memory_block() {
|
||||
let home = Home::new();
|
||||
let (cfg, mut s) = session(&home);
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&Block("I take my tea strong."),
|
||||
"sum".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
s.baseline().system.contains("I take my tea strong."),
|
||||
"{}",
|
||||
s.baseline().system
|
||||
);
|
||||
let on_disk = home.read("sessions/a/1.baseline.json");
|
||||
assert!(on_disk.contains("I take my tea strong."), "{on_disk}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reopen_loads_the_new_epoch() {
|
||||
let home = Home::new();
|
||||
let (cfg, mut s) = session(&home);
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&NoMemory,
|
||||
"carry".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
drop(s);
|
||||
let s = Session::open(&home.dir, id("a")).unwrap();
|
||||
assert_eq!(s.epoch(), Epoch(1));
|
||||
assert_eq!(s.carry(), Some("carry"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_second_commit_does_not_write_another_epoch_end_on_the_old_log() {
|
||||
let home = Home::new();
|
||||
let (cfg, mut s) = session(&home);
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&NoMemory,
|
||||
"first".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let ends = home
|
||||
.records("a")
|
||||
.iter()
|
||||
.filter(|r| matches!(r, LogRecord::EpochEnd { .. }))
|
||||
.count();
|
||||
assert_eq!(ends, 1);
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&NoMemory,
|
||||
"second".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(s.epoch(), Epoch(2));
|
||||
let ends = home
|
||||
.records("a")
|
||||
.iter()
|
||||
.filter(|r| matches!(r, LogRecord::EpochEnd { .. }))
|
||||
.count();
|
||||
assert_eq!(ends, 1, "epoch 0 still has one EpochEnd");
|
||||
let mid = home.read("sessions/a/1.jsonl");
|
||||
assert!(mid.contains("\"type\":\"epoch_end\""), "{mid}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_after_epoch_end_without_next_files_does_not_write_a_second_end() {
|
||||
let home = Home::new();
|
||||
let (cfg, mut s) = session(&home);
|
||||
s.append(LogRecord::EpochEnd {
|
||||
time: bxcore::Timestamp::now(),
|
||||
next: Epoch(1),
|
||||
summary: "already".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
drop(s);
|
||||
let mut s = Session::open(&home.dir, id("a")).unwrap();
|
||||
assert_eq!(s.epoch(), Epoch(0));
|
||||
compact::commit(
|
||||
&mut s,
|
||||
&cfg,
|
||||
&Registry::m2b(),
|
||||
&NoMemory,
|
||||
"ignored".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let ends = home
|
||||
.records("a")
|
||||
.iter()
|
||||
.filter(|r| matches!(r, LogRecord::EpochEnd { .. }))
|
||||
.count();
|
||||
assert_eq!(ends, 1);
|
||||
assert_eq!(s.epoch(), Epoch(1));
|
||||
assert_eq!(
|
||||
s.carry(),
|
||||
Some("already"),
|
||||
"the summary already in EpochEnd is the carry"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_new_session_start_keeps_the_live_slot() {
|
||||
let home = Home::new();
|
||||
let cfg = home.config(std::path::Path::new("/tmp/unused.sock"));
|
||||
let baseline = Baseline::assemble(&cfg, &Registry::m2b()).unwrap();
|
||||
let mut s = Session::create(&home.dir, id("a"), baseline, 7).unwrap();
|
||||
compact::commit(&mut s, &cfg, &Registry::m2b(), &NoMemory, "s".to_string()).unwrap();
|
||||
assert_eq!(s.slot(), 7);
|
||||
match &s.records()[0] {
|
||||
LogRecord::SessionStart { slot, epoch, .. } => {
|
||||
assert_eq!(*slot, 7);
|
||||
assert_eq!(*epoch, Epoch(1));
|
||||
}
|
||||
other => panic!("{other:?}"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user