use mempill_sqlite::open_default_in_memory;
use mempill_core::application::{IngestClaimRequest, QueryMemoryRequest};
use mempill_types::{
AgentId, BeliefStatus, Cardinality, Confidence, Criticality,
Disposition, ExternalKind, ProvenanceLabel,
};
#[tokio::test]
async fn b11_oracle_absent_external_contradiction_resolves_to_contested() {
let engine = open_default_in_memory()
.expect("in-memory DefaultEngine must open (oracle=None by construction)");
let agent = AgentId("b11-agent".into());
let ingest_a = IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "city".into(),
value: serde_json::json!("Berlin"),
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.95, valid_time_confidence: 0.0 },
criticality: Criticality::Medium,
derived_from: vec![],
};
let resp_a = engine.ingest_claim(ingest_a).await
.expect("ingest of claim A must succeed");
assert_eq!(
resp_a.disposition, Disposition::CommittedCheap,
"first External claim must be CommittedCheap (no conflict)"
);
let ingest_b = IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "city".into(),
value: serde_json::json!("Paris"), provenance: ProvenanceLabel::External(ExternalKind::ExternalFirstHand),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.90, valid_time_confidence: 0.0 },
criticality: Criticality::Medium,
derived_from: vec![],
};
let resp_b = engine.ingest_claim(ingest_b).await
.expect("DEFECT-1 FIXED: ingest B must succeed with edges pre-loaded before begin_atomic. \
The B11 contested path is now fully reachable end-to-end.");
assert_eq!(
resp_b.disposition, Disposition::Contested,
"B11(a): oracle absent + fresh External contradiction MUST produce Contested, not {:?}",
resp_b.disposition
);
let query_req = QueryMemoryRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "city".into(),
as_of_tx_time: None,
};
let query_resp = engine.query_memory(query_req).await
.expect("query must succeed");
assert_eq!(
query_resp.belief.status, BeliefStatus::Contested,
"B11: query after oracle-absent contradiction must return BeliefStatus::Contested, \
not {:?}. Silent incumbent-wins is the prohibited behavior.",
query_resp.belief.status
);
let all_surfaced_refs: Vec<_> = query_resp.belief.primary
.iter()
.map(|b| b.claim_ref.clone())
.chain(query_resp.belief.alternatives.iter().map(|b| b.claim_ref.clone()))
.collect();
assert!(
all_surfaced_refs.contains(&resp_a.claim_ref),
"B11: the INCUMBENT (claim A / 'Berlin') MUST be surfaced in Contested projection. \
It was not — the incumbent was excluded by ingest-time supersession (now fixed). \
Surfaced refs: {all_surfaced_refs:?}"
);
assert!(
all_surfaced_refs.contains(&resp_b.claim_ref),
"B11: the CHALLENGER (claim B / 'Paris') MUST be surfaced in Contested projection. \
Surfaced refs: {all_surfaced_refs:?}"
);
let all_surfaced_values: Vec<_> = query_resp.belief.primary
.iter()
.map(|b| b.fact.value.clone())
.chain(query_resp.belief.alternatives.iter().map(|b| b.fact.value.clone()))
.collect();
assert!(
all_surfaced_values.contains(&serde_json::json!("Berlin")),
"B11: 'Berlin' (incumbent value) MUST be visible in Contested. Got: {all_surfaced_values:?}"
);
assert!(
all_surfaced_values.contains(&serde_json::json!("Paris")),
"B11: 'Paris' (challenger value) MUST be visible in Contested. Got: {all_surfaced_values:?}"
);
}
#[tokio::test]
async fn b11_gate_level_oracle_absent_routes_to_contested_disposition() {
let engine = open_default_in_memory()
.expect("in-memory DefaultEngine must open");
let agent = AgentId("b11-gate-agent".into());
let resp = engine.ingest_claim(IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "location".into(),
value: serde_json::json!("Berlin"),
provenance: ProvenanceLabel::External(ExternalKind::ExternalFirstHand),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.95, valid_time_confidence: 0.0 },
criticality: Criticality::High,
derived_from: vec![],
}).await.expect("ingest must succeed");
assert_eq!(
resp.disposition, Disposition::CommittedCheap,
"gate must route non-conflicting External claim to CommittedCheap"
);
let q = engine.query_memory(QueryMemoryRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "location".into(),
as_of_tx_time: None,
}).await.expect("query must succeed");
assert!(
matches!(q.belief.status, BeliefStatus::Resolved | BeliefStatus::TimingUncertain),
"single live External claim must yield Resolved or TimingUncertain, got {:?}",
q.belief.status
);
let primary = q.belief.primary.as_ref()
.expect("primary belief must be present for single claim");
assert_eq!(primary.fact.value, serde_json::json!("Berlin"));
assert_eq!(primary.claim_ref, resp.claim_ref, "queried claim_ref must match ingested");
}
#[tokio::test]
async fn b11_model_derived_on_empty_subject_line_routes_to_committed_inferred() {
let engine = open_default_in_memory()
.expect("in-memory DefaultEngine must open");
let agent = AgentId("b11-model-agent".into());
let resp = engine.ingest_claim(IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "inferred_tag".into(),
value: serde_json::json!("premium"),
provenance: ProvenanceLabel::ModelDerived,
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.75, valid_time_confidence: 0.0 },
criticality: Criticality::Low,
derived_from: vec![],
}).await.expect("ModelDerived ingest must succeed");
assert_eq!(
resp.disposition, Disposition::CommittedInferred,
"ModelDerived ingest must be CommittedInferred (never Contested)"
);
}