relay-knowledge 1.1.17

Graph-database-based knowledge graph project.
Documentation
use rusqlite::{Connection, params};

use crate::storage::StorageError;

use super::{CandidateBudgets, visit_candidates, visit_candidates_with_budgets};

#[test]
fn candidate_stream_preserves_every_supported_path_shape_and_ordered_lines() {
    let connection = candidate_database();
    let supported = [
        ("Cargo.toml", "toml"),
        ("web/package.json", "json"),
        ("python/pyproject.toml", "toml"),
        ("service/go.mod", "gomod"),
        ("native/CMakeLists.txt", "cmake"),
        ("Makefile", "make"),
        ("gradle/build.gradle.kts", "kotlin"),
        (".github/workflows/ci.yml", "yaml"),
        ("nested/.gitlab-ci.yaml", "yaml"),
        ("images/Dockerfile", "dockerfile"),
        ("images/Dockerfile.dev", "dockerfile"),
        ("infra/main.tf", "hcl"),
        ("service/relay.service", "ini"),
        ("mac/relay.plist", "xml"),
        ("deploy/arbitrary-config", "yaml"),
        ("docs/architecture.MD", "markdown"),
        ("docs/design.mdx", "mdx"),
    ];
    for (index, (path, language)) in supported.iter().enumerate() {
        insert_chunk(
            &connection,
            "scope",
            &format!("supported-{index}"),
            path,
            language,
            "first\nsecond",
            1,
        );
    }
    insert_chunk(
        &connection,
        "scope",
        "cargo-tail",
        "Cargo.toml",
        "toml",
        "third",
        3,
    );
    for (index, path) in [
        "src/lib.rs",
        "docs/guide.txt",
        "nested/Dockerfilex",
        "github/workflows/not-root.yml.txt",
        "Cargo.toml.bak",
    ]
    .iter()
    .enumerate()
    {
        insert_chunk(
            &connection,
            "scope",
            &format!("ignored-{index}"),
            path,
            "rust",
            "ignored",
            1,
        );
    }
    let mut documents = Vec::new();

    let stats = visit_candidates(&connection, "scope", |document| {
        documents.push(document);
        Ok(())
    })
    .expect("candidate documents should stream");

    assert_eq!(stats.document_count, supported.len());
    assert_eq!(stats.chunk_count, supported.len() + 1);
    assert!(
        documents
            .iter()
            .all(|document| document.path != "src/lib.rs")
    );
    let cargo = documents
        .iter()
        .find(|document| document.path == "Cargo.toml")
        .expect("Cargo manifest should load");
    assert_eq!(
        cargo
            .lines
            .iter()
            .map(|line| (line.number, line.text.as_str()))
            .collect::<Vec<_>>(),
        vec![(1, "first"), (2, "second"), (3, "third")]
    );
}

#[test]
fn unrelated_source_chunks_do_not_cross_the_materialization_boundary() {
    let connection = candidate_database();
    for index in 0..2_000 {
        insert_chunk(
            &connection,
            "scope",
            &format!("source-{index:04}"),
            &format!("src/module_{index:04}.rs"),
            "rust",
            &"x".repeat(4_096),
            1,
        );
    }
    insert_chunk(
        &connection,
        "scope",
        "manifest",
        "Cargo.toml",
        "toml",
        "[package]\nname = \"bounded\"",
        1,
    );
    let mut paths = Vec::new();

    let stats = visit_candidates(&connection, "scope", |document| {
        paths.push(document.path);
        Ok(())
    })
    .expect("bounded candidate load should succeed");

    assert_eq!(paths, ["Cargo.toml"]);
    assert_eq!(stats.document_count, 1);
    assert_eq!(stats.chunk_count, 1);
    assert!(stats.materialized_bytes < 128);
}

