relay-knowledge 1.1.14

Graph-database-based knowledge graph project.
Documentation
use crate::{
    domain::{ProposalConflictSeverity, ProposalKind, ProposalProvenance, ProposalState},
    storage::{
        IndexStore, NewProposal, NewProposalConflict, ProposalDecision, ProposalListRequest,
        SqliteGraphStore,
    },
};

#[tokio::test]
async fn sqlite_proposals_and_conflicts_round_trip() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    let proposal = store
        .insert_proposal(NewProposal {
            proposal_id: "proposal:fixture".to_owned(),
            source_scope: "docs".to_owned(),
            kind: ProposalKind::Evidence,
            title: "Derived evidence".to_owned(),
            summary: "OCR output".to_owned(),
            payload_json: "{\"source_scope\":\"docs\",\"evidence\":[]}".to_owned(),
            origin: "worker:ocr".to_owned(),
            provenance: ProposalProvenance {
                producer: "ocr_worker".to_owned(),
                provider: Some("fixture".to_owned()),
                model: Some("fixture-ocr".to_owned()),
                prompt_id: None,
                prompt_version: None,
                schema_version: Some("worker-proposal.v2".to_owned()),
                input_source_hash: Some("sha256:image".to_owned()),
                input_fact_ids: vec!["ev-1".to_owned()],
                stale_when: vec!["parent evidence changes".to_owned()],
                budget_notes: vec!["timeout_ms=30000".to_owned()],
            },
            confidence_basis_points: 7000,
            conflicts: vec![NewProposalConflict {
                conflict_id: "conflict:1".to_owned(),
                existing_fact_kind: "evidence".to_owned(),
                existing_fact_id: "ev-1".to_owned(),
                severity: ProposalConflictSeverity::Blocking,
                reason: "same parent evidence".to_owned(),
            }],
            now_ms: 10,
        })
        .await
        .expect("proposal should insert");

    assert_eq!(proposal.state, ProposalState::Proposed);
    assert_eq!(proposal.conflict_count, 1);
    assert_eq!(proposal.provenance.producer, "ocr_worker");
    assert_eq!(proposal.provenance.input_fact_ids, ["ev-1"]);
    assert_eq!(
        store
            .proposal_count(Some(ProposalState::Proposed))
            .await
            .expect("proposal count should load"),
        1
    );
    assert_eq!(
        store
            .proposal_count(Some(ProposalState::Rejected))
            .await
            .expect("rejected proposal count should load"),
        0
    );

    let listed = store
        .list_proposals(ProposalListRequest {
            state: Some(ProposalState::Proposed),
            limit: 10,
        })
        .await
        .expect("proposal list should load");
    let conflicts = store
        .proposal_conflicts("proposal:fixture".to_owned())
        .await
        .expect("conflicts should load");

    assert_eq!(listed.len(), 1);
    assert_eq!(conflicts[0].severity, ProposalConflictSeverity::Blocking);

    let decided = store
        .decide_proposal(ProposalDecision {
            proposal_id: "proposal:fixture".to_owned(),
            next_state: ProposalState::Rejected,
            actor: "reviewer".to_owned(),
            reason: Some("duplicate".to_owned()),
            now_ms: 20,
        })
        .await
        .expect("proposal should reject");

    assert_eq!(decided.state, ProposalState::Rejected);
    assert_eq!(decided.decided_by.as_deref(), Some("reviewer"));
    assert_eq!(
        store
            .proposal_count(Some(ProposalState::Proposed))
            .await
            .expect("updated proposal count should load"),
        0
    );
    assert_eq!(
        store
            .proposal_count(Some(ProposalState::Rejected))
            .await
            .expect("updated rejected count should load"),
        1
    );
}