use super::{
apr_version, build_identity, decide, model_sha256, read_receipt, receipt_path, write_receipt,
F2Decision, F2Receipt, F2ReceiptKey, F2ValidateReason, F2_RECEIPT_SCHEMA,
};
fn key() -> F2ReceiptKey {
F2ReceiptKey {
model_sha256: "a".repeat(64),
apr_version: "0.68.2".to_string(),
device: "NVIDIA GeForce RTX 4090".to_string(),
}
}
fn receipt_for(key: &F2ReceiptKey) -> F2Receipt {
F2Receipt {
schema: F2_RECEIPT_SCHEMA,
key: key.clone(),
validated_at: 1_758_400_000,
positions_judged: 64,
}
}
#[test]
fn a_receipt_whose_three_keys_all_match_is_read_and_the_forward_is_skipped() {
let k = key();
let r = receipt_for(&k);
assert_eq!(
decide(Ok(Some(r.clone())), &k, false),
F2Decision::Skip { receipt: r }
);
}
#[test]
fn falsifier_a_planted_receipt_with_the_wrong_model_sha256_revalidates() {
let k = key();
let mut planted = receipt_for(&k);
planted.key.model_sha256 = "b".repeat(64); match decide(Ok(Some(planted)), &k, false) {
F2Decision::Validate(F2ValidateReason::ModelSha256Mismatch { found, expected }) => {
assert_eq!(found, "b".repeat(64));
assert_eq!(expected, "a".repeat(64));
},
other => panic!("a wrong-model receipt must re-validate naming the model, got {other:?}"),
}
}
#[test]
fn falsifier_a_planted_receipt_from_a_different_apr_version_revalidates() {
let k = key();
let mut planted = receipt_for(&k);
planted.key.apr_version = "0.61.0".to_string(); match decide(Ok(Some(planted)), &k, false) {
F2Decision::Validate(F2ValidateReason::AprVersionMismatch { found, expected }) => {
assert_eq!(found, "0.61.0");
assert_eq!(expected, "0.68.2");
},
other => {
panic!("a wrong-version receipt must re-validate naming the version, got {other:?}")
},
}
}
#[test]
fn falsifier_a_planted_receipt_for_a_different_device_revalidates() {
let k = key();
let mut planted = receipt_for(&k);
planted.key.device = "NVIDIA GB10".to_string(); match decide(Ok(Some(planted)), &k, false) {
F2Decision::Validate(F2ValidateReason::DeviceMismatch { found, expected }) => {
assert_eq!(found, "NVIDIA GB10");
assert_eq!(expected, "NVIDIA GeForce RTX 4090");
},
other => panic!("a wrong-device receipt must re-validate naming the device, got {other:?}"),
}
}
#[test]
fn a_missing_receipt_validates_because_absence_is_never_consent() {
assert_eq!(
decide(Ok(None), &key(), false),
F2Decision::Validate(F2ValidateReason::NoReceipt)
);
}
#[test]
fn an_unreadable_receipt_validates_and_says_so_distinctly_from_missing() {
let d = decide(Err("permission denied".to_string()), &key(), false);
assert_eq!(
d,
F2Decision::Validate(F2ValidateReason::Unreadable(
"permission denied".to_string()
))
);
assert_ne!(d, F2Decision::Validate(F2ValidateReason::NoReceipt));
}
#[test]
fn a_receipt_from_an_older_schema_revalidates() {
let k = key();
let mut old = receipt_for(&k);
old.schema = 0;
assert_eq!(
decide(Ok(Some(old)), &k, false),
F2Decision::Validate(F2ValidateReason::SchemaMismatch {
found: 0,
expected: F2_RECEIPT_SCHEMA
})
);
}
#[test]
fn revalidate_forces_a_fresh_run_even_on_a_perfect_receipt() {
let k = key();
assert_eq!(
decide(Ok(Some(receipt_for(&k))), &k, true),
F2Decision::Validate(F2ValidateReason::Revalidate)
);
}
#[test]
fn revalidate_wins_over_every_other_reason_so_the_message_names_the_flag() {
let k = key();
for found in [
Ok(None),
Err("boom".to_string()),
Ok(Some({
let mut r = receipt_for(&k);
r.key.device = "other".to_string();
r
})),
] {
assert_eq!(
decide(found, &k, true),
F2Decision::Validate(F2ValidateReason::Revalidate)
);
}
}
#[test]
fn a_written_receipt_reads_back_equal_and_a_missing_one_is_none_not_err() {
let dir = std::env::temp_dir().join(format!("f2-receipt-test-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let k = key();
let path = receipt_path(&dir, &k.model_sha256);
assert_eq!(
read_receipt(&path),
Ok(None),
"no file must read as None, not Err"
);
let r = receipt_for(&k);
write_receipt(&path, &r).expect("write");
assert_eq!(read_receipt(&path), Ok(Some(r)));
let leftovers: Vec<_> = std::fs::read_dir(&dir)
.expect("dir")
.filter_map(Result::ok)
.filter(|e| e.file_name().to_string_lossy().ends_with(".tmp"))
.collect();
assert!(
leftovers.is_empty(),
"temp files left beside the receipt: {leftovers:?}"
);
std::fs::write(&path, "{not json").expect("corrupt");
let err = read_receipt(&path).expect_err("garbage must not parse");
assert!(err.contains("not a receipt"), "{err}");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn the_receipt_file_carries_all_three_keys_flat_so_a_human_can_read_them() {
let k = key();
let json = serde_json::to_string(&receipt_for(&k)).expect("serialize");
for needle in [
"\"model_sha256\"",
"\"apr_version\"",
"\"device\"",
"\"schema\"",
"\"positions_judged\"",
] {
assert!(json.contains(needle), "receipt JSON lacks {needle}: {json}");
}
}
#[test]
fn model_sha256_is_the_real_sha256_lowercase_hex() {
assert_eq!(
model_sha256(b""),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
assert_eq!(
model_sha256(b"abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn apr_version_names_this_crates_version_and_this_executable() {
let v = apr_version();
let want = format!("{} exe:", env!("CARGO_PKG_VERSION"));
assert!(v.starts_with(&want), "{v:?} does not start with {want:?}");
assert_ne!(
v,
env!("CARGO_PKG_VERSION"),
"the key is the version alone (#4290)"
);
assert_eq!(
v,
apr_version(),
"the same executable must key the same way twice"
);
}
#[test]
fn f2_receipt_key_is_the_build_not_the_version() {
let device = "NVIDIA GeForce RTX 4090".to_string();
let k = |id: String| F2ReceiptKey {
model_sha256: "ab".repeat(32),
apr_version: id,
device: device.clone(),
};
let rc1 = build_identity("0.69.3", Some(b"rc.1 forward loop"));
let rc2 = build_identity("0.69.3", Some(b"rc.2 one-engine loop"));
let written = receipt_for(&k(rc1.clone()));
assert!(matches!(
decide(Ok(Some(written.clone())), &k(rc2), false),
F2Decision::Validate(F2ValidateReason::AprVersionMismatch { .. })
));
assert!(matches!(
decide(Ok(Some(written)), &k(rc1), false),
F2Decision::Skip { .. }
));
let (u1, u2) = (
build_identity("0.69.3", None),
build_identity("0.69.3", None),
);
assert_ne!(u1, u2);
let unreadable = receipt_for(&k(u1));
assert!(matches!(
decide(Ok(Some(unreadable)), &k(u2), false),
F2Decision::Validate(F2ValidateReason::AprVersionMismatch { .. })
));
}
#[test]
fn two_writers_on_one_model_each_rename_a_whole_file() {
let dir = std::env::temp_dir().join(format!("f2-receipt-race-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let k = key();
let path = receipt_path(&dir, &k.model_sha256);
let mut a = receipt_for(&k);
a.positions_judged = 11;
let mut b = receipt_for(&k);
b.positions_judged = 22;
for _ in 0..40 {
let (pa, pb, ra, rb) = (path.clone(), path.clone(), a.clone(), b.clone());
let ta = std::thread::spawn(move || write_receipt(&pa, &ra));
let tb = std::thread::spawn(move || write_receipt(&pb, &rb));
ta.join().expect("thread a").expect("write a");
tb.join().expect("thread b").expect("write b");
let survivor = read_receipt(&path)
.expect("the receipt must always parse")
.expect("the receipt must exist");
assert!(
survivor == a || survivor == b,
"the survivor is neither writer's whole receipt: {survivor:?}"
);
}
let _ = std::fs::remove_dir_all(&dir);
}