use super::*;
fn id(byte: u8) -> AccessId {
AccessId::new(TxId::from_bytes([byte; 12]))
}
#[test]
fn current_time_accepts_only_empty_objects() {
for arguments in ["{}", " \n {\t} "] {
assert!(launch_current_time(arguments).is_ok_and(|time| !time.is_empty()));
}
for arguments in ["", "{", "null", "[]", "{\"zone\":\"UTC\"}"] {
assert_eq!(
launch_current_time(arguments),
Err("invalid CurrentTime arguments".to_owned())
);
}
}
#[test]
fn schemas_and_ids_are_strict() {
let valid = r#"{"title":"t","navigation_hint":"h","narrative":"n","connections":["000000000000000000000000"]}"#;
assert!(parse::<CreateArguments>("KmapCreateNode", valid).is_ok());
for invalid_json in [
r#"{"title":"t","navigation_hint":"h","narrative":"n"}"#,
r#"{"title":"t","navigation_hint":"h","narrative":"n","connections":[],"extra":1}"#,
"[]",
"{",
] {
assert_eq!(
parse::<CreateArguments>("KmapCreateNode", invalid_json).err(),
Some("invalid KmapCreateNode arguments".to_owned())
);
}
for invalid_id in [
"00000000000000000000000",
"0000000000000000000000000",
"00000000000000000000000A",
"00000000000000000000000g",
] {
assert!(parse_id(invalid_id, "KmapOpenNode").is_err());
}
}
#[test]
fn open_formatting_tracks_components_order_and_first_provenance() {
let mut state = SessionState::default();
let first = state.record_open(vec![
LoadedNode {
access_id: id(1),
source: None,
title: "Root".to_owned(),
navigation_hint: "Start".to_owned(),
narrative: Some("Root text".to_owned()),
},
LoadedNode {
access_id: id(2),
source: Some(id(1)),
title: "Child".to_owned(),
navigation_hint: "Next".to_owned(),
narrative: None,
},
]);
assert_eq!(
first,
format!(
"Node ID: {}\nTitle: Root\nNavigation Hint: Start\nNarrative: Root text\n\nNode ID: {}\nTitle: Child\nNavigation Hint: Next",
render_id(id(1)),
render_id(id(2))
)
);
let second = state.record_open(vec![LoadedNode {
access_id: id(2),
source: Some(id(9)),
title: "Child".to_owned(),
navigation_hint: "Next".to_owned(),
narrative: Some("Later".to_owned()),
}]);
assert_eq!(
second,
format!("Node ID: {}\nNarrative: Later", render_id(id(2)))
);
assert_eq!(state.provenance.get(&id(2)), Some(&id(1)));
assert_eq!(
state.record_open(Vec::new()),
"No new Kmap node components."
);
}
#[test]
fn session_penalties_deduplicate_and_preserve_loaded_order() {
let mut state = SessionState::default();
for node in [id(1), id(2), id(3)] {
state.add_loaded(node);
}
state.provenance.insert(id(2), id(1));
let nodes = state.new_penalties(vec![id(2), id(2), id(9), id(1)]);
assert_eq!(nodes, vec![id(2), id(1)]);
let measurements = state.measurements(&nodes);
assert_eq!(measurements.len(), 1);
assert_eq!(measurements[0].source, id(1));
assert_eq!(measurements[0].target, id(2));
state.commit_penalties(nodes);
assert_eq!(state.eligible(), vec![id(3)]);
}
#[test]
fn turn_binding_requires_identical_context_profile_and_policy() {
let active = (1, 2, 3);
let mut slot = None;
assert_eq!(bind_once(&mut slot, active), Ok(()));
assert_eq!(bind_once(&mut slot, active), Ok(()));
for different in [(9, 2, 3), (1, 9, 3), (1, 2, 9)] {
assert_eq!(
bind_once(&mut slot, different),
Err(
"different Kmap access context, profile ID, or policy is already active".to_owned()
)
);
}
}