scryer-cli 0.4.0

CLI tool and shared daemon runner for Scryer code intelligence
//! `scryer report` end to end: the real binary and a real database.

use std::process::Command;

fn scryer(db: &std::path::Path, telemetry: Option<&str>) -> Command {
    let mut cmd = Command::new(env!("CARGO_BIN_EXE_scryer"));
    cmd.env("SCRYER_DB_URL", db)
        .env("SCRYER_NO_DAEMON", "1")
        .env_remove("SCRYER_TELEMETRY");
    if let Some(value) = telemetry {
        cmd.env("SCRYER_TELEMETRY", value);
    }
    cmd
}

fn report(db: &std::path::Path) -> String {
    let out = scryer(db, None).arg("report").output().unwrap();
    assert!(out.status.success(), "{out:?}");
    String::from_utf8(out.stdout).unwrap()
}

#[test]
fn report_says_how_usage_is_recorded_when_there_is_none() {
    let dir = tempfile::tempdir().unwrap();
    let text = report(&dir.path().join("s.db"));
    assert!(text.contains("No Scryer calls recorded"), "{text}");
    assert!(text.contains("SCRYER_TELEMETRY=off"), "{text}");
}

#[test]
fn report_lists_measured_usage_per_tool_and_the_opt_out_stops_recording() {
    let dir = tempfile::tempdir().unwrap();
    let db = dir.path().join("s.db");

    let out = scryer(&db, None)
        .args(["call", "list_projects"])
        .output()
        .unwrap();
    assert!(out.status.success(), "{out:?}");
    let text = report(&db);
    assert!(text.contains("list_projects: 1 calls"), "{text}");
    assert!(text.contains("followed by another Scryer call"), "{text}");
    for retired in ["saved", "savings", "$"] {
        assert!(!text.contains(retired), "{retired} in {text}");
    }

    // With telemetry off the second call leaves no row.
    let out = scryer(&db, Some("off"))
        .args(["call", "list_projects"])
        .output()
        .unwrap();
    assert!(out.status.success(), "{out:?}");
    assert!(report(&db).contains("list_projects: 1 calls"));
}