relay-knowledge 1.1.14

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

use serde_json::Value;

const ALIAS: &str = "knowledge-loop-fixture";

#[test]
fn bootstrap_binds_business_software_and_context_to_one_indexed_commit() {
    let mut fixture = AcceptanceFixture::create();

    let initialized = fixture.cli(["map", "init", "--format", "json"]);
    let initial_version = initialized["map_version"]
        .as_u64()
        .expect("map init should report its version");
    let repeated = fixture.cli(["map", "init", "--format", "json"]);
    assert_eq!(repeated["map_version"], initial_version);
    write(
        &fixture.repository,
        ".knowledge/business-glossary.yaml",
        r#"schema_version: 1
domains:
  - id: revenue
    name: Revenue
    description: Subscription revenue concepts.
terms:
  - id: monthly-recurring-revenue
    domain: revenue
    canonical_name: Monthly Recurring Revenue
    definition: Recurring subscription revenue normalized to one month.
    language: en
    aliases:
      - value: MRR
        kind: abbreviation
        language: en
    semantics:
      aggregation: sum
      unit: USD
      grain: subscription
      time_basis: month
    mappings:
      - relation: calculated_from
        target_kind: file
        target: src/lib.rs
"#,
    );
    git(&fixture.repository, ["add", ".knowledge"]);
    git(
        &fixture.repository,
        ["commit", "-m", "author business glossary"],
    );
    fixture.commit = git_text(&fixture.repository, ["rev-parse", "HEAD"]);

    let route = fixture.cli(["map", "route", "software-model", "--format", "json"]);
    assert_eq!(
        route["route"]["source_order"],
        serde_json::json!(["repository-software-model"])
    );
    assert_eq!(route["sources"][0]["kind"], "repo");
    assert_eq!(route["sources"][0]["uri"], ".");
    assert_eq!(route["sources"][0]["source_scope"], "repo");
    let business_route = fixture.cli(["map", "route", "business-knowledge", "--format", "json"]);
    assert_eq!(
        business_route["route"]["source_order"],
        serde_json::json!(["repository-business-glossary"])
    );

    fixture.cli([
        "repo",
        "register",
        fixture.repository_text(),
        "--alias",
        ALIAS,
        "--format",
        "json",
    ]);
    let indexed = fixture.cli(["repo", "index", ALIAS, "--ref", "HEAD", "--format", "json"]);
    assert_eq!(indexed["scope"]["resolved_commit_sha"], fixture.commit);
    assert_eq!(indexed["status"]["stale"], false);
    assert_eq!(indexed["checkpoint"]["state"], "completed");

    let software = fixture.cli([
        "repo",
        "software",
        ALIAS,
        "--kind",
        "all",
        "--ref",
        &fixture.commit,
        "--freshness",
        "wait-until-fresh",
        "--format",
        "json",
    ]);
    let architecture = fixture.cli([
        "repo",
        "view",
        ALIAS,
        "--kind",
        "architecture-layers",
        "--ref",
        &fixture.commit,
        "--freshness",
        "wait-until-fresh",
        "--format",
        "json",
    ]);
    let business = fixture.cli([
        "repo",
        "business",
        ALIAS,
        "--kind",
        "all",
        "--query",
        "MRR",
        "--ref",
        &fixture.commit,
        "--freshness",
        "wait-until-fresh",
        "--format",
        "json",
    ]);
    let business_domains = fixture.cli([
        "repo",
        "view",
        ALIAS,
        "--kind",
        "business-domains",
        "--ref",
        &fixture.commit,
        "--freshness",
        "wait-until-fresh",
        "--format",
        "json",
    ]);
    let context = fixture.cli([
        "repo",
        "context",
        ALIAS,
        "--query",
        "MRR",
        "--ref",
        &fixture.commit,
        "--freshness",
        "wait-until-fresh",
        "--format",
        "json",
    ]);

    let indexed_scope = indexed["scope"]["scope_id"]
        .as_str()
        .expect("index should report a source scope");
    for response in [&software, &architecture, &business, &business_domains] {
        assert_eq!(response["scope"]["scope_id"], indexed_scope);
        assert_eq!(response["scope"]["resolved_commit_sha"], fixture.commit);
        assert_eq!(response["scope"]["stale"], false);
    }
    assert_eq!(context["repository_scope"]["scope_id"], indexed_scope);
    assert_eq!(
        context["repository_scope"]["resolved_commit_sha"],
        fixture.commit
    );
    assert_eq!(context["repository_scope"]["stale"], false);
    assert_eq!(software["status"]["source_scope"], indexed_scope);
    assert_eq!(software["status"]["stale"], false);
    assert_eq!(business["status"]["source_scope"], indexed_scope);
    assert_eq!(business["status"]["resolved_commit_sha"], fixture.commit);
    assert_eq!(business["resolution"], "exact");
    assert_eq!(
        business["terms"][0]["canonical_name"],
        "Monthly Recurring Revenue"
    );
    assert_eq!(
        business["terms"][0]["mappings"][0]["resolution_state"],
        "resolved"
    );
    assert_eq!(
        business["terms"][0]["definitions"][0]["evidence"]["resolved_commit_sha"],
        fixture.commit
    );
    assert_eq!(
        context["business_context"][0]["id"],
        "monthly-recurring-revenue"
    );
    assert!(
        business_domains["evidence"]
            .as_array()
            .is_some_and(|evidence| {
                evidence
                    .iter()
                    .any(|item| item["evidence_kind"] == "business_glossary")
            })
    );
    assert!(
        software["files"]
            .as_array()
            .is_some_and(|files| !files.is_empty())
    );
    assert!(
        software["sdk_usages"].as_array().is_some_and(|usages| {
            usages.iter().any(|usage| {
                usage["resolution_state"] == "unresolved"
                    && usage["target_hint"]
                        .as_str()
                        .is_some_and(|hint| hint.contains("vendor_sdk"))
                    && usage["evidence_path"] == "src/lib.rs"
            })
        }),
        "software projection should retain unresolved SDK metadata: {}",
        software["sdk_usages"]
    );
    assert!(
        architecture["evidence"]
            .as_array()
            .is_some_and(|evidence| !evidence.is_empty())
    );

    let validated = fixture.cli(["map", "validate", "--format", "json"]);
    assert_eq!(
        validated["valid"], true,
        "final map validation should succeed: {validated}"
    );
    assert_eq!(validated["diagnostics"], serde_json::json!([]));
}

