#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
reason = "test code"
)]
use super::tests::{grepped, run, viewed, write_city_ledger};
use super::{Audience, Selection};
use kernel::{Address, B3Hash, EventDraft, EventKind, EventRecord, Payload, Seq, TimeMs};
use serde_json::{Value, json};
use std::path::Path;
fn span(since: Option<u64>, until: Option<u64>) -> runtime::clock::UtcSpan {
runtime::clock::UtcSpan::new(since.map(TimeMs::new), until.map(TimeMs::new)).unwrap()
}
#[test]
fn the_records_lens_keeps_the_lines_inside_a_time_window() {
let dir = tempfile::tempdir().unwrap();
let lines = write_city_ledger(dir.path());
let t = |v: &Value| v["t"].as_u64().unwrap();
let cases: Vec<(Selection, Vec<u8>)> = vec![
(
Selection {
span: span(Some(3), Some(7)),
..Selection::default()
},
grepped(&lines, |v, _| (3..7).contains(&t(v)), usize::MAX),
),
(
Selection {
span: span(Some(4), None),
kind: Some(EventKind::ToolCalled),
..Selection::default()
},
grepped(
&lines,
|v, _| t(v) >= 4 && v["kind"] == "tool_called",
usize::MAX,
),
),
(
Selection {
span: span(None, Some(5)),
tail: Some(2),
..Selection::default()
},
grepped(&lines, |v, _| t(v) < 5, 2),
),
];
for (chosen, expected) in cases {
assert_eq!(
String::from_utf8(viewed(dir.path(), &chosen, Audience::Agent)).unwrap(),
String::from_utf8(expected).unwrap(),
"{chosen:?}"
);
}
}
fn write_moments(dir: &Path, moments: &[u64]) -> Vec<Vec<u8>> {
let mut prev = kernel::GENESIS_PREV;
let mut blob = Vec::new();
let mut lines = Vec::new();
for (seq, at) in moments.iter().enumerate() {
let draft = EventDraft {
run: run(1),
t: TimeMs::new(*at),
who: "tester".to_owned(),
addr: Some(Address::parse("lab/a").unwrap()),
kind: EventKind::ToolCalled,
data: Payload::new(json!({"tool": "read"}).as_object().cloned().unwrap()).unwrap(),
ig: false,
};
let line = EventRecord::from_draft(draft, Seq::new(u64::try_from(seq).unwrap()), prev)
.canonical_line()
.unwrap();
prev = B3Hash::digest(&line);
blob.extend_from_slice(&line);
blob.push(b'\n');
lines.push(line);
}
std::fs::write(dir.join("ledger-00000000000000000000.jsonl"), &blob).unwrap();
lines
}
#[test]
fn a_line_whose_time_steps_back_is_judged_on_its_own() {
let dir = tempfile::tempdir().unwrap();
let lines = write_moments(dir.path(), &[10, 50, 20, 60, 30, 5, 15]);
let kept = |chosen: &Selection| {
String::from_utf8(viewed(dir.path(), chosen, Audience::Agent)).unwrap()
};
let joined = |seqs: &[usize]| {
seqs.iter()
.map(|seq| format!("{}\n", String::from_utf8(lines[*seq].clone()).unwrap()))
.collect::<String>()
};
let window = Selection {
span: span(Some(15), Some(40)),
..Selection::default()
};
assert_eq!(kept(&window), joined(&[2, 4, 6]));
let last = Selection {
tail: Some(1),
span: span(Some(15), Some(40)),
..Selection::default()
};
assert_eq!(kept(&last), joined(&[6]));
}
#[test]
fn a_moment_view_cannot_read_names_the_shape_it_wants() {
let read = |words: &[&str]| {
let words: Vec<String> = words.iter().map(|word| (*word).to_owned()).collect();
match super::super::grammar::parse(&words) {
Ok(super::super::grammar::Invocation::Run(_, arguments)) => Selection::read(&arguments),
other => panic!("not a view line: {other:?}"),
}
};
let unread = read(&["view", "city", "--since", "yesterday"]);
assert!(
matches!(&unread, Err(line) if line.starts_with("--since 'yesterday': ")
&& line.contains("2026-05-14T09:31:07Z")),
"{unread:?}"
);
let empty = read(&[
"view",
"city",
"--since",
"2026-05-14T10:00:00Z",
"--until",
"2026-05-14T09:00:00Z",
]);
assert!(
matches!(&empty, Err(line) if line.starts_with("--since and --until: ")),
"{empty:?}"
);
let both = read(&[
"view",
"city",
"--since",
"2026-05-14T09:00:00Z",
"--until",
"2026-05-14T10:00:00Z",
]);
assert!(
matches!(&both, Ok(chosen) if chosen.span == span(Some(1_778_749_200_000), Some(1_778_752_800_000))),
"{both:?}"
);
}