context7-cli 0.5.1

Search library documentation from your terminal — zero runtime, bilingual (EN/PT), multi-key rotation
Documentation
//! Testes de integração E2E para o subcomando `context7 health`.
//!
//! Todos os testes invocam o binário compilado via `assert_cmd::Command::cargo_bin`.
//! Nenhum teste faz I/O de rede real — os cenários de saída 66 e 74 disparam antes
//! do probe HTTP. O isolamento de chaves é garantido por `CONTEXT7_HOME` apontando
//! para um `TempDir` vazio.

use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use tempfile::TempDir;

// ── Helper ────────────────────────────────────────────────────────────────────

#[allow(deprecated)]
fn cmd_isolado(xdg_home: &TempDir) -> Command {
    let mut cmd = Command::cargo_bin("context7").unwrap();
    cmd.env_clear()
        .env("CONTEXT7_HOME", xdg_home.path())
        .env("HOME", xdg_home.path());
    cmd
}

// ── Testes ────────────────────────────────────────────────────────────────────

/// `context7 health` sem nenhuma chave configurada deve sair com código 66.
#[test]
#[serial]
fn testa_health_sem_chaves_retorna_66() {
    let dir = TempDir::new().unwrap();
    cmd_isolado(&dir).arg("health").assert().code(66);
}

/// `context7 --json health` sem chaves deve emitir JSON parseável e sair com 66.
///
/// Verifica que o envelope NDJSON contém os campos obrigatórios: `type`, `timestamp`,
/// `config_ok`, `keys_count`, `api_alcancavel`.
#[test]
#[serial]
fn testa_health_json_formato_parseavel() {
    let dir = TempDir::new().unwrap();
    let output = cmd_isolado(&dir)
        .args(["--json", "health"])
        .output()
        .unwrap();

    // exit 66 esperado (sem chaves)
    assert_eq!(output.status.code(), Some(66));

    // stdout deve ser JSON parseável com campos obrigatórios
    let stdout = String::from_utf8_lossy(&output.stdout);
    let linha = stdout.lines().next().unwrap_or("");
    let valor: serde_json::Value =
        serde_json::from_str(linha).expect("stdout deve ser JSON válido em modo --json");

    assert_eq!(valor["type"], "health", "campo 'type' deve ser 'health'");
    assert!(
        valor["timestamp"].is_string(),
        "campo 'timestamp' deve existir"
    );
    assert_eq!(
        valor["config_ok"], true,
        "config_ok deve ser true (cliente HTTP criado)"
    );
    assert_eq!(valor["keys_count"], 0, "keys_count deve ser 0 sem chaves");
    assert_eq!(
        valor["api_alcancavel"], false,
        "api_alcancavel deve ser false sem chaves"
    );
}

/// `context7 health --quiet` sem chaves deve sair com 66 e produzir stdout vazio.
///
/// Garante que o gate `--quiet` funciona em conjunto com o subcomando health.
#[test]
#[serial]
fn testa_health_quiet_silencia_stdout() {
    let dir = TempDir::new().unwrap();
    let output = cmd_isolado(&dir)
        .args(["--quiet", "health"])
        .output()
        .unwrap();

    assert_eq!(output.status.code(), Some(66));
    assert!(
        output.stdout.is_empty(),
        "stdout deve estar vazio com --quiet, obteve: {:?}",
        String::from_utf8_lossy(&output.stdout)
    );
}

/// `context7 health` sem chaves exibe mensagem legível sobre chaves ausentes.
///
/// Verifica que o output humano menciona algo sobre "keys" ou "chaves" ou o símbolo de falha.
#[test]
#[serial]
fn testa_health_exibe_diagnostico_sem_chaves() {
    let dir = TempDir::new().unwrap();
    cmd_isolado(&dir).arg("health").assert().code(66).stdout(
        predicate::str::contains("keys")
            .or(predicate::str::contains("chaves"))
            .or(predicate::str::contains("FAIL"))
            .or(predicate::str::contains("[FAIL]")),
    );
}