struct AcceptanceFixture {
    repository: PathBuf,
    runtime: PathBuf,
    commit: String,
}

impl AcceptanceFixture {
    fn create() -> Self {
        let nonce = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("clock should be after the epoch")
            .as_nanos();
        let root = std::env::temp_dir().join(format!("relay-knowledge-loop-{nonce}"));
        let repository = root.join("repository");
        let runtime = root.join("runtime");
        fs::create_dir_all(repository.join("src")).expect("source directory should exist");
        fs::create_dir_all(repository.join("docs")).expect("docs directory should exist");
        fs::create_dir_all(&runtime).expect("runtime directory should exist");

        write(
            &repository,
            "Cargo.toml",
            "[package]\nname = \"knowledge-loop-fixture\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\nserde = \"1\"\n",
        );
        write(
            &repository,
            "src/lib.rs",
            "use vendor_sdk::Client;\n\npub fn client() -> Option<Client> { None }\n",
        );
        write(
            &repository,
            "docs/architecture.md",
            "# Architecture\n\nThe application layer consumes an external SDK boundary.\n",
        );
        write(
            &repository,
            "AGENTS.md",
            "Knowledge map: .knowledge/knowledge-map.yaml\n",
        );
        git(&repository, ["init"]);
        git(
            &repository,
            ["config", "user.email", "relay@example.invalid"],
        );
        git(&repository, ["config", "user.name", "Relay Test"]);
        git(&repository, ["add", "."]);
        git(&repository, ["commit", "-m", "initial knowledge fixture"]);
        let commit = git_text(&repository, ["rev-parse", "HEAD"]);

        Self {
            repository,
            runtime,
            commit,
        }
    }

    fn repository_text(&self) -> &str {
        self.repository
            .to_str()
            .expect("fixture repository path should be UTF-8")
    }

    fn cli<const N: usize>(&self, args: [&str; N]) -> Value {
        let mut command = Command::new(env!("CARGO_BIN_EXE_relay-knowledge"));
        command
            .current_dir(&self.repository)
            .env_clear()
            .env("HOME", self.runtime.join("home"))
            .env("TMPDIR", self.runtime.join("tmp"))
            .env("RELAY_KNOWLEDGE_HOME", self.runtime.join("relay"))
            .env("RELAY_KNOWLEDGE_SEMANTIC_BACKEND", "local")
            .env("RELAY_KNOWLEDGE_VECTOR_BACKEND", "local")
            .args(args);
        if let Some(path) = std::env::var_os("PATH") {
            command.env("PATH", path);
        }
        let output = command.output().expect("relay-knowledge should run");
        assert!(
            output.status.success(),
            "relay-knowledge failed: {}\n{}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );
        serde_json::from_slice(&output.stdout).expect("relay-knowledge stdout should be JSON")
    }
}

impl Drop for AcceptanceFixture {
    fn drop(&mut self) {
        let root = self
            .repository
            .parent()
            .expect("fixture repository should have a root");
        let _ = fs::remove_dir_all(root);
    }
}

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

fn git<const N: usize>(repository: &Path, args: [&str; N]) {
    let output = Command::new("git")
        .current_dir(repository)
        .args(args)
        .output()
        .expect("git should run");
    assert!(
        output.status.success(),
        "git failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
}

fn git_text<const N: usize>(repository: &Path, args: [&str; N]) -> String {
    let output = Command::new("git")
        .current_dir(repository)
        .args(args)
        .output()
        .expect("git should run");
    assert!(output.status.success());
    String::from_utf8(output.stdout)
        .expect("git output should be UTF-8")
        .trim()
        .to_owned()
}