#![allow(clippy::unwrap_used, clippy::expect_used, clippy::print_stderr)]
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use serde::Deserialize;
use mif_rh::{FindingIndex, IndexedFinding};
#[derive(Debug, Deserialize)]
struct Fixture {
top_k_default: usize,
pairs: Vec<Pair>,
}
#[derive(Debug, Deserialize)]
struct Pair {
id: String,
cross_topic: bool,
#[serde(default)]
top_k: Option<usize>,
a: Member,
b: Member,
}
#[derive(Debug, Deserialize)]
struct Member {
finding_id: String,
topic: String,
text: String,
}
fn required() -> bool {
std::env::var_os("MIF_RH_SEARCH_EVAL_REQUIRED").is_some()
}
fn rht_root() -> Option<PathBuf> {
if let Ok(path) = std::env::var("MIF_RH_PARITY_FIXTURES_ROOT") {
let path = PathBuf::from(path);
return path.is_dir().then_some(path);
}
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let candidate = manifest_dir
.join("../../../../repos/research-harness-template")
.canonicalize()
.ok()?;
candidate.is_dir().then_some(candidate)
}
fn fixture_path() -> Option<PathBuf> {
if let Ok(path) = std::env::var("MIF_RH_SEARCH_EVAL_FIXTURE") {
let path = PathBuf::from(path);
assert!(
path.is_file(),
"MIF_RH_SEARCH_EVAL_FIXTURE is set but {} is not a file",
path.display()
);
return Some(path);
}
let candidate = rht_root()?.join("evals/fixtures/search/known-similar-pairs.json");
candidate.is_file().then_some(candidate)
}
fn load_fixture() -> Option<Fixture> {
let Some(path) = fixture_path() else {
assert!(
!required(),
"MIF_RH_SEARCH_EVAL_REQUIRED is set but no known-similar-pairs fixture was found \
(set MIF_RH_SEARCH_EVAL_FIXTURE to the fixture file, or \
MIF_RH_PARITY_FIXTURES_ROOT to an rht checkout containing \
evals/fixtures/search/known-similar-pairs.json)"
);
eprintln!(
"skipping: no known-similar-pairs fixture found (set MIF_RH_SEARCH_EVAL_FIXTURE \
or MIF_RH_PARITY_FIXTURES_ROOT to override)"
);
return None;
};
let raw = std::fs::read_to_string(&path)
.map_err(|err| format!("failed to read {}: {err}", path.display()))
.expect("fixture file should be readable");
let fixture: Fixture = serde_json::from_str(&raw)
.map_err(|err| format!("failed to parse {}: {err}", path.display()))
.expect("fixture file should be valid known-similar-pairs JSON");
Some(fixture)
}
#[test]
fn fixture_is_well_formed() {
let Some(fixture) = load_fixture() else {
return;
};
assert!(
fixture.pairs.len() >= 10,
"expected at least 10 pairs, got {}",
fixture.pairs.len()
);
assert!(
fixture.pairs.iter().any(|pair| pair.cross_topic),
"expected at least one cross_topic pair"
);
assert!(
fixture.top_k_default > 0,
"top_k_default must be positive — a top-0 eval checks nothing"
);
for pair in &fixture.pairs {
if let Some(top_k) = pair.top_k {
assert!(top_k > 0, "{}: top_k override must be positive", pair.id);
}
}
let mut seen = HashSet::new();
for pair in &fixture.pairs {
for member in [&pair.a, &pair.b] {
assert!(
seen.insert(member.finding_id.as_str()),
"duplicate finding_id across members: {}",
member.finding_id
);
}
}
}
#[test]
fn known_similar_pairs_rank_each_counterpart_in_top_k() {
let Some(fixture) = load_fixture() else {
return;
};
let embedder = match mif_embed::Embedder::load() {
Ok(embedder) => embedder,
Err(err) => {
assert!(
!required(),
"MIF_RH_SEARCH_EVAL_REQUIRED is set but the embedding model failed to load: {err}"
);
eprintln!("skipping: embedding model unavailable ({err})");
return;
},
};
assert!(
!fixture.pairs.is_empty(),
"fixture has no pairs — the ranking assertions below would pass vacuously"
);
let mut embeddings: HashMap<String, Vec<f32>> = HashMap::new();
let mut indexed = Vec::new();
for pair in &fixture.pairs {
for member in [&pair.a, &pair.b] {
let vector = embedder
.embed(&member.text)
.map_err(|err| format!("failed to embed {}: {err}", member.finding_id))
.expect("member text should embed");
let fresh = embeddings
.insert(member.finding_id.clone(), vector.clone())
.is_none();
assert!(
fresh,
"finding_id {} appears in more than one pair member — every member must be a \
distinct index entry",
member.finding_id
);
indexed.push(IndexedFinding {
finding_id: member.finding_id.clone(),
topic: member.topic.clone(),
content: member.text.clone(),
vector,
});
}
}
let scratch = tempfile::tempdir().unwrap();
let mut index = FindingIndex::open(&scratch.path().join("search-eval.sqlite")).unwrap();
index.rebuild(&indexed).unwrap();
let mut failures = Vec::new();
for pair in &fixture.pairs {
let top_k = pair.top_k.unwrap_or(fixture.top_k_default);
assert!(
top_k > 0,
"{}: effective top_k is 0 — every check would fail as 'not in top-0', hiding the \
real configuration error",
pair.id
);
for (query, counterpart) in [(&pair.a, &pair.b), (&pair.b, &pair.a)] {
let matches = index
.find_similar(
&embeddings[&query.finding_id],
top_k,
Some(&query.finding_id),
)
.unwrap();
if !matches
.iter()
.any(|m| m.finding_id == counterpart.finding_id)
{
failures.push(format!(
"{}: {} -> {} not in top-{top_k}",
pair.id, query.finding_id, counterpart.finding_id
));
}
}
}
assert!(
failures.is_empty(),
"known-similar counterpart(s) missed the top-k:\n{}",
failures.join("\n")
);
}