relay-knowledge 1.1.17

Graph-database-based knowledge graph project.
Documentation
// Direct tests for context expansion, budgeting, and provenance.

use super::*;
use crate::{
    api::{CodeRepositoryIndexLag, CodeRepositoryPendingIndexWork, CodeRepositoryScopeMetadata},
    domain::{CodeRepositorySelector, FreshnessPolicy, RepositoryCodeRange},
};

#[test]
fn context_roles_preserve_edge_kind_and_drive_provenance_and_hints() {
    let mut hit = call_graph_hit();
    hit.edge_kind = Some("call".to_owned());
    let mut roles = HashMap::new();
    remember_context_roles(&mut roles, CodeQueryKind::Callees, &[hit.clone()]);
    let excerpts = code_excerpts(true, &[], &[], &[hit.clone()], &roles);
    let hints = impact_hints(&[hit.clone()], &roles);

    assert_eq!(hit.edge_kind.as_deref(), Some("call"));
    assert_eq!(provenance_kind(&hit, &roles), CodeQueryKind::Callees);
    assert_eq!(excerpts[0].provenance.query_kind, CodeQueryKind::Callees);
    assert_eq!(hints[0].relationship, "callee");
}

#[test]
fn count_truncation_reports_when_unique_hits_exceed_limit() {
    let mut hits = vec![call_graph_hit(), call_graph_hit_at("src/main.rs")];

    assert!(truncate_hits(&mut hits, 1));
    assert_eq!(hits.len(), 1);
}

#[test]
fn pinned_context_request_uses_primary_served_commit_for_followups() {
    let request = CodeGraphContextRequest::new(
        CodeRepositorySelector::new("repo", "HEAD", Vec::new(), Vec::new()).unwrap(),
        "retry policy",
        3,
        FreshnessPolicy::AllowStale,
        1024,
        true,
        false,
    )
    .unwrap();

    let pinned = pinned_context_request(&request, &scope_metadata("commit-a"));

    assert_eq!(pinned.repository.ref_selector, "commit-a");
    assert_eq!(request.repository.ref_selector, "HEAD");
}

#[test]
fn context_freshness_merges_degraded_expansion_state_and_reason() {
    let primary = freshness(CodeRepositoryFreshnessState::Fresh, None, Vec::new());
    let degraded = freshness(
        CodeRepositoryFreshnessState::Degraded,
        Some("parser degraded"),
        vec!["src/lib.rs".to_owned()],
    );

    let merged = merge_context_freshness(primary, vec![degraded]);

    assert_eq!(merged.state, CodeRepositoryFreshnessState::Degraded);
    assert_eq!(merged.degraded_reason.as_deref(), Some("parser degraded"));
    assert_eq!(merged.direct_source_read_paths, ["src/lib.rs"]);
}

#[test]
fn budget_truncation_keeps_impact_hints_aligned_with_graph_paths() {
    let graph_paths = (0..12)
        .map(|index| call_graph_hit_at(&format!("src/path_{index}.rs")))
        .collect::<Vec<_>>();
    let mut pack = CodeGraphContextPack {
        business_context: Vec::new(),
        entry_points: Vec::new(),
        related_symbols: Vec::new(),
        impact_hints: impact_hints(&graph_paths, &HashMap::new()),
        code_excerpts: Vec::new(),
        graph_paths,
    };

    assert!(pack_to_budget(&mut pack, 1024, &HashMap::new()));
    assert!(serialized_context_bytes(&pack) <= 1024);
    assert_eq!(pack.impact_hints.len(), pack.graph_paths.len());
    for hint in &pack.impact_hints {
        assert!(pack.graph_paths.iter().any(|hit| hit.path == hint.path));
    }
}

#[test]
fn budget_truncation_clears_hit_excerpts_before_dropping_evidence() {
    let mut hit = call_graph_hit();
    hit.excerpt = "x".repeat(5000);
    let mut pack = CodeGraphContextPack {
        business_context: Vec::new(),
        entry_points: vec![hit],
        related_symbols: Vec::new(),
        graph_paths: Vec::new(),
        impact_hints: Vec::new(),
        code_excerpts: code_excerpts(true, &[call_graph_hit()], &[], &[], &HashMap::new()),
    };

    assert!(pack_to_budget(&mut pack, 1024, &HashMap::new()));
    assert_eq!(pack.entry_points.len(), 1);
    assert!(pack.entry_points[0].excerpt.is_empty());
    assert!(serialized_context_bytes(&pack) <= 1024);
}

