tessera-codegraph 0.8.0

A local, deterministic semantic code graph and MCP server for AI coding agents. 11 languages, personalized PageRank impact, hallucination validator, call-path tracing, graph export, incremental indexing.
Documentation
use std::fs;

use assert_cmd::Command;
use rusqlite::Connection;
use tempfile::TempDir;

#[test]
fn config_include_exclude_and_extra_ignores_shape_index() {
    let temp = TempDir::new().unwrap();
    fs::create_dir_all(temp.path().join(".tessera")).unwrap();
    fs::create_dir_all(temp.path().join("src/generated")).unwrap();
    fs::create_dir_all(temp.path().join("vendor")).unwrap();
    fs::write(
        temp.path().join(".tessera/config.toml"),
        r#"
[include]
paths = ["src/"]

[exclude]
paths = ["src/generated/"]

[ignore]
extra = ["vendor"]
"#,
    )
    .unwrap();
    fs::write(
        temp.path().join("src/app.ts"),
        "export function keepMe() { return 1; }\n",
    )
    .unwrap();
    fs::write(
        temp.path().join("src/generated/app.ts"),
        "export function generated() { return 1; }\n",
    )
    .unwrap();
    fs::write(
        temp.path().join("vendor/app.ts"),
        "export function vendored() { return 1; }\n",
    )
    .unwrap();

    let db = temp.path().join(".tessera/config.db");
    Command::cargo_bin("tessera")
        .unwrap()
        .args([
            "index",
            temp.path().to_str().unwrap(),
            "--db",
            db.to_str().unwrap(),
            "--full",
        ])
        .assert()
        .success();

    let conn = Connection::open(db).unwrap();
    let symbols: Vec<String> = conn
        .prepare("SELECT qualified_name FROM symbols ORDER BY qualified_name")
        .unwrap()
        .query_map([], |row| row.get::<_, String>(0))
        .unwrap()
        .collect::<rusqlite::Result<_>>()
        .unwrap();

    assert_eq!(symbols, vec!["keepMe"]);
}

#[test]
fn unreadable_utf8_source_warns_without_aborting_index() {
    let temp = TempDir::new().unwrap();
    fs::write(
        temp.path().join("ok.ts"),
        "export function stillIndexes() { return 1; }\n",
    )
    .unwrap();
    fs::write(temp.path().join("bad.ts"), [0xff, 0xfe, 0xfd]).unwrap();

    let db = temp.path().join(".tessera/warn.db");
    Command::cargo_bin("tessera")
        .unwrap()
        .args([
            "index",
            temp.path().to_str().unwrap(),
            "--db",
            db.to_str().unwrap(),
            "--full",
        ])
        .assert()
        .success()
        .stderr(predicates::str::contains("warning"))
        .stderr(predicates::str::contains("bad.ts"));

    let conn = Connection::open(db).unwrap();
    let count: i64 = conn
        .query_row(
            "SELECT COUNT(*) FROM symbols WHERE qualified_name = 'stillIndexes'",
            [],
            |row| row.get(0),
        )
        .unwrap();
    assert_eq!(count, 1);
}

#[test]
fn fixture_snapshot_covers_symbols_refs_imports_edges_and_exports() {
    let temp = TempDir::new().unwrap();
    fs::write(
        temp.path().join("util.ts"),
        "export function helper() { return 1; }\n",
    )
    .unwrap();
    fs::write(
        temp.path().join("app.ts"),
        r#"import { helper } from "./util";

export function entry() {
    return helper();
}
"#,
    )
    .unwrap();

    let db = temp.path().join(".tessera/snapshot.db");
    Command::cargo_bin("tessera")
        .unwrap()
        .args([
            "index",
            temp.path().to_str().unwrap(),
            "--db",
            db.to_str().unwrap(),
            "--full",
        ])
        .assert()
        .success();

    let conn = Connection::open(db).unwrap();
    let symbols: Vec<(String, String, bool)> = conn
        .prepare(
            "
            SELECT qualified_name, kind, exported
            FROM symbols
            ORDER BY qualified_name
            ",
        )
        .unwrap()
        .query_map([], |row| {
            Ok((
                row.get::<_, String>(0)?,
                row.get::<_, String>(1)?,
                row.get::<_, i64>(2)? != 0,
            ))
        })
        .unwrap()
        .collect::<rusqlite::Result<_>>()
        .unwrap();
    assert_eq!(
        symbols,
        vec![
            ("entry".to_string(), "function".to_string(), true),
            ("helper".to_string(), "function".to_string(), true),
        ]
    );

    let refs: Vec<String> = conn
        .prepare("SELECT symbol_name FROM refs ORDER BY symbol_name")
        .unwrap()
        .query_map([], |row| row.get::<_, String>(0))
        .unwrap()
        .collect::<rusqlite::Result<_>>()
        .unwrap();
    assert_eq!(refs, vec!["helper"]);

    let imports: Vec<String> = conn
        .prepare("SELECT source FROM imports ORDER BY source")
        .unwrap()
        .query_map([], |row| row.get::<_, String>(0))
        .unwrap()
        .collect::<rusqlite::Result<_>>()
        .unwrap();
    assert_eq!(imports, vec!["./util"]);

    let edges: Vec<(String, String)> = conn
        .prepare(
            "
            SELECT s.qualified_name, e.to_symbol_name
            FROM edges e
            JOIN symbols s ON s.id = e.from_symbol_id
            ORDER BY s.qualified_name, e.to_symbol_name
            ",
        )
        .unwrap()
        .query_map([], |row| {
            Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
        })
        .unwrap()
        .collect::<rusqlite::Result<_>>()
        .unwrap();
    assert_eq!(edges, vec![("entry".to_string(), "helper".to_string())]);
}