#![cfg(feature = "std")]
#![allow(clippy::unwrap_used, clippy::expect_used)]
use dsfb_gpu_debug_core::casefile_v2::{
casefile_v2_header_hash, verify_casefile_v2_header, AtlasAlgebraStatus, CaseFileV2Header,
CaseFileV2HeaderError, CaseFileV2Schema, CorpusStage, CASEFILE_V2_HEADER_DOMAIN,
};
use dsfb_gpu_debug_core::motif::DetectorProfile;
fn header_with_d64() -> CaseFileV2Header {
CaseFileV2Header {
schema: CaseFileV2Schema::HeaderOnlyT10,
corpus_hash_v1: [0xAB; 32],
corpus_stage: CorpusStage::FrozenT10,
detector_profile: DetectorProfile::D64,
detector_registry_hash: DetectorProfile::D64.registry_hash(),
atlas_algebra_status: AtlasAlgebraStatus::S1_1TypeSurfaceOnly,
semantic_non_bypass: true,
}
}
#[test]
fn casefile_v2_header_domain_is_panel_locked() {
assert_eq!(
CASEFILE_V2_HEADER_DOMAIN,
"DSFB-GPU-ATLAS:CASEFILE-V2-HEADER:v1\0"
);
}
#[test]
fn casefile_v2_header_schema_at_t10_is_header_only() {
assert_eq!(
CaseFileV2Schema::HeaderOnlyT10.as_str(),
"HeaderOnlyT10",
"T.10 receipt schema must be HeaderOnlyT10"
);
}
#[test]
fn casefile_v2_header_records_corpus_hash() {
let h = header_with_d64();
assert_eq!(h.corpus_hash_v1, [0xAB; 32]);
assert_eq!(h.corpus_stage, CorpusStage::FrozenT10);
}
#[test]
fn casefile_v2_header_records_s1_1_type_surface_only() {
let h = header_with_d64();
assert_eq!(
h.atlas_algebra_status,
AtlasAlgebraStatus::S1_1TypeSurfaceOnly
);
assert_eq!(
AtlasAlgebraStatus::S1_1TypeSurfaceOnly.as_str(),
"S1_1TypeSurfaceOnly"
);
}
#[test]
fn casefile_v2_header_does_not_claim_registry_hash_v2() {
let h = header_with_d64();
assert_eq!(
h.detector_registry_hash,
DetectorProfile::D64.registry_hash()
);
let field_names: &[&str] = &[
"schema",
"corpus_hash_v1",
"corpus_stage",
"detector_profile",
"detector_registry_hash",
"atlas_algebra_status",
"semantic_non_bypass",
];
assert_eq!(field_names.len(), 7);
}
#[test]
fn casefile_v2_header_hash_is_stable() {
let h = header_with_d64();
let a = casefile_v2_header_hash(&h);
let b = casefile_v2_header_hash(&h);
assert_eq!(a, b);
assert_ne!(
a, [0u8; 32],
"header hash must not be the all-zero sentinel"
);
}
#[test]
fn casefile_v2_header_hash_changes_if_corpus_hash_changes() {
let h0 = header_with_d64();
let mut h1 = h0;
h1.corpus_hash_v1 = [0xCD; 32];
let a = casefile_v2_header_hash(&h0);
let b = casefile_v2_header_hash(&h1);
assert_ne!(a, b, "changing corpus_hash_v1 must change the header hash");
}
#[test]
fn casefile_v2_header_hash_changes_if_detector_profile_changes() {
let h0 = header_with_d64();
let mut h1 = h0;
h1.detector_profile = DetectorProfile::D205;
h1.detector_registry_hash = DetectorProfile::D205.registry_hash();
let a = casefile_v2_header_hash(&h0);
let b = casefile_v2_header_hash(&h1);
assert_ne!(
a, b,
"changing detector_profile must change the header hash"
);
}
#[test]
fn casefile_v2_header_hash_changes_if_atlas_algebra_status_changes() {
let h0 = header_with_d64();
let mut h1 = h0;
h1.semantic_non_bypass = !h1.semantic_non_bypass;
let a = casefile_v2_header_hash(&h0);
let b = casefile_v2_header_hash(&h1);
assert_ne!(
a, b,
"changing semantic_non_bypass must change the header hash"
);
}
#[test]
fn casefile_v2_header_verifies_clean() {
let h = header_with_d64();
let result = verify_casefile_v2_header(&h);
assert!(result.is_ok(), "clean header must verify; got {result:?}");
}
#[test]
fn casefile_v2_header_rejects_zero_corpus_hash() {
let mut h = header_with_d64();
h.corpus_hash_v1 = [0u8; 32];
let result = verify_casefile_v2_header(&h);
assert_eq!(result, Err(CaseFileV2HeaderError::ZeroCorpusHash));
}
#[test]
fn casefile_v2_header_preserves_semantic_non_bypass_true() {
let mut h = header_with_d64();
h.semantic_non_bypass = false;
let result = verify_casefile_v2_header(&h);
assert_eq!(result, Err(CaseFileV2HeaderError::SemanticNonBypassFalse));
}
#[test]
fn casefile_v2_header_rejects_prefreeze_stage_at_t10() {
let mut h = header_with_d64();
h.corpus_stage = CorpusStage::InternalAuditPreFreeze;
let result = verify_casefile_v2_header(&h);
assert_eq!(result, Err(CaseFileV2HeaderError::CorpusStagePreFreeze));
}
#[test]
fn t10_does_not_create_registry_hash_v2() {
let symbols: &[&str] = &[
"CaseFileV2Header",
"CaseFileV2HeaderError",
"CaseFileV2Schema",
"CorpusStage",
"AtlasAlgebraStatus",
"CASEFILE_V2_HEADER_DOMAIN",
"casefile_v2_header_hash",
"verify_casefile_v2_header",
];
assert_eq!(symbols.len(), 8);
assert!(!symbols.contains(&"registry_hash_v2"));
}
#[test]
fn t10_does_not_generate_detector_registry() {
assert_eq!(
CaseFileV2Schema::HeaderOnlyT10.as_str(),
"HeaderOnlyT10",
"T.10 non-claim: the only valid schema at T.10 is HeaderOnlyT10"
);
}
#[test]
fn t10_does_not_change_audit_path_hashes() {
let d16 = DetectorProfile::D16.registry_hash();
let d64 = DetectorProfile::D64.registry_hash();
let d128 = DetectorProfile::D128.registry_hash();
let d205 = DetectorProfile::D205.registry_hash();
for h in &[d16, d64, d128, d205] {
assert_ne!(*h, [0u8; 32]);
}
assert_ne!(d16, d64);
assert_ne!(d64, d128);
assert_ne!(d128, d205);
}