quotch 0.5.5

Fast cross-platform CLI for AI coding-agent usage limits
//! CLI integration tests that exercise the real built `quotch` binary.
//!
//! Every test scrubs HOME (so no real ~/.claude, ~/.codex,
//! ~/.config/github-copilot, ~/.gemini creds are found), XDG_CACHE_HOME (so
//! no real quotch cache is read/written), and CODEX_HOME (so codex's
//! CODEX_HOME override can't leak a real auth.json either). With all
//! providers' discover() finding nothing, no network call is ever made and
//! the tests are hermetic and deterministic, whether or not the machine
//! running them has real provider credentials configured.

use assert_cmd::Command;
use predicates::prelude::*;

/// Builds a `quotch` command with HOME/XDG_CACHE_HOME/CODEX_HOME pointed at
/// fresh, empty temp locations so every provider's `discover()` finds no
/// credentials and no network call is made.
fn scrubbed_cmd(home: &std::path::Path) -> Command {
    let mut cmd = Command::cargo_bin("quotch").unwrap();
    cmd.env("HOME", home)
        .env("XDG_CACHE_HOME", home.join("cache"))
        .env("CODEX_HOME", home.join("no-such-codex-home"));
    cmd
}

#[test]
fn version_flag_prints_name_and_version() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .arg("--version")
        .assert()
        .success()
        .stdout(predicate::str::contains("quotch"))
        .stdout(predicate::str::contains(env!("CARGO_PKG_VERSION")));
}

#[test]
fn help_flag_exits_zero() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .arg("-h")
        .assert()
        .success()
        .stdout(predicate::str::contains("Usage"));
}

#[test]
fn unknown_flag_exits_two() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .arg("--definitely-not-a-flag")
        .assert()
        .code(2)
        .stderr(predicate::str::contains("Usage"));
}

#[test]
fn json_empty_when_no_providers() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .arg("--json")
        .assert()
        .success()
        .stdout(predicate::str::contains("\"v\": 1"))
        .stdout(predicate::str::contains("\"snapshots\": []"));
}

#[test]
fn line_mode_empty_when_no_providers() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .assert()
        .success()
        .stdout(predicate::str::is_empty().or(predicate::str::is_match(r"^\s*$").unwrap()));
}

#[test]
fn skill_print_outputs_frontmatter() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .args(["skill", "print"])
        .assert()
        .success()
        .stdout(predicate::str::contains("name: quotch"))
        .stdout(predicate::str::contains("quotch --json"));
}

#[test]
fn skill_install_writes_file() {
    let home = tempfile::tempdir().unwrap();
    let install_dir = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .args([
            "skill",
            "install",
            "--dir",
            install_dir.path().to_str().unwrap(),
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("installed:"));

    let written = install_dir.path().join("quotch").join("SKILL.md");
    let contents = std::fs::read_to_string(&written)
        .unwrap_or_else(|e| panic!("expected {} to exist: {e}", written.display()));
    assert!(!contents.trim().is_empty());
}

#[test]
fn skill_bad_subcommand_exits_two() {
    let home = tempfile::tempdir().unwrap();
    scrubbed_cmd(home.path())
        .args(["skill", "nonsense"])
        .assert()
        .code(2);
}