use std::sync::Arc;
use fathomdb_embedder_api::{Embedder, EmbedderError, EmbedderIdentity, Vector};
use fathomdb_engine::{Engine, PreparedWrite};
use fathomdb_schema::SQLITE_SUFFIX;
use tempfile::TempDir;
#[derive(Clone, Debug)]
struct FixedEmbedder;
impl Embedder for FixedEmbedder {
fn identity(&self) -> EmbedderIdentity {
EmbedderIdentity::new("deterministic", "rev-a", 8)
}
fn embed(&self, _text: &str) -> Result<Vector, EmbedderError> {
let mut v = vec![0.0_f32; 8];
v[0] = 1.0;
Ok(v)
}
}
fn fixture(name: &str) -> (TempDir, std::path::PathBuf) {
let dir = TempDir::new().unwrap();
let path = dir.path().join(format!("{name}{SQLITE_SUFFIX}"));
(dir, path)
}
fn seed_corpus(engine: &Engine) {
engine.configure_vector_kind_for_test("doc").expect("vector kind");
for body in ["hybrid retrieval alpha", "hybrid retrieval beta", "hybrid retrieval gamma"] {
engine
.write(&[PreparedWrite::Node {
kind: "doc".to_string(),
body: body.to_string(),
source_id: fathomdb_engine::SourceId::new("test:fixture").expect("test source id"),
logical_id: None,
state: fathomdb_engine::InitialState::Active,
reason: None,
valid_from: None,
valid_until: None,
}])
.expect("write");
}
engine.drain(10_000).expect("drain");
}
#[test]
fn default_search_paths_suppress_the_explanation_sidecar() {
let (_dir, path) = fixture("exp_obs_off");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
seed_corpus(&opened.engine);
let plain = opened.engine.search("hybrid").expect("search");
assert!(!plain.results.is_empty(), "expected fused hits");
assert!(plain.explanation.is_none(), "default search must suppress the explanation");
let reranked =
opened.engine.search_reranked("hybrid", None, 0, false, 0.3, 0).expect("search_reranked");
assert!(reranked.explanation.is_none(), "search_reranked must suppress the explanation");
opened.engine.close().unwrap();
}
#[test]
fn search_explained_is_byte_identical_results_plus_a_sidecar() {
let (_dir, path) = fixture("exp_obs_byte_stable");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
seed_corpus(&opened.engine);
let plain =
opened.engine.search_reranked("hybrid", None, 0, false, 0.3, 0).expect("search_reranked");
let explained =
opened.engine.search_explained("hybrid", None, 0, false, 0.3, 0).expect("search_explained");
assert_eq!(
explained.results, plain.results,
"explain path must not perturb the fused/reranked results"
);
assert_eq!(explained.projection_cursor, plain.projection_cursor);
assert_eq!(explained.soft_fallback, plain.soft_fallback);
assert!(plain.explanation.is_none());
assert!(explained.explanation.is_some(), "explain path must populate the sidecar");
opened.engine.close().unwrap();
}
#[test]
fn explanation_per_hit_mirrors_results_and_trace_is_populated() {
let (_dir, path) = fixture("exp_obs_fidelity");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
seed_corpus(&opened.engine);
let explained =
opened.engine.search_explained("hybrid", None, 0, false, 0.3, 0).expect("search_explained");
let exp = explained.explanation.as_ref().expect("sidecar present");
assert_eq!(exp.per_hit.len(), explained.results.len(), "one per_hit entry per returned hit");
for (p, h) in exp.per_hit.iter().zip(&explained.results) {
assert_eq!(p.id, h.write_cursor, "per_hit id mirrors the hit's positional write_cursor");
assert_eq!(p.arm, h.branch, "per_hit arm == hit branch");
assert_eq!(p.blended, h.score, "per_hit blended == hit score");
assert_eq!(p.ce_score, h.ce_score, "per_hit ce_score == hit ce_score");
assert_eq!(p.fused_score, p.blended, "depth=0: fused_score == blended");
assert!(
p.vector_rank.is_some() || p.text_rank.is_some() || p.graph_rank.is_some(),
"each hit carries at least one arm rank"
);
}
assert_eq!(exp.trace.query_chars, "hybrid".chars().count() as u32);
assert_eq!(exp.trace.rerank_depth, 0);
assert_eq!(exp.trace.alpha, 0.3);
assert!(!exp.trace.use_graph_arm);
assert!(!exp.trace.ce_active, "no CE rerank at depth 0");
assert!(
exp.trace.embedder_id.contains("deterministic@rev-a"),
"trace records the active embedder identity, got {:?}",
exp.trace.embedder_id
);
assert!(
exp.trace.vector_hits > 0 || exp.trace.text_hits > 0,
"at least one arm produced input hits"
);
opened.engine.close().unwrap();
}
#[test]
fn r_obs_1_golden_field_fidelity_at_rerank_depth_gt0() {
let (_dir, path) = fixture("exp_obs_golden");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
seed_corpus(&opened.engine);
let depth = 3usize;
let explained = opened
.engine
.search_explained("hybrid", None, depth, false, 0.3, 0)
.expect("search_explained");
let exp = explained.explanation.as_ref().expect("sidecar present");
assert_eq!(exp.per_hit.len(), explained.results.len());
for (p, h) in exp.per_hit.iter().zip(&explained.results) {
assert_eq!(p.id, h.write_cursor, "per_hit[i].id == results[i].write_cursor");
assert_eq!(p.arm, h.branch, "per_hit[i].arm == results[i].branch");
assert_eq!(p.ce_score, h.ce_score, "per_hit[i].ce_score == results[i].ce_score");
assert_eq!(p.blended, h.score, "per_hit[i].blended == results[i].score");
if let Some(ce) = p.ce_score {
assert!((0.0..=1.0).contains(&ce), "ce_score ∈ [0,1], got {ce}");
}
assert!(p.vector_rank.is_some() || p.text_rank.is_some() || p.graph_rank.is_some());
}
let top_fused = exp.per_hit.first().expect("≥1 hit").fused_score;
assert!(
top_fused > 0.0 && top_fused < 1.0,
"fused_score is raw RRF, not normalized: {top_fused}"
);
assert_eq!(exp.trace.rerank_depth, depth as u32);
assert_eq!(exp.trace.alpha, 0.3);
assert_eq!(exp.trace.pool_n, 0, "pool_n echoes the request (0 → defaults applied downstream)");
let model_scored = exp.per_hit.iter().any(|p| p.ce_score.is_some());
assert_eq!(exp.trace.ce_active, model_scored, "ce_active reflects whether the CE model ran");
opened.engine.close().unwrap();
}
#[test]
fn r_obs_2_cov_byte_identity_at_depth_gt0_and_graph_arm() {
let (_dir, path) = fixture("exp_obs_cov");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
seed_corpus(&opened.engine);
for (depth, graph) in [(3usize, false), (0usize, true)] {
let plain = opened
.engine
.search_reranked("hybrid", None, depth, graph, 0.3, 0)
.expect("search_reranked");
let explained = opened
.engine
.search_explained("hybrid", None, depth, graph, 0.3, 0)
.expect("search_explained");
assert_eq!(
explained.results, plain.results,
"explain must not perturb results (depth={depth}, graph={graph})"
);
assert_eq!(explained.projection_cursor, plain.projection_cursor);
assert_eq!(explained.soft_fallback, plain.soft_fallback);
assert!(plain.explanation.is_none(), "explain=false suppresses the sidecar");
assert!(explained.explanation.is_some(), "explain=true populates the sidecar");
}
opened.engine.close().unwrap();
}