tokensave 7.9.0

Code intelligence tool that builds a semantic knowledge graph from Rust, Go, Java, Scala, TypeScript, Python, C, C++, Kotlin, C#, Swift, and many more codebases
use std::fs;
use std::process::Command;

use tempfile::TempDir;

fn tokensave_command(home: &TempDir) -> Command {
    let mut command = Command::new(env!("CARGO_BIN_EXE_tokensave"));
    command
        .env("HOME", home.path())
        .env("TOKENSAVE_SKIP_UPDATE_CHECK", "1");
    command
}

fn isolated_home_with_claude_session() -> TempDir {
    let home = TempDir::new().expect("temp home");
    let tokensave_dir = home.path().join(".tokensave");
    fs::create_dir_all(&tokensave_dir).expect("tokensave dir");
    fs::write(
        tokensave_dir.join("config.toml"),
        "last_pricing_fetch_at = 4102444800\n",
    )
    .expect("config");

    let session_dir = home.path().join(".claude/projects/project");
    fs::create_dir_all(&session_dir).expect("claude session dir");
    fs::write(
        session_dir.join("session.jsonl"),
        "{\"type\":\"assistant\",\"message\":{\"id\":\"msg-1\",\"model\":\"claude-opus-4-6\",\"role\":\"assistant\",\"usage\":{\"input_tokens\":1000,\"output_tokens\":200,\"cache_creation_input_tokens\":500,\"cache_read_input_tokens\":800},\"content\":[]},\"timestamp\":\"2026-07-31T12:00:00.000Z\"}\n",
    )
    .expect("claude session");
    home
}

#[test]
fn cost_without_droid_preserves_existing_output() {
    let home = isolated_home_with_claude_session();
    let output = tokensave_command(&home)
        .args(["cost", "all"])
        .output()
        .expect("run tokensave cost");

    assert!(
        output.status.success(),
        "stderr: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    let stdout = String::from_utf8(output.stdout).expect("utf8 stdout");
    assert!(stdout.contains("1.0k"), "{stdout}");
    assert!(!stdout.contains("Coverage:"), "{stdout}");
    assert!(!stdout.contains("Droid"), "{stdout}");
    assert!(!stdout.contains("Augment"), "{stdout}");
    assert!(!stdout.contains("Copilot"), "{stdout}");

    let json = tokensave_command(&home)
        .args(["cost", "all", "--export", "json"])
        .output()
        .expect("run tokensave cost json");
    assert!(
        json.status.success(),
        "stderr: {}",
        String::from_utf8_lossy(&json.stderr)
    );
    let value: serde_json::Value = serde_json::from_slice(&json.stdout).expect("valid json output");
    let object = value.as_object().expect("json object");
    assert_eq!(value["total_input_tokens"], 1000);
    assert!(!object.contains_key("by_agent"), "{value}");
    assert!(!object.contains_key("coverage"), "{value}");
}

#[test]
fn removed_droid_source_does_not_surface_stale_rows() {
    let home = isolated_home_with_claude_session();
    let droid_dir = home.path().join(".factory/sessions/project");
    fs::create_dir_all(&droid_dir).expect("droid session dir");
    fs::write(
        droid_dir.join("session.settings.json"),
        r#"{
            "providerLockTimestamp": "2026-07-31T12:00:00.000Z",
            "model": "claude-opus-4",
            "tokenUsage": {
                "inputTokens": 2000,
                "outputTokens": 400,
                "factoryCredits": 1500
            }
        }"#,
    )
    .expect("droid session");

    let first = tokensave_command(&home)
        .args(["cost", "all", "--by-agent"])
        .output()
        .expect("ingest Droid source");
    assert!(first.status.success());
    assert!(
        String::from_utf8_lossy(&first.stdout).contains("droid"),
        "{}",
        String::from_utf8_lossy(&first.stdout)
    );

    fs::remove_dir_all(home.path().join(".factory")).expect("remove Droid source");

    let second = tokensave_command(&home)
        .args(["cost", "all", "--by-agent"])
        .output()
        .expect("query after Droid removal");
    assert!(second.status.success());
    let stdout = String::from_utf8_lossy(&second.stdout);
    assert!(!stdout.contains("droid"), "{stdout}");
    assert!(!stdout.contains("Coverage:"), "{stdout}");
}