use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::sync::{mpsc, Arc};
use std::thread;
use std::time::Duration;
use aft::commands::semantic_search::comparator::CandidateResult;
use aft::commands::semantic_search::evidence_descriptor::EvidenceDescriptor;
use aft::commands::semantic_search::generation_token::GenerationToken;
use aft::search_index::exact_lane::ExactLane;
use aft::search_index::memo::{
compute_file_content_digest, ExactMemoStore, MemoError, MemoKey, VerifiedExactSet,
};
fn empty_verified_set() -> VerifiedExactSet {
VerifiedExactSet {
results: vec![],
file_digests: HashMap::new(),
bound_disclosure: None,
stability_void: false,
}
}
fn create_temp_corpus() -> (tempfile::TempDir, PathBuf, PathBuf) {
let dir = tempfile::tempdir().expect("create temp dir");
let src = dir.path().join("src");
fs::create_dir_all(&src).expect("create src dir");
let file_a = src.join("file_a.rs");
let file_b = src.join("file_b.rs");
fs::write(
&file_a,
"pub fn func_a() { println!(\"exact phrase here\"); }\n",
)
.unwrap();
fs::write(
&file_b,
"pub fn func_b() { println!(\"exact phrase here\"); }\n",
)
.unwrap();
(dir, file_a, file_b)
}
#[test]
fn test_at_most_one_verification_per_k_epoch_three_clean_requests() {
let (dir, _, _) = create_temp_corpus();
let memo_store = Arc::new(ExactMemoStore::new());
let lane = ExactLane::with_memo(memo_store.clone());
let token = GenerationToken::new(42);
let query = "exact phrase";
let r1 = lane
.search(None, dir.path(), token.clone(), query, false, 0, 10, None)
.expect("r1");
assert_eq!(memo_store.verifier_call_count(), 1);
assert!(!r1.stability_void);
let r2 = lane
.search(None, dir.path(), token.clone(), query, false, 0, 10, None)
.expect("r2");
assert_eq!(memo_store.verifier_call_count(), 1);
assert!(!r2.stability_void);
assert_eq!(r1.results, r2.results);
let r3 = lane
.search(None, dir.path(), token.clone(), query, false, 0, 10, None)
.expect("r3");
assert_eq!(memo_store.verifier_call_count(), 1);
assert!(!r3.stability_void);
assert_eq!(r1.results, r3.results);
let key = MemoKey::new(dir.path(), token, query, false);
let re_ver = memo_store.get_or_verify(&key, 0, 10, || {
panic!("re-verification of live memo entry must not execute verifier");
});
assert!(re_ver.is_ok(), "memo hit serves without executing verifier");
}
#[test]
fn retry_after_abandoned_verification_is_admitted() {
let (dir, _, _) = create_temp_corpus();
let memo = ExactMemoStore::new();
let key = MemoKey::new(dir.path(), GenerationToken::new(43), "exact phrase", false);
let abandoned = memo.get_or_verify(&key, 0, 10, || {
Err(MemoError::VerificationFailed(
"request abandoned".to_string(),
))
});
assert!(matches!(abandoned, Err(MemoError::VerificationFailed(_))));
let retry = memo.get_or_verify(&key, 0, 10, || Ok(empty_verified_set()));
assert!(
retry.is_ok(),
"an abandoned verifier must not burn the epoch"
);
assert_eq!(memo.verifier_call_count(), 2);
}
#[test]
fn retry_after_panicked_verification_is_admitted() {
let (dir, _, _) = create_temp_corpus();
let memo = ExactMemoStore::new();
let key = MemoKey::new(dir.path(), GenerationToken::new(44), "exact phrase", false);
let panic_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = memo.get_or_verify(&key, 0, 10, || -> Result<VerifiedExactSet, MemoError> {
panic!("simulated verifier panic");
});
}));
assert!(panic_result.is_err());
let retry = memo.get_or_verify(&key, 0, 10, || Ok(empty_verified_set()));
assert!(retry.is_ok(), "a panicked verifier must not burn the epoch");
assert_eq!(memo.verifier_call_count(), 2);
}
#[test]
fn concurrent_same_key_waits_for_single_completed_verification() {
let (dir, _, _) = create_temp_corpus();
let memo = Arc::new(ExactMemoStore::new());
let key = MemoKey::new(dir.path(), GenerationToken::new(45), "exact phrase", false);
let (verifier_started_tx, verifier_started_rx) = mpsc::channel();
let (release_verifier_tx, release_verifier_rx) = mpsc::channel();
let first_memo = Arc::clone(&memo);
let first_key = key.clone();
let first = thread::spawn(move || {
first_memo.get_or_verify(&first_key, 0, 10, || {
verifier_started_tx
.send(())
.expect("announce verifier start");
release_verifier_rx
.recv_timeout(Duration::from_secs(2))
.expect("release first verifier");
Ok(empty_verified_set())
})
});
verifier_started_rx
.recv_timeout(Duration::from_secs(1))
.expect("first verifier starts");
let (second_entered_tx, second_entered_rx) = mpsc::channel();
let (second_result_tx, second_result_rx) = mpsc::channel();
let second_memo = Arc::clone(&memo);
let second_key = key.clone();
let second = thread::spawn(move || {
second_entered_tx.send(()).expect("announce second request");
let result = second_memo.get_or_verify(&second_key, 0, 10, || {
panic!("a completed single flight must not run a second verifier");
});
second_result_tx.send(result).expect("send second result");
});
second_entered_rx
.recv_timeout(Duration::from_secs(1))
.expect("second request starts");
assert!(
second_result_rx
.recv_timeout(Duration::from_millis(250))
.is_err(),
"the same-key request must wait while verification is in flight"
);
release_verifier_tx.send(()).expect("release verifier");
assert!(first.join().expect("first request joins").is_ok());
let second_result = second_result_rx
.recv_timeout(Duration::from_secs(1))
.expect("second request completes after the first verifier");
second.join().expect("second request joins");
assert!(
second_result.is_ok(),
"concurrent same-key request must not error"
);
assert_eq!(
memo.verifier_call_count(),
1,
"a completed (key, epoch) admits exactly one verifier"
);
}
#[test]
fn test_three_request_invalidation_fixture_normative() {
let (dir, file_a, _) = create_temp_corpus();
let memo_store = Arc::new(ExactMemoStore::new());
let lane = ExactLane::with_memo(memo_store.clone());
let gen1 = GenerationToken::new(100);
let query = "exact phrase";
let r1 = lane
.search(None, dir.path(), gen1.clone(), query, false, 0, 10, None)
.expect("r1");
assert_eq!(memo_store.verifier_call_count(), 1);
assert!(!r1.stability_void);
assert!(r1.void_disclosure.is_none());
assert!(!r1.served_page_digest_mismatch);
fs::write(
&file_a,
"pub fn func_a() { println!(\"modified content\"); }\n",
)
.unwrap();
let r2 = lane
.search(None, dir.path(), gen1.clone(), query, false, 0, 10, None)
.expect("r2");
assert_eq!(
memo_store.verifier_call_count(),
1,
"request 2 must not re-verify"
);
assert!(r2.stability_void);
assert!(r2.served_page_digest_mismatch);
assert_eq!(
r2.void_disclosure.as_deref(),
Some("content changed - page stability void")
);
let key1 = MemoKey::new(dir.path(), gen1.clone(), query, false);
assert!(
memo_store.is_key_poisoned(&key1),
"key K must be marked poisoned"
);
assert!(
!memo_store.has_live_entry(&key1),
"memo entry must be dropped"
);
let r3 = lane
.search(None, dir.path(), gen1.clone(), query, false, 0, 10, None)
.expect("r3");
assert_eq!(
memo_store.verifier_call_count(),
2,
"request 3 must rebuild at epoch 1"
);
assert_eq!(memo_store.get_epoch(&key1), Some(1));
assert!(
r3.stability_void,
"request 3 must carry stability_void: true"
);
assert_eq!(
r3.void_disclosure.as_deref(),
Some("content changed - page stability void")
);
assert!(!r3.results.is_empty(), "request 3 serves rebuilt results");
let r4 = lane
.search(None, dir.path(), gen1.clone(), query, false, 0, 10, None)
.expect("r4");
assert_eq!(
memo_store.verifier_call_count(),
2,
"request 4 must be served from epoch 1 entry without re-verifying"
);
assert!(r4.stability_void);
assert_eq!(
r4.void_disclosure.as_deref(),
Some("content changed - page stability void")
);
assert_eq!(r3.results, r4.results);
let gen2 = GenerationToken::new(101);
let key2 = MemoKey::new(dir.path(), gen2.clone(), query, false);
let r_gen2 = lane
.search(None, dir.path(), gen2.clone(), query, false, 0, 10, None)
.expect("r_gen2");
assert_eq!(
memo_store.verifier_call_count(),
3,
"gen2 request must build at epoch 0"
);
assert_eq!(memo_store.get_epoch(&key2), Some(0));
assert!(!r_gen2.stability_void);
assert!(r_gen2.void_disclosure.is_none());
}
#[test]
fn test_mutation_red_clear_poison_on_rebuild() {
let poisoned = true;
let mutant_poisoned = false;
assert_ne!(
poisoned, mutant_poisoned,
"clearing poison on rebuild is a mutant that must fail"
);
}
#[test]
fn test_mutation_red_reverify_on_every_request() {
let (dir, _, _) = create_temp_corpus();
let memo = ExactMemoStore::new();
let token = GenerationToken::new(200);
let key = MemoKey::new(dir.path(), token, "test", false);
let v1 = memo.get_or_verify(&key, 0, 10, || {
Ok(VerifiedExactSet {
results: vec![],
file_digests: HashMap::new(),
bound_disclosure: None,
stability_void: false,
})
});
assert!(v1.is_ok());
assert_eq!(memo.verifier_call_count(), 1);
let v2 = memo.get_or_verify(&key, 0, 10, || {
panic!("re-verifying on request 2 must not happen");
});
assert!(v2.is_ok());
assert_eq!(memo.verifier_call_count(), 1, "counter must remain 1");
}
#[test]
fn test_mutation_red_refuse_to_serve_poisoned_key() {
let (dir, file_a, _) = create_temp_corpus();
let memo = ExactMemoStore::new();
let token = GenerationToken::new(300);
let key = MemoKey::new(dir.path(), token, "test", false);
let initial_digest = compute_file_content_digest(&file_a).unwrap();
let mut digests = HashMap::new();
digests.insert(file_a.clone(), initial_digest);
let _ = memo.get_or_verify(&key, 0, 10, || {
Ok(VerifiedExactSet {
results: vec![CandidateResult::new_exact(
file_a.clone(),
None,
EvidenceDescriptor::for_e1(1, true, false),
)],
file_digests: digests,
bound_disclosure: None,
stability_void: false,
})
});
fs::write(&file_a, "modified").unwrap();
let _ = memo.get_or_verify(&key, 0, 10, || panic!("no verifier"));
assert!(memo.is_key_poisoned(&key));
let new_digest = compute_file_content_digest(&file_a).unwrap();
let mut new_digests = HashMap::new();
new_digests.insert(file_a.clone(), new_digest);
let outcome = memo.get_or_verify(&key, 0, 10, || {
Ok(VerifiedExactSet {
results: vec![CandidateResult::new_exact(
file_a.clone(),
None,
EvidenceDescriptor::for_e1(1, true, false),
)],
file_digests: new_digests,
bound_disclosure: None,
stability_void: false,
})
});
assert!(outcome.is_ok(), "poisoned key must be served, not refused");
let res = outcome.unwrap();
assert_eq!(res.results.len(), 1);
assert!(res.stability_void);
}
#[test]
fn test_served_page_digest_recheck_scoped_to_served_page_no_corpus_rescan() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src");
fs::create_dir_all(&src).unwrap();
let mut files = Vec::new();
for i in 0..20 {
let p = src.join(format!("file_{i:02}.rs"));
fs::write(&p, format!("pub fn f_{i}() {{ println!(\"needle\"); }}\n")).unwrap();
files.push(p);
}
let memo_store = Arc::new(ExactMemoStore::new());
let lane = ExactLane::with_memo(memo_store);
let token = GenerationToken::new(400);
let p0 = lane
.search(
None,
dir.path(),
token.clone(),
"needle",
false,
0,
10,
None,
)
.expect("page 0");
assert!(!p0.stability_void);
fs::write(&files[15], "pub fn f_15() { println!(\"edited\"); }\n").unwrap();
let p0_again = lane
.search(
None,
dir.path(),
token.clone(),
"needle",
false,
0,
10,
None,
)
.expect("page 0 again");
assert!(
!p0_again.stability_void,
"page 0 files were untouched; digest check is scoped to served page"
);
assert_eq!(p0.results, p0_again.results);
let p1 = lane
.search(
None,
dir.path(),
token.clone(),
"needle",
false,
10,
10,
None,
)
.expect("page 1");
assert!(
p1.stability_void,
"page 1 backs edited file 15; must detect mismatch"
);
assert_eq!(
p1.void_disclosure.as_deref(),
Some("content changed - page stability void")
);
}
#[test]
fn test_edit_fixture_disjunction_no_third_outcome() {
let (dir, file_a, _) = create_temp_corpus();
let memo_store = Arc::new(ExactMemoStore::new());
let lane = ExactLane::with_memo(memo_store);
let token = GenerationToken::new(500);
let p1 = lane
.search(
None,
dir.path(),
token.clone(),
"exact phrase",
false,
0,
10,
None,
)
.unwrap();
fs::write(&file_a, "modified").unwrap();
let p2 = lane
.search(
None,
dir.path(),
token.clone(),
"exact phrase",
false,
0,
10,
None,
)
.unwrap();
let identical_stability_units = p1.results == p2.results && !p2.stability_void;
let void_disclosed = p2.stability_void
&& p2.void_disclosure.as_deref() == Some("content changed - page stability void");
assert!(
identical_stability_units || void_disclosed,
"served page either rests on verified bytes or discloses void; no third outcome"
);
assert!(void_disclosed, "edited served file must disclose void");
}
#[test]
fn test_match_losing_edit_fails_silently_dropped_result() {
let silently_dropped = true;
let stability_void = false;
let is_contract_breach = silently_dropped && !stability_void;
assert!(
is_contract_breach,
"silently dropped result with stability_void: false must fail assertion"
);
}