use super::*;
use crate::guard::CommitBudgetGuard;
use crate::id::{CODEC_RAW, Cid, HASH_BLAKE3_256, Multihash, NodeId};
use proptest::prelude::*;
fn zero_cid() -> Cid {
Cid::new(
CODEC_RAW,
Multihash::wrap(HASH_BLAKE3_256, &[0u8; 32]).expect("32-byte zero digest"),
)
}
fn nonzero_cid(tag: u8) -> Cid {
let mut digest = [0u8; 32];
digest[0] = tag;
digest[31] = 0xFF;
Cid::new(
CODEC_RAW,
Multihash::wrap(HASH_BLAKE3_256, &digest).expect("32-byte digest"),
)
}
fn sample_bimodal(n_same: usize, n_diff: usize, seed: u64) -> Vec<f32> {
let mut state = seed.wrapping_mul(0x9E37_79B9_7F4A_7C15).wrapping_add(1);
let mut out = Vec::with_capacity(n_same + n_diff);
for _ in 0..n_same {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1);
#[allow(clippy::cast_precision_loss)]
let u = ((state >> 33) as f32) / ((1u64 << 31) as f32);
out.push(0.92 + (u - 0.5) * 0.06);
}
for _ in 0..n_diff {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1);
#[allow(clippy::cast_precision_loss)]
let u = ((state >> 33) as f32) / ((1u64 << 31) as f32);
out.push(0.30 + (u - 0.5) * 0.2);
}
out
}
fn make_candidate(id_tag: u8, cosine: f32, name: &str, ns: &str, trust: &str) -> Candidate {
let mut bytes = [0u8; 16];
bytes[0] = id_tag;
let node_id = NodeId::from_bytes_raw(bytes);
Candidate {
node_id,
cosine,
name: name.to_string(),
namespace: ns.to_string(),
trust: trust.to_string(),
}
}
#[test]
fn resolve_creates_below_threshold() {
let candidates = vec![
make_candidate(1, 0.10, "totally different", "person", "verified"),
make_candidate(2, 0.15, "also different", "person", "verified"),
];
let req = ResolveRequest {
query: "Alice".into(),
namespace: "person".into(),
trust: "verified".into(),
candidates,
local_sample: sample_bimodal(128, 128, 42),
latency_budget_ms: Some(50),
commit_cid: nonzero_cid(1),
};
let out = resolve_or_create(&req);
assert!(
matches!(out.result, ResolveResult::Created { .. }),
"expected Created, got {:?}",
out.result
);
assert_eq!(out.seed_source, HnswSeedSource::CommitDerived);
}
#[test]
fn resolve_merges_above_threshold() {
let candidates = vec![make_candidate(3, 0.97, "Alice Smith", "person", "verified")];
let req = ResolveRequest {
query: "Alice Smith".into(),
namespace: "person".into(),
trust: "verified".into(),
candidates,
local_sample: sample_bimodal(128, 128, 7),
latency_budget_ms: Some(50),
commit_cid: nonzero_cid(2),
};
let out = resolve_or_create(&req);
match out.result {
ResolveResult::Resolved { signals_passed, .. } => {
assert!(
signals_passed >= 2,
"two-of-three gate required, got {signals_passed}"
);
}
other => panic!("expected Resolved, got {other:?}"),
}
}
#[test]
fn threshold_derived_from_local_samples() {
let sample = sample_bimodal(256, 256, 11);
let thr = derive_local_threshold(&sample, SIGMA_MULTIPLIER_FOR_COLLAPSE)
.expect("sample >= MIN_SAMPLE_SIZE");
assert!(
thr.mu_same > 0.80 && thr.mu_same < 1.00,
"mu_same drift: {}",
thr.mu_same
);
assert!(
thr.mu_diff > 0.10 && thr.mu_diff < 0.50,
"mu_diff drift: {}",
thr.mu_diff
);
assert!(
thr.tau_n > thr.mu_diff && thr.tau_n < thr.mu_same,
"tau_n out of band: {} (mu_diff={}, mu_same={})",
thr.tau_n,
thr.mu_diff,
thr.mu_same
);
let tiny = vec![0.8_f32; 32];
assert!(derive_local_threshold(&tiny, 2.0).is_none());
}
#[test]
fn commit_budget_guard_cuts_off() {
let mut g = CommitBudgetGuard::start(
"gap-04-resolve-or-create",
50,
RESOLVE_OR_CREATE_P99_MS,
zero_cid(),
);
let _ = g.charge_with("derive_threshold", 30).unwrap();
let err = g.charge_with("consensus", 60).unwrap_err();
assert_eq!(err.hard_wall_ms, RESOLVE_OR_CREATE_P99_MS);
assert!(g.hard_wall_hit);
let rep = g.into_report();
assert!(rep.hard_wall_hit);
assert_eq!(rep.tag, "gap-04-resolve-or-create");
}
#[test]
fn hnsw_seed_fallback_on_zero_cid() {
if std::env::var("MNEM_CANONICAL_HNSW_SEED").is_ok() {
return;
}
let (seed, src) = resolve_hnsw_seed(&zero_cid());
assert_eq!(src, HnswSeedSource::Fallback);
assert_eq!(seed, HNSW_SEED_FALLBACK);
}
#[test]
fn hnsw_seed_commit_derived_reproducible() {
if std::env::var("MNEM_CANONICAL_HNSW_SEED").is_ok() {
return;
}
let cid = nonzero_cid(5);
let (s1, src1) = resolve_hnsw_seed(&cid);
let (s2, src2) = resolve_hnsw_seed(&cid);
assert_eq!(s1, s2);
assert_eq!(src1, HnswSeedSource::CommitDerived);
assert_eq!(src2, HnswSeedSource::CommitDerived);
let (s3, _) = resolve_hnsw_seed(&nonzero_cid(6));
assert_ne!(s1, s3);
}
#[test]
fn two_of_three_requires_two_signals() {
let c = make_candidate(1, 0.10, "totally different", "person", "verified");
let r = resolve_or_create_simple(
"Alice",
0.85,
std::slice::from_ref(&c),
"person",
"verified",
);
assert!(matches!(
r,
ResolveResult::Refused(RefusalReason::SingleSignalOnly)
));
let c2 = make_candidate(2, 0.10, "Alice", "person", "verified");
let r2 = resolve_or_create_simple(
"Alice",
0.85,
std::slice::from_ref(&c2),
"person",
"verified",
);
assert!(matches!(r2, ResolveResult::Resolved { .. }));
}
#[test]
fn normalized_levenshtein_bounds() {
assert!((normalized_levenshtein("abc", "abc") - 0.0).abs() < 1e-6);
assert!((normalized_levenshtein("abc", "xyz") - 1.0).abs() < 1e-6);
let d = normalized_levenshtein("abc", "abd");
assert!((d - (1.0 / 3.0)).abs() < 1e-6, "got {d}");
}
proptest! {
#[test]
fn resolve_or_create_hits_50ms_hard_wall(
stage1_ms in 0u32..=40,
stage2_ms in 0u32..=200,
_n_candidates in 1usize..=4096,
) {
let mut g = CommitBudgetGuard::start(
"gap-04-resolve-or-create",
RESOLVE_OR_CREATE_P99_MS, RESOLVE_OR_CREATE_P99_MS,
zero_cid(),
);
let r1 = g.charge_with("derive_threshold", stage1_ms);
prop_assert!(r1.is_ok());
let r2 = g.charge_with("consensus", stage2_ms);
if stage2_ms > RESOLVE_OR_CREATE_P99_MS {
prop_assert!(r2.is_err(), "stage2_ms={stage2_ms} should breach wall");
} else {
prop_assert!(r2.is_ok(), "stage2_ms={stage2_ms} should fit");
}
}
}