use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use serde_json::Value;
use cleanlib_client::{derive_status, VerdictEnvelopeV1};
#[derive(Debug, serde::Deserialize)]
struct ExpectedCorpus {
fixtures: HashMap<String, ExpectedRow>,
}
#[derive(Debug, serde::Deserialize)]
struct ExpectedRow {
status: String,
reason_code: String,
}
fn fixtures_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join("contract-fixtures")
}
#[test]
fn all_envelope_fixtures_match_expected_json() {
let base = fixtures_dir();
let expected_path = base.join("EXPECTED.json");
let raw = fs::read_to_string(&expected_path).unwrap_or_else(|e| {
panic!("could not read {}: {e}", expected_path.display());
});
let corpus: ExpectedCorpus =
serde_json::from_str(&raw).expect("EXPECTED.json must parse as ExpectedCorpus");
let fixtures_path = base.join("fixtures");
let mut checked = 0usize;
let mut failures: Vec<String> = Vec::new();
for (fname, expected) in &corpus.fixtures {
if fname == "verdict-unreachable.json" {
continue;
}
let fixture_path = fixtures_path.join(fname);
let body = fs::read_to_string(&fixture_path).unwrap_or_else(|e| {
panic!("could not read fixture {}: {e}", fixture_path.display());
});
let env: VerdictEnvelopeV1 = serde_json::from_str(&body).unwrap_or_else(|e| {
panic!("fixture {} did not parse as VerdictEnvelopeV1: {e}", fname);
});
let derived = derive_status(&env);
let got_status = derived.status.as_str();
let got_reason = derived.reason_code.as_str();
if got_status != expected.status || got_reason != expected.reason_code {
failures.push(format!(
" {}: expected ({}, {}), got ({}, {})",
fname, expected.status, expected.reason_code, got_status, got_reason
));
}
checked += 1;
}
assert!(
failures.is_empty(),
"{} fixture(s) drift from EXPECTED.json:\n{}",
failures.len(),
failures.join("\n")
);
assert_eq!(
checked, 7,
"contract corpus must contain exactly 7 envelope fixtures (got {checked})"
);
}
#[test]
fn unreachable_fixture_is_out_of_band_not_envelope() {
let body = fs::read_to_string(fixtures_dir().join("fixtures").join("verdict-unreachable.json"))
.expect("verdict-unreachable.json must be vendored alongside envelope fixtures");
let v: Value =
serde_json::from_str(&body).expect("unreachable fixture must still be valid JSON");
assert!(v.is_object(), "unreachable fixture is a JSON object");
let typed: Result<VerdictEnvelopeV1, _> = serde_json::from_str(&body);
assert!(
typed.is_err(),
"verdict-unreachable.json must NOT parse as VerdictEnvelopeV1 (it's out-of-band)"
);
}