use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use cleanlib_client::{CustomerState, Tier};
#[derive(Debug, serde::Deserialize)]
struct Golden {
mappings: HashMap<String, Row>,
}
#[derive(Debug, serde::Deserialize)]
struct Row {
state: String,
label: String,
copy: String,
tier: String,
color_token: String,
color_hex: String,
emoji: String,
}
fn golden_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/contract-fixtures/CUSTOMER_STATE_EXPECTED.json")
}
fn tier_str(t: Tier) -> &'static str {
match t {
Tier::Block => "block",
Tier::Warn => "warn",
Tier::Clean => "clean",
}
}
const UNKNOWN_PROBE: &str = "SOME_FUTURE_VARIANT";
#[test]
fn customer_state_matches_cross_surface_golden() {
let raw = fs::read_to_string(golden_path())
.unwrap_or_else(|e| panic!("could not read CUSTOMER_STATE_EXPECTED.json: {e}"));
let golden: Golden =
serde_json::from_str(&raw).expect("CUSTOMER_STATE_EXPECTED.json must parse");
let mut failures: Vec<String> = Vec::new();
for (wire, row) in &golden.mappings {
let source = if wire == "__UNKNOWN__" { UNKNOWN_PROBE } else { wire.as_str() };
let s = CustomerState::from_wire(source);
let got = [
("state", s.as_str(), row.state.as_str()),
("label", s.label(), row.label.as_str()),
("copy", s.copy(), row.copy.as_str()),
("tier", tier_str(s.tier()), row.tier.as_str()),
("color_token", s.color_token(), row.color_token.as_str()),
("color_hex", s.color_hex(), row.color_hex.as_str()),
("emoji", s.emoji(), row.emoji.as_str()),
];
for (field, got, want) in got {
if got != want {
failures.push(format!(" [{wire}] {field}: got {got:?}, want {want:?}"));
}
}
}
assert!(
failures.is_empty(),
"customer-state contract drift vs CUSTOMER_STATE_EXPECTED.json:\n{}",
failures.join("\n")
);
}
#[test]
fn golden_covers_every_known_wire_source() {
let raw = fs::read_to_string(golden_path()).unwrap();
let golden: Golden = serde_json::from_str(&raw).unwrap();
let missing: Vec<&str> = cleanlib_client::ALL_VERDICT_SOURCES
.iter()
.copied()
.filter(|src| !golden.mappings.contains_key(*src))
.collect();
assert!(missing.is_empty(), "wire sources absent from golden: {missing:?}");
}
#[test]
fn unknown_sentinel_is_fail_closed_not_clean() {
let raw = fs::read_to_string(golden_path()).unwrap();
let golden: Golden = serde_json::from_str(&raw).unwrap();
let sentinel = golden.mappings.get("__UNKNOWN__").expect("golden must carry __UNKNOWN__ sentinel");
assert_eq!(sentinel.state, "not_yet_assessed");
assert_ne!(sentinel.state, "clean");
assert_eq!(CustomerState::from_wire(UNKNOWN_PROBE), CustomerState::NotYetAssessed);
}