use std::collections::HashMap;
use std::sync::Arc;
use fathomdb_embedder_api::{Embedder, EmbedderError, EmbedderIdentity, Vector};
use fathomdb_engine::{
apply_importance_reweight, Engine, EngineError, IdSpace, PreparedWrite, SearchHit,
SoftFallbackBranch,
};
use fathomdb_schema::SQLITE_SUFFIX;
use tempfile::TempDir;
fn hit(id: u64, body: &str, score: f64) -> SearchHit {
SearchHit {
id: IdSpace::content(id.to_string()),
write_cursor: id,
kind: "doc".to_string(),
body: body.to_string(),
score,
branch: SoftFallbackBranch::Vector,
source_id: None,
ce_score: None,
}
}
#[test]
fn importance_disabled_is_a_no_op() {
let hits = vec![hit(1, "a", 0.02), hit(2, "b", 0.01)];
let mut imp = HashMap::new();
imp.insert(1u64, 0.1_f64); let out = apply_importance_reweight(hits.clone(), &imp, &HashMap::new(), false);
assert_eq!(out, hits, "flag off => order + scores unchanged");
}
#[test]
fn importance_all_absent_equals_disabled_r_f9_4() {
let hits = vec![hit(1, "a", 0.02), hit(2, "b", 0.01)];
let disabled = apply_importance_reweight(hits.clone(), &HashMap::new(), &HashMap::new(), false);
let enabled_absent =
apply_importance_reweight(hits.clone(), &HashMap::new(), &HashMap::new(), true);
assert_eq!(enabled_absent, disabled, "all-absent reweight-ON must equal reweight-OFF");
assert_eq!(enabled_absent, hits, "and preserve the input order/scores");
}
#[test]
fn importance_enabled_deweights_and_reorders() {
let hits = vec![hit(1, "high-raw", 0.02), hit(2, "neutral", 0.015)];
let mut imp = HashMap::new();
imp.insert(1u64, 0.1_f64);
let out = apply_importance_reweight(hits, &imp, &HashMap::new(), true);
assert_eq!(out[0].write_cursor, 2, "de-weighted node drops below the neutral node");
assert_eq!(out[1].write_cursor, 1);
assert!(out[0].score >= out[1].score, "reweighted list stays sorted by score desc");
}
#[test]
fn confidence_scales_contribution_pure_fn() {
let hits = vec![hit(1, "edge", 0.02)];
let mut conf = HashMap::new();
conf.insert(1u64, 0.5_f64);
let out = apply_importance_reweight(hits, &HashMap::new(), &conf, true);
assert!((out[0].score - 0.01).abs() < 1e-9, "confidence 0.5 halves the contribution");
}
#[test]
fn importance_floor_zero_zeroes_contribution() {
let hits = vec![hit(1, "floored", 0.02), hit(2, "kept", 0.015)];
let mut imp = HashMap::new();
imp.insert(1u64, 0.0_f64);
let out = apply_importance_reweight(hits, &imp, &HashMap::new(), true);
assert_eq!(out[0].write_cursor, 2, "floored node ranks last");
assert_eq!(out[1].score, 0.0, "floor 0.0 zeroes the contribution");
}
#[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)
}
#[test]
fn importance_write_read_roundtrip_and_range_validation() {
let (_dir, path) = fixture("f9_roundtrip");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
let engine = &opened.engine;
engine.configure_vector_kind_for_test("doc").expect("vector kind");
let receipt = engine
.write(&[PreparedWrite::Node {
kind: "doc".to_string(),
body: "importance subject".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");
let cursor = receipt.cursor;
assert_eq!(engine.node_importance(cursor).expect("read"), None, "absent => NULL");
for v in [0.0_f64, 0.5, 1.0] {
engine.write_node_importance(cursor, v).expect("set importance");
assert_eq!(engine.node_importance(cursor).expect("read"), Some(v), "round-trips exact");
}
for bad in [-0.1_f64, 1.1] {
assert!(
matches!(engine.write_node_importance(cursor, bad), Err(EngineError::WriteValidation)),
"importance {bad} out of [0,1] must be rejected"
);
}
assert_eq!(engine.node_importance(cursor).expect("read"), Some(1.0));
opened.engine.close().unwrap();
}
fn seed_two_docs(engine: &Engine) -> (u64, u64) {
engine.configure_vector_kind_for_test("doc").expect("vector kind");
let a = engine
.write(&[PreparedWrite::Node {
kind: "doc".to_string(),
body: "importance alpha widget".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 a")
.cursor;
let b = engine
.write(&[PreparedWrite::Node {
kind: "doc".to_string(),
body: "importance beta widget".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 b")
.cursor;
engine.drain(10_000).expect("drain");
(a, b)
}
#[test]
fn importance_reweight_reorders_vs_off() {
let (_dir, path) = fixture("f9_reorder");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
let engine = &opened.engine;
seed_two_docs(engine);
let baseline = engine.search("importance").expect("search");
assert_eq!(baseline.results.len(), 2, "both docs retrieved");
let top_id = baseline.results[0].write_cursor;
let second_id = baseline.results[1].write_cursor;
engine.write_node_importance(top_id, 0.01).expect("set importance");
engine.set_importance_reweight_enabled_for_test(true);
let reweighted = engine.search("importance").expect("search");
let rw_bodies: std::collections::BTreeSet<&str> =
reweighted.results.iter().map(|h| h.body.as_str()).collect();
let base_bodies: std::collections::BTreeSet<&str> =
baseline.results.iter().map(|h| h.body.as_str()).collect();
assert_eq!(base_bodies, rw_bodies, "reweight preserves the result SET");
assert_eq!(reweighted.results[0].write_cursor, second_id, "de-weighted top hit is now second");
assert_eq!(reweighted.results[1].write_cursor, top_id);
for w in reweighted.results.windows(2) {
assert!(w[0].score >= w[1].score, "reweighted list stays sorted by score desc");
}
opened.engine.close().unwrap();
}
#[test]
fn importance_reweight_on_all_null_equals_off_e2e() {
let (_dir, path) = fixture("f9_identity");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
let engine = &opened.engine;
seed_two_docs(engine);
let off = engine.search("importance").expect("search off");
engine.set_importance_reweight_enabled_for_test(true);
let on_all_null = engine.search("importance").expect("search on");
assert_eq!(
off.results, on_all_null.results,
"reweight-ON with all-absent importance must equal reweight-OFF"
);
opened.engine.close().unwrap();
}
#[test]
fn explain_surfaces_importance_contribution() {
let (_dir, path) = fixture("f9_explain");
let opened = Engine::open_with_embedder_for_test(&path, Arc::new(FixedEmbedder)).expect("open");
let engine = &opened.engine;
let (a, _b) = seed_two_docs(engine);
engine.write_node_importance(a, 0.5).expect("set importance");
engine.set_importance_reweight_enabled_for_test(true);
let explained =
engine.search_explained("importance", None, 0, false, 0.3, 0).expect("search_explained");
let exp = explained.explanation.expect("explanation sidecar present");
let entry =
exp.per_hit.iter().find(|p| p.id == a).expect("per_hit entry for the weighted node");
assert_eq!(entry.importance, Some(0.5), "explain surfaces the node importance");
assert_eq!(entry.confidence, None, "a node hit carries no edge confidence");
opened.engine.close().unwrap();
}
fn entity_node(body: &str, logical_id: &str) -> PreparedWrite {
PreparedWrite::Node {
kind: "doc".to_string(),
body: body.to_string(),
source_id: fathomdb_engine::SourceId::new("test:fixture").expect("test source id"),
logical_id: Some(logical_id.to_string()),
state: fathomdb_engine::InitialState::Active,
reason: None,
valid_from: None,
valid_until: None,
}
}
fn conf_edge(from: &str, to: &str, logical_id: &str, body: &str, confidence: f64) -> PreparedWrite {
PreparedWrite::Edge {
kind: "link".to_string(),
from: from.to_string(),
to: to.to_string(),
source_id: fathomdb_engine::SourceId::new("test:fixture").expect("test source id"),
logical_id: Some(logical_id.to_string()),
body: Some(body.to_string()),
t_valid: None,
t_invalid: None,
confidence: Some(confidence),
extractor_model_id: None,
temporal_fallback: None,
}
}
#[test]
fn confidence_on_graph_arm_reorders_and_surfaces_in_explain() {
let (_dir, path) = fixture("f9_graph_conf");
let opened = Engine::open(&path).expect("open");
let engine = &opened.engine;
engine
.write(&[
entity_node("zephyr anchor entity", "zephyr"),
entity_node("beta reachable payload node", "beta"),
entity_node("gamma reachable payload node", "gamma"),
conf_edge("zephyr", "gamma", "e-zg", "collaboration record one", 0.10),
conf_edge("zephyr", "beta", "e-zb", "collaboration record two", 0.90),
])
.expect("write");
let pos = |results: &[SearchHit], needle: &str| -> usize {
results
.iter()
.position(|h| h.body.contains(needle))
.unwrap_or_else(|| panic!("{needle} must be present in results"))
};
let off = engine.search_reranked("zephyr", None, 0, true, 0.3, 0).expect("search off");
for name in ["gamma reachable", "beta reachable"] {
let h = off.results.iter().find(|h| h.body.contains(name)).expect("graph hit present");
assert_eq!(h.branch, SoftFallbackBranch::GraphArm, "{name} is a graph-arm hit");
}
assert!(
pos(&off.results, "gamma reachable") < pos(&off.results, "beta reachable"),
"OFF: gamma (better bfs_rank) outranks beta — {:?}",
off.results.iter().map(|h| (h.body.as_str(), h.score)).collect::<Vec<_>>()
);
engine.set_importance_reweight_enabled_for_test(true);
let on = engine.search_reranked("zephyr", None, 0, true, 0.3, 0).expect("search on");
assert!(
pos(&on.results, "beta reachable") < pos(&on.results, "gamma reachable"),
"ON: beta (edge conf 0.90) overtakes gamma (edge conf 0.10) — {:?}",
on.results.iter().map(|h| (h.body.as_str(), h.score)).collect::<Vec<_>>()
);
let explained =
engine.search_explained("zephyr", None, 0, true, 0.3, 0).expect("search_explained");
let exp = explained.explanation.expect("explanation sidecar present");
let beta_id =
on.results.iter().find(|h| h.body.contains("beta reachable")).unwrap().write_cursor;
let gamma_id =
on.results.iter().find(|h| h.body.contains("gamma reachable")).unwrap().write_cursor;
let beta_exp =
exp.per_hit.iter().find(|p| p.id == beta_id).expect("per_hit entry for beta graph hit");
let gamma_exp =
exp.per_hit.iter().find(|p| p.id == gamma_id).expect("per_hit entry for gamma graph hit");
assert_eq!(
beta_exp.confidence,
Some(0.90),
"explain surfaces the traversing edge confidence for beta"
);
assert_eq!(
gamma_exp.confidence,
Some(0.10),
"explain surfaces the traversing edge confidence for gamma"
);
opened.engine.close().unwrap();
}