#[test]
fn candidate_bytes_are_rejected_before_content_materialization() {
    let connection = candidate_database();
    connection
        .execute(
            "INSERT INTO code_repository_chunks (
                 repository_id, source_scope, chunk_id, path, language_id, content, line_start
             ) VALUES ('repo', 'scope', 'oversized', 'README.md', 'markdown', zeroblob(?1), 1)",
            params![1_025_i64],
        )
        .expect("oversized fixture should insert");

    let error = visit_candidates_with_budgets(
        &connection,
        "scope",
        CandidateBudgets {
            documents: 10,
            chunks: 10,
            bytes: 1_024,
        },
        |_| Ok(()),
    )
    .expect_err("candidate byte budget should reject oversized content");

    assert!(matches!(error, StorageError::CapacityExceeded(message)
        if message.contains("candidate content bytes")));
}

#[test]
fn candidate_chunk_and_document_limits_report_capacity_errors() {
    let connection = candidate_database();
    insert_chunk(&connection, "scope", "one", "Cargo.toml", "toml", "one", 1);
    insert_chunk(
        &connection,
        "scope",
        "two",
        "nested/Cargo.toml",
        "toml",
        "two",
        1,
    );

    let chunk_error = visit_candidates_with_budgets(
        &connection,
        "scope",
        CandidateBudgets {
            documents: 2,
            chunks: 1,
            bytes: 100,
        },
        |_| Ok(()),
    )
    .expect_err("chunk budget should reject cap plus one");
    assert!(
        matches!(chunk_error, StorageError::CapacityExceeded(message)
        if message.contains("candidate chunk count"))
    );

    let mut visits = 0_usize;
    let document_error = visit_candidates_with_budgets(
        &connection,
        "scope",
        CandidateBudgets {
            documents: 1,
            chunks: 2,
            bytes: 100,
        },
        |_| {
            visits += 1;
            Ok(())
        },
    )
    .expect_err("document budget should reject cap plus one");
    assert!(
        matches!(document_error, StorageError::CapacityExceeded(message)
        if message.contains("candidate document count"))
    );
    assert_eq!(visits, 0, "document overflow must fail in SQL preflight");
}

#[test]
fn inconsistent_candidate_chunk_identity_remains_invalid_data() {
    let connection = candidate_database();
    insert_chunk(&connection, "scope", "one", "Cargo.toml", "toml", "one", 1);
    connection
        .execute(
            "INSERT INTO code_repository_chunks (
                 repository_id, source_scope, chunk_id, path, language_id, content, line_start
             ) VALUES ('other-repo', 'scope', 'two', 'Cargo.toml', 'toml', 'two', 2)",
            [],
        )
        .expect("inconsistent chunk should insert");

    let error = visit_candidates(&connection, "scope", |_| Ok(()))
        .expect_err("inconsistent document identity should fail");
    assert!(matches!(error, StorageError::InvalidInput(message)
        if message.contains("inconsistent indexed chunk identity")));
}

fn candidate_database() -> Connection {
    let connection = Connection::open_in_memory().expect("sqlite should open");
    connection
        .execute_batch(
            "
            CREATE TABLE code_repository_chunks (
                repository_id TEXT NOT NULL,
                source_scope TEXT NOT NULL,
                chunk_id TEXT NOT NULL,
                path TEXT NOT NULL,
                language_id TEXT NOT NULL,
                content TEXT NOT NULL,
                line_start INTEGER NOT NULL
            );
            CREATE INDEX code_repository_chunks_lookup
                ON code_repository_chunks(source_scope, path);
            ",
        )
        .expect("candidate schema should initialize");
    connection
}

fn insert_chunk(
    connection: &Connection,
    source_scope: &str,
    chunk_id: &str,
    path: &str,
    language_id: &str,
    content: &str,
    line_start: u32,
) {
    connection
        .execute(
            "INSERT INTO code_repository_chunks (
                 repository_id, source_scope, chunk_id, path, language_id, content, line_start
             ) VALUES ('repo', ?1, ?2, ?3, ?4, ?5, ?6)",
            params![
                source_scope,
                chunk_id,
                path,
                language_id,
                content,
                line_start
            ],
        )
        .expect("chunk should insert");
}