weavatrix-rust 1.0.2

Native repository intelligence for coding agents: 39 read-only MCP tools, evidence graph, cross-repository analysis, and lossless parsing
Documentation
use blazingly_json::json;
#[cfg(all(
    feature = "clone",
    feature = "git",
    feature = "memory",
    feature = "search",
    feature = "semantic",
    feature = "vector"
))]
use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use weavatrix_rust::{Weavatrix, tools};

#[test]
#[cfg(all(
    feature = "clone",
    feature = "git",
    feature = "memory",
    feature = "search",
    feature = "semantic",
    feature = "vector"
))]
fn catalog_covers_the_javascript_read_only_core_and_rust_extensions() {
    let actual = tools::catalog()
        .into_iter()
        .map(|tool| tool.name)
        .collect::<BTreeSet<_>>();
    for expected in [
        "graph_stats",
        "get_node",
        "get_neighbors",
        "query_graph",
        "god_nodes",
        "shortest_path",
        "get_dependents",
        "change_impact",
        "git_history",
        "cross_repo_git",
        "verified_change",
        "trace_api_contract",
        "get_community",
        "search_code",
        "read_source",
        "inspect_symbol",
        "context_bundle",
        "find_duplicates",
        "find_dead_code",
        "run_audit",
        "coverage_map",
        "hot_path_review",
        "list_communities",
        "module_map",
        "list_endpoints",
        "trace_endpoint",
        "rebuild_graph",
        "graph_diff",
        "get_architecture_contract",
        "prepare_change",
        "verify_architecture",
        "explain_architecture_violation",
        "propose_architecture_exception",
        "open_repo",
        "list_known_repos",
        "semantic_link",
        "vector_search",
        "seo_link_suggestions",
        "memory_context",
    ] {
        assert!(actual.contains(expected), "missing tool {expected}");
    }
}

#[test]
#[cfg(all(feature = "memory", feature = "semantic"))]
fn catalog_exposes_real_argument_contracts_and_profiles() {
    let catalog = tools::catalog();
    let query = catalog
        .iter()
        .find(|tool| tool.name == "query_graph")
        .unwrap();
    assert_eq!(query.input_schema["properties"]["depth"]["type"], "integer");
    assert_eq!(
        query.input_schema["properties"]["seed_files"]["items"]["type"],
        "string"
    );
    let memory = catalog
        .iter()
        .find(|tool| tool.name == "memory_context")
        .unwrap();
    assert_eq!(
        memory.input_schema["required"],
        json!(["events", "request"])
    );

    let code = tools::catalog_for_profile(weavatrix_rust::mcp::McpProfile::Code);
    assert!(code.iter().all(|tool| tool.name != "seo_link_suggestions"));
    let seo = tools::catalog_for_profile(weavatrix_rust::mcp::McpProfile::Seo);
    assert!(seo.iter().any(|tool| tool.name == "seo_link_suggestions"));
    assert!(seo.iter().all(|tool| tool.name != "verified_change"));
}

#[test]
#[cfg(all(
    feature = "lang-rust",
    feature = "search",
    feature = "semantic",
    feature = "vector"
))]
fn graph_search_source_and_semantic_tools_work_together() {
    let fixture = Fixture::new();
    fixture.write(
        "src/lib.rs",
        "pub fn alpha() { beta(); }\npub fn beta() {}\n",
    );
    fixture.write(
        "web/router.js",
        "function list() {}\nrouter.get(\"/items\", list);\n",
    );
    let mut engine = Weavatrix::open(&fixture.root).unwrap();

    let stats = tools::call(&mut engine, "graph_stats", json!({})).unwrap();
    assert!(stats["nodes"].as_u64().unwrap() >= 6);
    let search = tools::call(
        &mut engine,
        "search_code",
        json!({"query": "beta", "max_results": 10}),
    )
    .unwrap();
    assert!(search["occurrences"].as_u64().unwrap() >= 2);
    let endpoints = tools::call(&mut engine, "list_endpoints", json!({})).unwrap();
    assert_eq!(endpoints["endpoints"][0]["label"], "GET /items");

    let nodes = engine
        .state()
        .graph()
        .nodes()
        .iter()
        .filter(|node| node.label == "alpha" || node.label == "beta")
        .map(|node| node.id.to_string())
        .collect::<Vec<_>>();
    let semantic = tools::call(
        &mut engine,
        "semantic_link",
        json!({
            "min_similarity": 0.5,
            "vectors": [
                {"node": nodes[0], "values": [1.0, 0.0]},
                {"node": nodes[1], "values": [0.9, 0.1]}
            ]
        }),
    )
    .unwrap();
    assert!(
        semantic["edges"]
            .as_array()
            .is_some_and(|edges| !edges.is_empty())
    );

    let vector = tools::call(
        &mut engine,
        "vector_search",
        json!({
            "vectors": [
                {"node": "alpha", "values": [1.0, 0.0]},
                {"node": "beta", "values": [0.0, 1.0]}
            ],
            "query": [0.9, 0.1],
            "top_k": 1,
            "exact": true
        }),
    )
    .unwrap();
    assert_eq!(vector["hits"][0]["node"], "alpha");
}

