relay-knowledge 1.1.10

Graph-database-based knowledge graph project.
Documentation
use super::*;
use crate::domain::{EvidenceRecord, SourceScope};

#[tokio::test]
async fn older_index_refresh_does_not_regress_metadata() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    store
        .mark_refresh_complete(IndexKind::Semantic, GraphVersion::new(2))
        .await
        .expect("newer refresh should succeed");

    let stale_completion = store
        .mark_refresh_complete(IndexKind::Semantic, GraphVersion::new(1))
        .await
        .expect("older completion should not fail");

    assert_eq!(stale_completion.index_version, 1);
    assert_eq!(stale_completion.indexed_graph_version, GraphVersion::new(2));
}

#[tokio::test]
async fn missing_index_status_row_fails_refresh() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    store
        .run(|connection| {
            connection.execute("DELETE FROM index_status WHERE kind = 'vector'", [])?;
            Ok(())
        })
        .await
        .expect("fixture corruption should succeed");

    let error = store
        .mark_refresh_complete(IndexKind::Vector, GraphVersion::new(1))
        .await
        .expect_err("missing row should fail");

    assert_eq!(
        error.to_string(),
        "invalid storage input: index status row for 'vector' is missing"
    );
}

#[tokio::test]
async fn missing_required_index_status_row_fails_status_read() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    store
        .run(|connection| {
            connection.execute("DELETE FROM index_status WHERE kind = 'semantic'", [])?;
            Ok(())
        })
        .await
        .expect("fixture corruption should succeed");

    let error = store
        .index_statuses()
        .await
        .expect_err("missing row should fail");

    assert_eq!(
        error.to_string(),
        "invalid storage input: required index status row for 'semantic' is missing in storage metadata"
    );
}

#[tokio::test]
async fn unknown_index_kind_in_metadata_is_rejected() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    store
        .run(|connection| {
            connection.execute(
                "INSERT INTO index_status
                 (kind, index_version, indexed_graph_version, state, last_error)
                 VALUES ('future', 0, 0, 'fresh', NULL)",
                [],
            )?;
            Ok(())
        })
        .await
        .expect("fixture corruption should succeed");

    let error = store
        .index_statuses()
        .await
        .expect_err("unknown kind should fail");

    assert_eq!(
        error.to_string(),
        "invalid storage input: unknown index kind 'future' in storage metadata"
    );
}

#[tokio::test]
async fn unknown_index_state_in_metadata_is_rejected() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    store
        .run(|connection| {
            connection.execute(
                "UPDATE index_status SET state = 'mystery' WHERE kind = 'bm25'",
                [],
            )?;
            Ok(())
        })
        .await
        .expect("fixture corruption should succeed");

    let error = store
        .index_statuses()
        .await
        .expect_err("unknown state should fail");

    assert_eq!(
        error.to_string(),
        "invalid storage input: unknown index state 'mystery' in storage metadata"
    );
}

#[tokio::test]
async fn update_removes_unreferenced_entities() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    let scope = SourceScope::parse("docs").expect("scope should parse");
    let rust = EvidenceRecord::new(
        "ev-1",
        scope.clone(),
        "Graph storage",
        vec!["Rust".to_owned()],
    )
    .expect("evidence should validate");
    store
        .commit_mutation_batch(GraphMutationBatch::new(vec![rust]).expect("batch"))
        .await
        .expect("first commit should succeed");
    let sqlite = EvidenceRecord::new("ev-1", scope, "Graph storage", vec!["SQLite".to_owned()])
        .expect("evidence should validate");

    store
        .commit_mutation_batch(GraphMutationBatch::new(vec![sqlite]).expect("batch"))
        .await
        .expect("second commit should succeed");
    let graph = store.inspect_graph().await.expect("graph should inspect");

    assert_eq!(graph.entity_count, 1);
}

#[tokio::test]
async fn search_preserves_entity_labels_with_unit_separator() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    let scope = SourceScope::parse("docs").expect("scope should parse");
    let label = "Alpha\u{1f}Beta".to_owned();
    let evidence = EvidenceRecord::new("ev-separator", scope, "Needle evidence", vec![label])
        .expect("evidence should validate");
    let receipt = store
        .commit_mutation_batch(GraphMutationBatch::new(vec![evidence]).expect("batch"))
        .await
        .expect("commit should succeed");

    let hits = store
        .search(GraphSearchRequest {
            query: "Needle".to_owned(),
            source_scope: Some("docs".to_owned()),
            graph_version: receipt.graph_version,
            limit: 5,
            disabled_retriever_sources: Vec::new(),
        })
        .await
        .expect("search should succeed");

    assert_eq!(hits[0].entity_labels, ["Alpha\u{1f}Beta"]);
}

#[tokio::test]
async fn search_orders_equal_scores_by_evidence_id() {
    let store = SqliteGraphStore::open_in_memory().expect("store should open");
    let scope = SourceScope::parse("docs").expect("scope should parse");
    let evidence = ["ev-c", "ev-a", "ev-b"]
        .into_iter()
        .map(|id| EvidenceRecord::new(id, scope.clone(), "Tie token", Vec::new()))
        .collect::<Result<Vec<_>, _>>()
        .expect("evidence should validate");
    let receipt = store
        .commit_mutation_batch(GraphMutationBatch::new(evidence).expect("batch"))
        .await
        .expect("commit should succeed");

    let hits = store
        .search(GraphSearchRequest {
            query: "Tie".to_owned(),
            source_scope: Some("docs".to_owned()),
            graph_version: receipt.graph_version,
            limit: 2,
            disabled_retriever_sources: Vec::new(),
        })
        .await
        .expect("search should succeed");
    let ids = hits
        .iter()
        .map(|hit| hit.evidence_id.as_str())
        .collect::<Vec<_>>();

    assert_eq!(ids, ["ev-a", "ev-b"]);
}