ontoref-code/crates/ontoref-ontology/tests/render_deterministic.rs

52 lines
2 KiB
Rust
Raw Normal View History

//! Deterministic byte-stability for the `state.ncl` renderer (O1.5 / D9).
//!
//! The renderer is invoked twice on the same input; the two outputs MUST
//! be byte-identical. Failure would indicate a non-deterministic source
//! such as iteration over an unordered collection or use of a clock.
use std::path::{Path, PathBuf};
use std::process::Command;
use ontoref_ontology::render::render_state_ncl;
use ontoref_ontology::types::StateConfig;
/// Project root that holds the `.ontoref/` spine.
///
/// Walks up from this crate until it finds the directory containing `.ontoref/`.
/// Post-ADR-048 (constellation) the spine sits at the constellation parent,
/// outside `code/`; pre-ADR-048 it was inside the workspace. Searching ancestors
/// resolves it under either layout instead of hard-coding a hop count.
fn workspace_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.find(|dir| dir.join(".ontoref").is_dir())
.map(Path::to_path_buf)
.expect("a `.ontoref/` spine must exist in an ancestor of CARGO_MANIFEST_DIR")
}
fn nickel_export_state(path: &Path) -> StateConfig {
let output = Command::new("nickel")
.arg("export")
.arg("--format")
.arg("json")
.arg(path)
.output()
.expect("nickel binary must be on PATH for this test");
assert!(output.status.success(), "nickel export failed");
serde_json::from_slice(&output.stdout).expect("StateConfig parse")
}
#[test]
fn repeated_render_produces_identical_bytes() {
let root = workspace_root();
let source = ontoref_ontology::layout::resolve_section(&root, "ontology/state.ncl");
let state = nickel_export_state(&source);
let first = render_state_ncl(&state);
let second = render_state_ncl(&state);
let third = render_state_ncl(&state);
assert_eq!(first, second, "render is not deterministic across two calls");
assert_eq!(second, third, "render is not deterministic across three calls");
}