#[test]
#[cfg(feature = "search")]
fn search_code_bounds_returned_matches_without_losing_totals() {
    let fixture = Fixture::new();
    fixture.write("src/many.rs", "needle\nneedle\nneedle\nneedle\nneedle\n");
    let mut engine = Weavatrix::open(&fixture.root).unwrap();

    let search = tools::call(
        &mut engine,
        "search_code",
        json!({"query": "needle", "max_results": 2}),
    )
    .unwrap();

    assert_eq!(search["matches"].as_array().map(Vec::len), Some(2));
    assert_eq!(search["returned_matches"], 2);
    assert_eq!(search["totals"]["returned_matches"], 2);
    assert_eq!(search["matching_lines"], 5);
    assert_eq!(search["totals"]["matching_lines"], 5);
    assert_eq!(search["occurrences"], 5);
    assert_eq!(search["totals"]["occurrences"], 5);
    assert_eq!(search["truncated"], true);
}

#[test]
fn rust_tarpaulin_coverage_is_measured_not_static_reachability() {
    let fixture = Fixture::new();
    fixture.write("src/lib.rs", "pub fn covered() {}\n");
    fixture.write(
        "tarpaulin-report.json",
        r#"{
          "files": [{
            "path": ["src", "lib.rs"],
            "content": "pub fn covered() {}",
            "traces": [],
            "covered": 1,
            "coverable": 1
          }],
          "coverage": 100.0,
          "covered": 1,
          "coverable": 1
        }"#,
    );
    let mut engine = Weavatrix::open(&fixture.root).unwrap();
    let coverage = tools::call(&mut engine, "coverage_map", json!({})).unwrap();
    assert_eq!(coverage["status"], "COMPLETE");
    assert_eq!(coverage["measured_coverage"]["present"], true);
    assert_eq!(coverage["files"][0]["lines_hit"], 1);
    assert_eq!(coverage["files"][0]["lines_found"], 1);
}

#[test]
fn audit_compares_external_imports_with_supported_manifests() {
    let fixture = Fixture::new();
    fixture.write("package.json", r#"{"dependencies":{"lodash":"1.0.0"}}"#);
    fixture.write(
        "src/server.ts",
        "import express from \"express\";\nexport const app = express();\n",
    );
    let mut engine = Weavatrix::open(&fixture.root).unwrap();
    let audit = tools::call(&mut engine, "run_audit", json!({})).unwrap();
    let findings = audit["dependency_report"]["findings"].as_array().unwrap();
    assert!(
        findings
            .iter()
            .any(|finding| finding["id"] == "dependency.missing:typescript:express")
    );
    assert!(findings.iter().any(|finding| {
        finding["rule"] == "dependency.unused_declaration" && finding["package"] == "lodash"
    }));
}

#[test]
#[cfg(feature = "lang-rust")]
fn audit_matches_hyphenated_cargo_dependencies_to_grouped_rust_uses() {
    let fixture = Fixture::new();
    fixture.write(
        "Cargo.toml",
        "[package]\nname = \"fixture\"\nversion = \"0.1.0\"\n\n[dependencies]\nblazingly-json = \"0.1.0\"\n",
    );
    fixture.write(
        "src/lib.rs",
        "use blazingly_json::{Value, json};\npub fn value() -> Value { json!({\"ok\": true}) }\n",
    );

    let mut engine = Weavatrix::open(&fixture.root).unwrap();
    let audit = tools::call(&mut engine, "run_audit", json!({})).unwrap();
    let findings = audit["dependency_report"]["findings"].as_array().unwrap();

    assert!(
        !findings.iter().any(|finding| {
            finding["rule"] == "dependency.unused_declaration"
                && finding["package"] == "blazingly-json"
        }),
        "grouped use must count as exact Cargo import evidence, got {findings:?}"
    );
}

#[test]
#[cfg(feature = "lang-rust")]
fn incremental_refresh_rebuilds_only_after_source_changes() {
    let fixture = Fixture::new();
    fixture.write("src/lib.rs", "pub fn first() {}\n");
    let mut engine = Weavatrix::open(&fixture.root).unwrap();
    assert!(!engine.refresh_if_stale().unwrap());
    let before = engine.state().graph().node_count();

    fixture.write("src/lib.rs", "pub fn first() {}\npub fn second() {}\n");
    assert!(engine.refresh_if_stale().unwrap());
    assert!(engine.state().graph().node_count() > before);
}

struct Fixture {
    root: PathBuf,
}

impl Fixture {
    fn new() -> Self {
        let nonce = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let root =
            std::env::temp_dir().join(format!("weavatrix-tools-{}-{nonce}", std::process::id()));
        fs::create_dir_all(&root).unwrap();
        Self { root }
    }

    fn write(&self, relative: &str, contents: &str) {
        let path = self.root.join(relative);
        fs::create_dir_all(path.parent().unwrap_or(Path::new("."))).unwrap();
        fs::write(path, contents).unwrap();
    }
}

impl Drop for Fixture {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.root);
    }
}