relay-knowledge 1.1.16

Graph-database-based knowledge graph project.
Documentation
use std::{
    fs,
    path::PathBuf,
    process::Command,
    sync::Arc,
    time::{SystemTime, UNIX_EPOCH},
};

use relay_knowledge::{
    api::{
        CodeRepositoryFreshnessState, CodeRepositoryRegisterRequest, InterfaceKind, RequestContext,
    },
    application::{RelayKnowledgeService, RuntimeConfiguration},
    domain::{
        CodeGraphContextRequest, CodeIndexMode, CodeIndexRequest, CodeRepositorySelector,
        CodeRetrievalLayer, FreshnessPolicy,
    },
    env::{EnvironmentConfig, PlatformKind},
    storage::SqliteGraphStore,
};

#[tokio::test]
async fn codegraph_context_packs_entry_points_edges_excerpts_and_budget() {
    let repo = FixtureRepo::create("codegraph-context");
    repo.write(
        "src/lib.rs",
        r#"
pub fn retry_policy() -> u32 {
    3
}

pub fn retry_policy_v2() -> u32 {
    retry_policy()
}
"#,
    );
    repo.write(
        "src/main.rs",
        r#"
use crate::retry_policy;

fn run_worker() {
    retry_policy();
}
"#,
    );
    repo.git(["add", "."]);
    repo.git(["commit", "-m", "initial"]);
    let service = service_with_memory_store().await;

    service
        .register_code_repository(
            CodeRepositoryRegisterRequest {
                root_path: repo.path.display().to_string(),
                alias: "fixture".to_owned(),
                path_filters: vec!["src".to_owned()],
                language_filters: Vec::new(),
            },
            context("register-context"),
        )
        .await
        .expect("repository should register");
    service
        .index_code_repository(
            CodeIndexRequest {
                repository: selector("fixture", "HEAD"),
                mode: CodeIndexMode::Full,
                workspace_detection: Default::default(),
                freshness_policy: FreshnessPolicy::WaitUntilFresh,
                reuse_historical: false,
            },
            context("index-context"),
        )
        .await
        .expect("repository should index");

    let response = service
        .codegraph_context(
            CodeGraphContextRequest::new(
                selector("fixture", "HEAD"),
                "retry_policy",
                5,
                FreshnessPolicy::AllowStale,
                16_384,
                true,
                false,
            )
            .expect("context request should validate"),
            context("context"),
        )
        .await
        .expect("context should build");

    assert_eq!(response.repository_scope.alias, "fixture");
    assert_eq!(
        response.freshness.state,
        CodeRepositoryFreshnessState::Fresh
    );
    assert!(response.budget.context_bytes <= response.budget.max_context_bytes);
    assert!(response.budget.candidate_count >= response.pack.entry_points.len());
    assert!(response.retrieval_layers.iter().any(|layer| matches!(
        layer,
        CodeRetrievalLayer::Definition | CodeRetrievalLayer::Symbol
    )));
    assert!(
        response
            .pack
            .entry_points
            .iter()
            .any(|hit| hit.path == "src/lib.rs")
    );
    assert!(
        !response.pack.related_symbols.is_empty() || !response.pack.graph_paths.is_empty(),
        "context should include structural expansion evidence: {:?}",
        response.pack
    );
    assert!(
        response
            .pack
            .code_excerpts
            .iter()
            .any(|excerpt| excerpt.excerpt.contains("retry_policy"))
    );

    let no_code = service
        .codegraph_context(
            CodeGraphContextRequest::new(
                selector("fixture", "HEAD"),
                "retry_policy",
                5,
                FreshnessPolicy::AllowStale,
                16_384,
                false,
                false,
            )
            .expect("context request should validate"),
            context("context-no-code"),
        )
        .await
        .expect("context should build without code excerpts");

    assert!(no_code.pack.code_excerpts.is_empty());
    assert!(
        no_code
            .pack
            .entry_points
            .iter()
            .chain(&no_code.pack.related_symbols)
            .chain(&no_code.pack.graph_paths)
            .all(|hit| hit.excerpt.is_empty())
    );
}

async fn service_with_memory_store() -> RelayKnowledgeService {
    let environment = EnvironmentConfig::from_pairs(
        PlatformKind::Unix,
        [
            ("HOME", "/home/alice"),
            ("TMPDIR", "/tmp"),
            ("RELAY_KNOWLEDGE_HOME", "/srv/relay"),
        ],
    )
    .expect("environment should parse");
    let runtime = RuntimeConfiguration::from_environment(&environment)
        .await
        .expect("runtime should compose");
    RelayKnowledgeService::with_store(
        runtime,
        Arc::new(SqliteGraphStore::open_in_memory().expect("store should open")),
    )
}

fn selector(alias: &str, ref_selector: &str) -> CodeRepositorySelector {
    CodeRepositorySelector::new(alias, ref_selector, Vec::new(), Vec::new())
        .expect("selector should validate")
}

fn context(name: &str) -> RequestContext {
    RequestContext::with_ids(
        InterfaceKind::Cli,
        format!("req-{name}"),
        format!("trace-{name}"),
    )
}

struct FixtureRepo {
    path: PathBuf,
}

impl FixtureRepo {
    fn create(name: &str) -> Self {
        let nanos = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("clock should be after epoch")
            .as_nanos();
        let path = std::env::temp_dir().join(format!("relay-knowledge-{name}-{nanos}"));
        fs::create_dir_all(path.join("src")).expect("repo directory should be created");
        let repo = Self { path };
        repo.git(["init"]);
        repo.git(["config", "user.email", "relay@example.invalid"]);
        repo.git(["config", "user.name", "Relay Test"]);
        repo
    }

    fn write(&self, relative: &str, content: &str) {
        let path = self.path.join(relative);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).expect("parent directory should exist");
        }
        fs::write(path, content).expect("fixture file should be written");
    }

    fn git<const N: usize>(&self, args: [&str; N]) {
        let status = Command::new("git")
            .args(args)
            .current_dir(&self.path)
            .status()
            .expect("git command should run");
        assert!(status.success(), "git command should succeed");
    }
}