#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
reason = "test code"
)]
use kernel::{ContentBlock, Role};
use crate::prefix::BreakpointPlan;
use super::*;
fn message(role: Role, text: &str) -> ChatMessage {
ChatMessage {
role,
content: vec![ContentBlock::Text {
text: text.to_owned(),
}],
}
}
fn tool_results(text: &str) -> ChatMessage {
ChatMessage {
role: Role::User,
content: vec![ContentBlock::ToolResult {
tool_use_id: "tu_1".to_owned(),
content: text.to_owned(),
is_error: false,
attachments: Vec::new(),
}],
}
}
fn prefix() -> FrozenPrefix {
FrozenPrefix::assemble(
super::super::FrozenSegment::new(SegmentSlot::City, b"city".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Building, b"building".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Resident, b"resident".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Run, b"run".to_vec()),
)
.unwrap()
}
fn shape(messages: &[ChatMessage]) -> PromptShape {
PromptShape::of(&prefix(), &[], messages).unwrap()
}
fn tool() -> ToolDef {
ToolDef {
name: kernel::ToolName::parse("exec").unwrap(),
description: "run a command".to_owned(),
input_schema: kernel::Payload::empty(),
}
}
fn records(
messages: &[ChatMessage],
tools: &[ToolDef],
baseline: Option<&PromptShape>,
) -> (serde_json::Value, serde_json::Value) {
let prefix = prefix();
let assembled = serde_json::to_value(
prefix
.prompt_payload(&crate::prefix::BreakpointPlan::for_conversation(&[]))
.unwrap(),
)
.unwrap();
let shape = PromptShape::of(&prefix, tools, messages).unwrap();
let compared =
serde_json::to_value(shape.recorded(shape.attribute(baseline)).unwrap()).unwrap();
(assembled, compared)
}
#[test]
fn a_missing_baseline_is_a_first_request_rather_than_nothing_moved() {
let shape = shape(&[message(Role::User, "Task: dig")]);
assert_eq!(shape.attribute(None), ShapeChanged::FirstRequest);
}
#[test]
fn a_moved_region_is_named_system_tools_or_run() {
let first = shape(&[message(Role::User, "Task: dig")]);
let grown = shape(&[
message(Role::User, "Task: dig"),
message(Role::Assistant, "reading"),
]);
assert_eq!(
grown.attribute(Some(&first)),
ShapeChanged::Compared {
system: Vec::new(),
tools: PartChange::Same,
run: PartChange::Moved,
},
"a longer conversation is the run region moving"
);
assert_eq!(
first.attribute(Some(&first)),
ShapeChanged::Compared {
system: Vec::new(),
tools: PartChange::Same,
run: PartChange::Same,
},
"the same shape twice moves nothing"
);
let with_tool =
PromptShape::of(&prefix(), &[tool()], &[message(Role::User, "Task: dig")]).unwrap();
assert_eq!(
with_tool.attribute(Some(&first)),
ShapeChanged::Compared {
system: Vec::new(),
tools: PartChange::Moved,
run: PartChange::Same,
}
);
}
#[test]
fn the_tail_anchor_carries_the_trailing_tool_results_into_the_cache() {
let messages = vec![
message(Role::User, "Task: dig"),
message(Role::Assistant, "running it"),
tool_results("ok"),
];
let mut request = kernel::ChatRequest::empty("m", kernel::Ceiling::new(64).unwrap());
request.messages = std::borrow::Cow::Borrowed(&messages);
request.breakpoint = BreakpointPlan::for_conversation(&messages).message_breakpoint();
assert_eq!(
(0..messages.len())
.map(|index| request.carries_breakpoint(index))
.collect::<Vec<_>>(),
[false, false, true],
"one anchor, on the last message, tool results included"
);
assert!(
messages[2]
.content
.iter()
.any(|block| matches!(block, ContentBlock::ToolResult { .. })),
"the anchored message is the one the trailing tool results ride in"
);
}
#[test]
fn the_upper_bound_counts_segments_tool_schemas_and_the_conversation() {
let messages = [message(Role::User, "Task: dig")];
let bare = shape(&messages);
let segments = ["city", "building", "resident", "run"]
.iter()
.map(|text| u64::try_from(text.len()).unwrap())
.fold(0_u64, u64::saturating_add);
let measured = segments
+ u64::try_from(serde_json::to_vec(&Vec::<ToolDef>::new()).unwrap().len()).unwrap()
+ u64::try_from(serde_json::to_vec(&messages).unwrap().len()).unwrap();
assert_eq!(bare.request_upper_bound().unwrap(), measured);
let with_tool = PromptShape::of(&prefix(), &[tool()], &messages).unwrap();
assert!(
with_tool.request_upper_bound().unwrap() > bare.request_upper_bound().unwrap(),
"the tool schema bytes are part of what a request is billed for"
);
}
#[test]
fn one_segment_moving_is_named_by_its_slot() {
let first = shape(&[message(Role::User, "Task: dig")]);
let moved = FrozenPrefix::assemble(
super::super::FrozenSegment::new(SegmentSlot::City, b"city changed".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Building, b"building".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Resident, b"resident".to_vec()),
super::super::FrozenSegment::new(SegmentSlot::Run, b"run".to_vec()),
)
.unwrap();
let later = PromptShape::of(&moved, &[], &[message(Role::User, "Task: dig")]).unwrap();
assert_eq!(
later.attribute(Some(&first)),
ShapeChanged::Compared {
system: vec!["city".to_owned()],
tools: PartChange::Same,
run: PartChange::Same,
}
);
}
#[test]
fn a_replay_rebuilds_the_same_shape_and_re_derives_the_attribution() {
let first_messages = [message(Role::User, "Task: dig")];
let later_messages = [message(Role::User, "Task: dig"), tool_results("ok")];
let first = shape(&first_messages);
let later = PromptShape::of(&prefix(), &[tool()], &later_messages).unwrap();
let (first_assembled, first_compared) = records(&first_messages, &[], None);
let (later_assembled, later_compared) = records(&later_messages, &[tool()], Some(&first));
let recorded = PromptShapeCompared::deserialize(&later_compared).unwrap();
let rebuilt = PromptShape::from_parts(
segment_parts_of(&later_assembled).unwrap(),
recorded.tools,
recorded.run,
);
assert_eq!(rebuilt, later, "the records rebuild the same shape");
assert!(crate::replay::rebuild_shape(None, &first_assembled, &first_compared).is_ok());
assert!(
crate::replay::rebuild_shape(
Some((&first_assembled, &first_compared)),
&later_assembled,
&later_compared
)
.is_ok()
);
}
#[test]
fn a_line_whose_attribution_disagrees_is_refused() {
let first_messages = [message(Role::User, "Task: dig")];
let later_messages = [message(Role::User, "Task: dig again")];
let first = shape(&first_messages);
let later = shape(&later_messages);
let (first_assembled, first_compared) = records(&first_messages, &[], None);
let (later_assembled, _) = records(&later_messages, &[], Some(&first));
let lying = serde_json::to_value(later.recorded(ShapeChanged::FirstRequest).unwrap()).unwrap();
assert!(
crate::replay::rebuild_shape(
Some((&first_assembled, &first_compared)),
&later_assembled,
&lying
)
.is_err(),
"a recorded FirstRequest beside a comparable baseline is a lie"
);
}
#[test]
fn a_damaged_prompt_assembled_row_is_refused_in_one_sentence() {
let damaged = serde_json::Value::String("not a row".to_owned());
let refused = segment_parts_of(&damaged).unwrap_err();
assert_eq!(
refused.recovery(),
"replay with the build that wrote this ledger, or report the damaged line: its prompt_assembled payload is not what this build reads"
);
}