use std::fmt::Write as _;
use std::fs;
use std::path::PathBuf;
use chappi_core::{CognitiveState, Patch, apply_patch, snapshot_source, state_hash, validate};
use chappi_syntax::{canonical_hash, parse_document};
fn example_path(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("examples/v1")
.join(name)
}
fn decode_state(source: &str) -> CognitiveState {
let state =
CognitiveState::try_from(parse_document(source).expect("example syntax must parse"))
.expect("example snapshot must decode");
assert!(
validate(&state).is_empty(),
"example snapshot must validate"
);
state
}
#[test]
fn canonical_example_hash_is_cross_platform_stable() {
let source = fs::read_to_string(example_path("state.chappi")).expect("example must exist");
let document = parse_document(&source).expect("example syntax must parse");
let state = CognitiveState::try_from(document.clone()).expect("example snapshot must decode");
assert!(
validate(&state).is_empty(),
"example snapshot must validate"
);
assert_eq!(snapshot_source(&state), source.as_bytes());
let actual = hash_hex(canonical_hash(&document));
assert_eq!(
actual,
"147d76efee99185273151735d59047e0af66cdd1aee0d4340ac70137c7037d04"
);
}
#[test]
fn semantic_state_hash_matches_the_documented_patch_base() {
let source = fs::read_to_string(example_path("state.chappi")).expect("example must exist");
let state = decode_state(&source);
assert_eq!(
hash_hex(state_hash(&state)),
"2f8e28da99eddbaae241e3ab7d2030cb8d655958b14e57eeba930f6723e2b7b7"
);
}
#[test]
fn documented_patch_applies_to_the_canonical_example() {
let state_source =
fs::read_to_string(example_path("state.chappi")).expect("snapshot example must exist");
let patch_source =
fs::read_to_string(example_path("update.patch")).expect("patch example must exist");
let state = decode_state(&state_source);
let patch =
Patch::try_from(parse_document(&patch_source).expect("example patch syntax must parse"))
.expect("example patch must decode");
let updated = apply_patch(&state, &patch).expect("documented patch must apply");
assert_eq!(updated.nodes.len(), state.nodes.len() + 2);
assert_eq!(updated.focus_streams[0].frames.len(), 2);
}
fn hash_hex(hash: [u8; 32]) -> String {
hash.iter().fold(String::new(), |mut output, byte| {
write!(&mut output, "{byte:02x}").expect("writing to String cannot fail");
output
})
}