use super::*;
#[test]
fn t06_anti_laundering_chokepoint() {
let db = YantrikDB::new(":memory:", 8).unwrap();
assert_eq!(
db.provenance_gate_mode(),
crate::provenance::GateMode::Enforce,
"fresh DB defaults to enforce — the chokepoint is on by default"
);
let is_refusal = |e: &crate::error::YantrikDbError| {
matches!(
e,
crate::error::YantrikDbError::ProvenanceInconsistent { .. }
)
};
let rec = |source: &str, meta: serde_json::Value, seed: f32| {
db.record(
"t06 probe text",
"semantic",
0.5,
0.0,
604800.0,
&meta,
&vec_seed(seed, 8),
"t06_ns",
0.8,
"general",
source,
None,
)
};
let err = rec("inference", serde_json::json!({"kind": "fact"}), 1.0).unwrap_err();
assert!(is_refusal(&err), "record() kind=fact: got {err:?}");
let err = rec("inference", serde_json::json!({"kind": "observation"}), 1.5).unwrap_err();
assert!(is_refusal(&err), "record() kind=observation: got {err:?}");
let err = rec(
"inference",
serde_json::json!({"kind": "inference", "confidence_basis": "observation"}),
2.0,
)
.unwrap_err();
assert!(is_refusal(&err), "matrix: got {err:?}");
let rid = rec("inference", serde_json::json!({"kind": "inference"}), 3.0).unwrap();
let results = db
.recall(
&vec_seed(3.0, 8),
5,
None,
None,
false,
false,
None,
true, Some("t06_ns"),
None,
None,
None,
None,
false,
None, None, )
.unwrap();
let hit = results
.iter()
.find(|r| r.rid == rid)
.expect("the inference record must be recallable");
assert_eq!(
hit.source, "inference",
"recall must return source verbatim"
);
let mk = |source: &str, meta: serde_json::Value, seed: f32| RecordInput {
created_at: None,
idempotency_key: None,
text: "t06 batch probe".into(),
memory_type: "semantic".into(),
importance: 0.5,
valence: 0.0,
half_life: 604800.0,
metadata: meta,
embedding: vec_seed(seed, 8),
namespace: "t06_batch_ns".into(),
certainty: 0.8,
domain: "general".into(),
source: source.into(),
emotional_state: None,
};
let err = db
.record_batch(&[
mk("user", serde_json::json!({}), 4.0),
mk("inference", serde_json::json!({"kind": "fact"}), 5.0),
])
.unwrap_err();
assert!(is_refusal(&err), "batch: got {err:?}");
let rows: i64 = db
.conn()
.query_row(
"SELECT COUNT(*) FROM memories WHERE namespace = 't06_batch_ns'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(rows, 0, "a refused batch writes nothing");
let err = db
.record_with_rid(
"0198c1c2-0000-7000-8000-000000000d60",
"t06 rwr probe",
"semantic",
0.5,
0.0,
604800.0,
&serde_json::json!({"kind": "fact"}),
&vec_seed(6.0, 8),
"t06_ns",
0.8,
"general",
"inference",
None,
1_750_000_000_000_000,
&[],
"test-embedder",
None,
crate::provenance::WriteAdmission::Origin,
)
.unwrap_err();
assert!(is_refusal(&err), "record_with_rid(Origin): got {err:?}");
db.record_with_rid(
"0198c1c2-0000-7000-8000-000000000d61",
"t06 admitted replica probe",
"semantic",
0.5,
0.0,
604800.0,
&serde_json::json!({"kind": "fact"}),
&vec_seed(7.0, 8),
"t06_ns",
0.8,
"general",
"inference",
None,
1_750_000_000_000_000,
&[],
"test-embedder",
None,
crate::provenance::WriteAdmission::Admitted,
)
.expect("Admitted apply is not re-gated (leader gated at origin)");
let err = db
.correct(
&rid,
None,
Some(&serde_json::json!({"kind": "fact"})),
None,
None,
"promote",
)
.unwrap_err();
assert!(is_refusal(&err), "correct(metadata_merge): got {err:?}");
assert_eq!(db.history(&rid).unwrap().len(), 0, "no revision on refusal");
let target = rec("user", serde_json::json!({}), 8.0).unwrap();
let links = [RecordLink {
target_rid: target.clone(),
link_type: LinkType::Supports,
}];
let err = db
.record_with_links(
"t06 laundered via wrapper",
"semantic",
0.5,
0.0,
604800.0,
&serde_json::json!({"kind": "fact"}),
&vec_seed(9.0, 8),
"t06_ns",
0.8,
"general",
"inference",
None,
&links,
)
.unwrap_err();
assert!(is_refusal(&err), "record_with_links: got {err:?}");
let inbound = db
.linked_records(&target, LinkDirection::Inbound, None)
.unwrap();
assert!(
inbound.is_empty(),
"refused write applied links: {inbound:?}"
);
}
#[test]
fn t07_repetition_is_not_corroboration() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let write = |text: &str, key: Option<&str>| {
db.record_with_idempotency(
text,
"semantic",
0.7,
0.0,
604800.0,
&serde_json::json!({}),
&vec_seed(1.0, 8),
"t07_ns",
0.8,
"general",
"user",
None,
key,
None,
)
};
let rid1 = write("the same assertion", Some("t07-key")).unwrap();
let rid2 = write("the same assertion", Some("t07-key")).unwrap();
let rid3 = write("the same assertion", Some("t07-key")).unwrap();
assert_eq!(rid1, rid2);
assert_eq!(rid1, rid3, "every retry returns the ORIGINAL rid");
let rows: i64 = db
.conn()
.query_row(
"SELECT COUNT(*) FROM memories WHERE namespace = 't07_ns'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(rows, 1, "three keyed writes, one record");
let (certainty, importance): (f64, f64) = db
.conn()
.query_row(
"SELECT certainty, importance FROM memories WHERE rid = ?1",
rusqlite::params![rid1],
|r| Ok((r.get(0)?, r.get(1)?)),
)
.unwrap();
assert_eq!(certainty, 0.8, "certainty untouched by retries");
let stats_count: i64 = db
.conn()
.query_row(
"SELECT count FROM namespace_importance_stats WHERE namespace = 't07_ns'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(stats_count, 1, "one write, one calibration observation");
let _ = importance;
let err = write("a different assertion", Some("t07-key")).unwrap_err();
assert!(
matches!(
err,
crate::error::YantrikDbError::IdempotencyConflict { .. }
),
"expected IdempotencyConflict, got {err:?}"
);
let ka = write("unkeyed repeated content", None).unwrap();
let kb = write("unkeyed repeated content", None).unwrap();
let kc = write("unkeyed repeated content", None).unwrap();
assert_ne!(ka, kb);
assert_ne!(kb, kc);
let rows: i64 = db
.conn()
.query_row(
"SELECT COUNT(*) FROM memories WHERE namespace = 't07_ns' AND text = 'unkeyed repeated content'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(rows, 3, "no key, no dedup: three distinct records");
}
#[test]
fn test_relate_infers_entity_types() {
let db = YantrikDB::new(":memory:", 8).unwrap();
db.relate("MyApp", "React", "built_with", 1.0).unwrap();
let entities = db.search_entities(Some("MyApp"), None, 1).unwrap();
assert_eq!(entities.len(), 1);
assert_eq!(entities[0].entity_type, "project");
let entities = db.search_entities(Some("React"), None, 1).unwrap();
assert_eq!(entities.len(), 1);
assert_eq!(entities[0].entity_type, "tech");
}
#[test]
fn test_relate_infers_infrastructure() {
let db = YantrikDB::new(":memory:", 8).unwrap();
db.relate("Backend", "AWS", "deployed_on", 1.0).unwrap();
let entities = db.search_entities(Some("AWS"), None, 1).unwrap();
assert_eq!(entities[0].entity_type, "infrastructure");
}
#[test]
fn test_recall_with_response_has_certainty_reasons() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(1.0, 8);
db.record(
"Important architecture decision about microservices",
"semantic",
0.8,
0.0,
604800.0,
&empty_meta(),
&emb,
"default",
0.8,
"work",
"user",
None,
)
.unwrap();
let response = db
.recall_with_response(
&emb,
5,
None,
None,
false,
false,
Some("architecture decision"),
false,
None,
None,
None,
)
.unwrap();
assert!(
!response.certainty_reasons.is_empty(),
"should have certainty reasons"
);
assert!(response.confidence >= 0.0 && response.confidence <= 1.0);
}
#[test]
fn test_recall_empty_db_low_confidence() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(1.0, 8);
let response = db
.recall_with_response(
&emb,
5,
None,
None,
false,
false,
Some("anything"),
false,
None,
None,
None,
)
.unwrap();
assert!(
response.confidence < 0.5,
"empty DB should have low confidence"
);
assert!(
response
.certainty_reasons
.iter()
.any(|r| r.contains("No") || r.contains("Sparse") || r.contains("Weak")),
"should explain low confidence"
);
}
#[test]
fn test_relationship_depth_basic() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(1.0, 8);
db.relate("Alice", "Bob", "knows", 1.0).unwrap();
db.relate("Alice", "ProjectX", "works_on", 1.0).unwrap();
db.record(
"Alice presented the quarterly report",
"episodic",
0.5,
0.3,
604800.0,
&empty_meta(),
&emb,
"default",
0.8,
"work",
"user",
None,
)
.unwrap();
db.record(
"Alice prefers async communication",
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
&vec_seed(2.0, 8),
"default",
0.8,
"preference",
"user",
None,
)
.unwrap();
db.apply_pending_ops_once(100).unwrap();
let depth = db.relationship_depth("Alice", None).unwrap();
assert_eq!(depth.entity, "Alice");
assert_eq!(depth.entity_type, "person");
assert!(
depth.connection_count >= 2,
"Alice connected to Bob and ProjectX"
);
assert!(
depth.memories_mentioning >= 2,
"at least 2 memories mention Alice"
);
assert!(depth.depth_score > 0.0, "should have positive depth score");
assert!(depth.depth_score <= 1.0);
}
#[test]
fn test_relationship_depth_not_found() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let result = db.relationship_depth("NonexistentEntity", None);
assert!(result.is_err(), "should error for unknown entity");
}
#[test]
fn test_record_and_surface_procedural() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(3.0, 8);
let rid = db
.record_procedural(
"Use Agent tool with Explore subtype for architectural questions in this codebase",
&emb,
"work",
"code search",
0.8,
"default",
)
.unwrap();
let mem = db.get(&rid).unwrap().unwrap();
assert_eq!(mem.memory_type, "procedural");
assert!((mem.importance - 0.8).abs() < 0.01);
let results = db
.surface_procedural(&emb, Some("how to search code"), Some("work"), 5, None)
.unwrap();
assert!(!results.is_empty(), "should surface the procedural memory");
assert_eq!(results[0].memory_type, "procedural");
}
#[test]
fn test_reinforce_procedural() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(4.0, 8);
let rid = db
.record_procedural(
"Always run tests before pushing",
&emb,
"work",
"git workflow",
0.5,
"default",
)
.unwrap();
let reinforced = db.reinforce_procedural(&rid, 1.0).unwrap();
assert!(reinforced);
let mem = db.get(&rid).unwrap().unwrap();
assert!(
mem.importance > 0.5,
"importance should increase after positive reinforcement"
);
}
#[test]
fn test_procedural_stats() {
let db = YantrikDB::new(":memory:", 8).unwrap();
db.record_procedural(
"proc 1",
&vec_seed(1.0, 8),
"work",
"task A",
0.7,
"default",
)
.unwrap();
db.record_procedural(
"proc 2",
&vec_seed(2.0, 8),
"work",
"task B",
0.9,
"default",
)
.unwrap();
db.record_procedural(
"proc 3",
&vec_seed(3.0, 8),
"health",
"exercise",
0.5,
"default",
)
.unwrap();
let stats = db.procedural_stats(None).unwrap();
assert!(
stats.len() >= 2,
"should have stats for work and health domains"
);
let work_stats = stats.iter().find(|(d, _, _)| d == "work");
assert!(work_stats.is_some());
let (_, count, _) = work_stats.unwrap();
assert_eq!(*count, 2);
}
#[test]
fn test_session_awareness_trigger() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let emb = vec_seed(1.0, 8);
let sid = db
.session_start("default", "claude", &serde_json::json!({}))
.unwrap();
db.record(
"Worked on battle testing the MCP server",
"episodic",
0.7,
0.5,
604800.0,
&empty_meta(),
&emb,
"default",
0.8,
"work",
"user",
None,
)
.unwrap();
let _summary = db
.session_end(&sid, Some("Battle tested MCP server v0.2.8"))
.unwrap();
db.conn().execute(
"UPDATE sessions SET ended_at = ended_at - 86400 * 3, started_at = started_at - 86400 * 3 WHERE session_id = ?1",
params![sid],
).unwrap();
let config = ThinkConfig {
run_consolidation: false,
run_conflict_scan: false,
run_pattern_mining: false,
run_personality: false,
..Default::default()
};
let result = db.think(&config).unwrap();
let session_triggers: Vec<_> = result
.triggers
.iter()
.filter(|t| t.trigger_type == "session_awareness")
.collect();
assert!(
!session_triggers.is_empty(),
"should generate session awareness trigger after 3-day gap"
);
assert!(
session_triggers[0].reason.contains("hours"),
"reason should mention time gap"
);
}