#[test]
fn budget_truncation_preserves_entry_excerpt_before_expansion_evidence() {
    let mut entry = call_graph_hit_at("src/context.rs");
    entry.excerpt = "pub struct AgentContextPackBuilder;".to_owned();
    let graph_paths = (0..8)
        .map(|index| {
            let mut hit = call_graph_hit_at(&format!("src/expansion_{index}.rs"));
            hit.excerpt = "x".repeat(3000);
            hit
        })
        .collect::<Vec<_>>();
    let mut pack = CodeGraphContextPack {
        business_context: Vec::new(),
        entry_points: vec![entry],
        related_symbols: Vec::new(),
        impact_hints: impact_hints(&graph_paths, &HashMap::new()),
        code_excerpts: Vec::new(),
        graph_paths,
    };

    assert!(pack_to_budget(&mut pack, 2048, &HashMap::new()));
    assert_eq!(pack.entry_points.len(), 1);
    assert!(
        pack.entry_points[0]
            .excerpt
            .contains("AgentContextPackBuilder")
    );
    assert!(serialized_context_bytes(&pack) <= 2048);
}

#[test]
fn expansion_queries_carry_inline_scope_filters_without_kind_or_name_terms() {
    let source = CodeRetrievalRequest::new(
        "path:src lang:rust name:Retry kind:function retry policy",
        CodeRepositorySelector::new("repo", "HEAD", Vec::new(), Vec::new()).unwrap(),
        CodeQueryKind::Hybrid,
        3,
        crate::domain::FreshnessPolicy::AllowStale,
    )
    .unwrap();
    let mut target = CodeRetrievalRequest::new(
        "RetryPolicy",
        CodeRepositorySelector::new("repo", "HEAD", Vec::new(), Vec::new()).unwrap(),
        CodeQueryKind::References,
        3,
        crate::domain::FreshnessPolicy::AllowStale,
    )
    .unwrap();

    carry_context_filters(&mut target, &source);

    assert_eq!(target.query_path_substrings, ["src"]);
    assert_eq!(target.query_language_filters, ["rust"]);
    assert!(target.query_kind_filters.is_empty());
    assert!(target.query_name_substrings.is_empty());
}

fn call_graph_hit() -> CodeRetrievalHit {
    call_graph_hit_at("src/lib.rs")
}

fn call_graph_hit_at(path: &str) -> CodeRetrievalHit {
    CodeRetrievalHit {
        repository_id: "repo".to_owned(),
        scope_id: "scope".to_owned(),
        resolved_commit_sha: "commit".to_owned(),
        tree_hash: "tree".to_owned(),
        path: path.to_owned(),
        language_id: "rust".to_owned(),
        byte_range: RepositoryCodeRange { start: 0, end: 1 },
        line_range: RepositoryCodeRange { start: 1, end: 1 },
        symbol_snapshot_id: None,
        canonical_symbol_id: None,
        file_id: None,
        retrieval_layers: vec![CodeRetrievalLayer::CallGraph],
        index_versions: Vec::new(),
        stale: false,
        staleness_hint: None,
        degraded_reason: None,
        edge_kind: None,
        edge_resolution_state: None,
        edge_target_hint: None,
        edge_confidence_basis_points: None,
        edge_confidence_tier: None,
        score: 1.0,
        excerpt: "call();".to_owned(),
    }
}

fn scope_metadata(resolved_commit_sha: &str) -> CodeRepositoryScopeMetadata {
    CodeRepositoryScopeMetadata {
        scope_id: "scope".to_owned(),
        repository_id: "repo".to_owned(),
        alias: "repo".to_owned(),
        requested_ref: "HEAD".to_owned(),
        resolved_commit_sha: resolved_commit_sha.to_owned(),
        tree_hash: "tree".to_owned(),
        path_filters: Vec::new(),
        language_filters: Vec::new(),
        indexed_file_count: 1,
        index_versions: Vec::new(),
        stale: false,
    }
}

fn freshness(
    state: CodeRepositoryFreshnessState,
    degraded_reason: Option<&str>,
    direct_source_read_paths: Vec<String>,
) -> CodeRepositoryFreshnessDiagnostics {
    CodeRepositoryFreshnessDiagnostics {
        state,
        freshness_policy: FreshnessPolicy::AllowStale,
        graph_version: 1,
        source_scope: Some("scope".to_owned()),
        scope_stale: matches!(
            state,
            CodeRepositoryFreshnessState::Stale | CodeRepositoryFreshnessState::Pending
        ),
        stale_reason: None,
        degraded_reason: degraded_reason.map(str::to_owned),
        index_lag: CodeRepositoryIndexLag {
            requested_ref: "HEAD".to_owned(),
            requested_resolved_ref: "commit".to_owned(),
            served_ref: "commit".to_owned(),
            requested_ref_indexed: true,
            pending_file_count: None,
            pending_task_count: 0,
        },
        pending: CodeRepositoryPendingIndexWork::default(),
        cursor: None,
        direct_source_read_required: false,
        direct_source_read_paths,
        agent_instructions: Vec::new(),
    }
}