use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use tempfile::TempDir;
#[allow(deprecated)] fn cmd_idioma(dir: &TempDir) -> Command {
let mut cmd = Command::cargo_bin("context7").unwrap();
cmd.env_clear()
.env("CONTEXT7_HOME", dir.path())
.env("HOME", dir.path());
cmd
}
#[test]
#[serial]
fn testa_help_renderiza_sem_panico_independente_de_lang() {
let dir = TempDir::new().unwrap();
cmd_idioma(&dir).arg("--help").assert().success();
}
#[test]
#[serial]
fn testa_keys_list_vazio_mensagem_em_portugues() {
let dir = TempDir::new().unwrap();
cmd_idioma(&dir)
.env("CONTEXT7_LANG", "pt")
.args(["keys", "list"])
.assert()
.success()
.stdout(
predicate::str::contains("Nenhuma chave")
.or(predicate::str::contains("nenhuma"))
.or(predicate::str::contains("Use"))
.or(predicate::str::contains("0 chave")),
);
}
#[test]
#[serial]
fn testa_keys_list_vazio_nao_exibe_erro_sistema() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir).args(["keys", "list"]).output().unwrap();
assert!(
saida.status.success(),
"keys list sem chaves deve retornar exit 0"
);
let stderr = String::from_utf8_lossy(&saida.stderr);
assert!(
!stderr.contains("Error") || stderr.is_empty(),
"keys list não deve produzir mensagens de erro no stderr: {stderr}"
);
}
#[test]
#[serial]
fn testa_env_context7_lang_pt_aceita_sem_crash() {
let dir = TempDir::new().unwrap();
cmd_idioma(&dir)
.env("CONTEXT7_LANG", "pt")
.args(["keys", "list"])
.assert()
.success();
}
#[test]
#[serial]
fn testa_env_context7_lang_en_aceita_sem_crash() {
let dir = TempDir::new().unwrap();
cmd_idioma(&dir)
.env("CONTEXT7_LANG", "en")
.args(["keys", "list"])
.assert()
.success();
}
#[test]
#[serial]
fn testa_env_context7_lang_invalido_usa_fallback_sem_panic() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir)
.env("CONTEXT7_LANG", "xx-invalido")
.args(["keys", "list"])
.output()
.unwrap();
let stderr = String::from_utf8_lossy(&saida.stderr);
assert!(
!stderr.contains("thread 'main' panicked"),
"CONTEXT7_LANG inválido não deve causar panic: {stderr}"
);
}
#[test]
#[serial]
fn testa_erro_sem_chave_mensagem_legivel_pt() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir)
.env("CONTEXT7_LANG", "pt")
.args(["library", "react"])
.output()
.unwrap();
assert!(!saida.status.success());
let stderr = String::from_utf8_lossy(&saida.stderr);
let stdout = String::from_utf8_lossy(&saida.stdout);
let combinado = format!("{stdout}{stderr}");
assert!(
!combinado.contains("thread 'main' panicked"),
"não deve panic: {combinado}"
);
}
#[test]
#[serial]
fn testa_erro_sem_chave_mensagem_legivel_en() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir)
.env("CONTEXT7_LANG", "en")
.args(["library", "react"])
.output()
.unwrap();
assert!(!saida.status.success());
let stderr = String::from_utf8_lossy(&saida.stderr);
let stdout = String::from_utf8_lossy(&saida.stdout);
let combinado = format!("{stdout}{stderr}");
assert!(
!combinado.contains("thread 'main' panicked"),
"não deve panic: {combinado}"
);
}
#[test]
#[serial]
fn testa_b2_chave_ausente_em_portugues_usa_mensagem_pt() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir)
.env("CONTEXT7_LANG", "pt")
.args(["library", "react"])
.output()
.unwrap();
assert!(
!saida.status.success(),
"Deve falhar sem chave de API configurada"
);
let stderr = String::from_utf8_lossy(&saida.stderr);
let stdout = String::from_utf8_lossy(&saida.stdout);
let combinado = format!("{stdout}{stderr}");
assert!(
combinado.contains("chave de API") || combinado.contains("CONTEXT7_API_KEYS"),
"Mensagem PT deve mencionar 'chave de API' ou 'CONTEXT7_API_KEYS', obteve: {combinado}"
);
assert!(
!combinado.contains("thread 'main' panicked"),
"Não deve causar panic: {combinado}"
);
}
#[test]
#[serial]
fn testa_b2_chave_ausente_em_ingles_usa_mensagem_en() {
let dir = TempDir::new().unwrap();
let saida = cmd_idioma(&dir)
.env("CONTEXT7_LANG", "en")
.args(["library", "react"])
.output()
.unwrap();
assert!(
!saida.status.success(),
"Deve falhar sem chave de API configurada"
);
let stderr = String::from_utf8_lossy(&saida.stderr);
let stdout = String::from_utf8_lossy(&saida.stdout);
let combinado = format!("{stdout}{stderr}");
assert!(
combinado.contains("API key") || combinado.contains("CONTEXT7_API_KEYS"),
"Mensagem EN deve mencionar 'API key' ou 'CONTEXT7_API_KEYS', obteve: {combinado}"
);
assert!(
!combinado.contains("thread 'main' panicked"),
"Não deve causar panic: {combinado}"
);
}
#[test]
#[serial]
fn testa_keys_list_mascara_chave_longa() {
let dir = TempDir::new().unwrap();
let chave = "ctx7sk-chave-muito-longa-para-mascarar";
cmd_idioma(&dir)
.args(["keys", "add", chave])
.assert()
.success();
let output = cmd_idioma(&dir).args(["keys", "list"]).output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stdout.contains(chave),
"chave completa não deve aparecer em texto claro no list: {stdout}"
);
assert!(
stdout.contains("..."),
"chave longa mascarada deve usar '...': {stdout}"
);
}
#[test]
#[serial]
fn testa_keys_list_mascara_chave_curta() {
let dir = TempDir::new().unwrap();
let chave = "abc1234"; cmd_idioma(&dir)
.args(["keys", "add", chave])
.assert()
.success();
let output = cmd_idioma(&dir).args(["keys", "list"]).output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stdout.contains(chave),
"chave curta completa não deve aparecer no list: {stdout}"
);
}
use context7_cli::i18n::{resolver_idioma, Idioma, Mensagem};
#[test]
fn testa_resolver_flag_explicita_en() {
let idioma = resolver_idioma(Some("en"));
assert!(matches!(idioma, Idioma::English));
}
#[test]
fn testa_resolver_flag_explicita_pt() {
let idioma = resolver_idioma(Some("pt"));
assert!(matches!(idioma, Idioma::Portugues));
}
#[test]
#[serial]
fn testa_resolver_env_context7_lang_pt_quando_sem_flag() {
unsafe { std::env::set_var("CONTEXT7_LANG", "pt") };
let idioma = resolver_idioma(None);
unsafe { std::env::remove_var("CONTEXT7_LANG") };
assert!(matches!(idioma, Idioma::Portugues));
}
#[test]
fn testa_resolver_fallback_quando_tudo_none_retorna_idioma_valido() {
let idioma = resolver_idioma(None);
assert!(matches!(idioma, Idioma::English | Idioma::Portugues));
}
#[test]
fn testa_mensagem_operacao_cancelada_en_vs_pt_sao_distintas() {
let variante = Mensagem::OperacaoCancelada;
let _ = variante; let _ = resolver_idioma(Some("en"));
let _ = resolver_idioma(Some("pt"));
}
use context7_cli::i18n::t;
#[test]
fn testa_mensagem_falha_buscar_documentacao_acessivel_e_nao_vazia() {
let texto = t(Mensagem::FalhaBuscarDocumentacao);
assert!(
!texto.is_empty(),
"FalhaBuscarDocumentacao deve ser não-vazia"
);
let texto_lower = texto.to_lowercase();
assert!(
texto_lower.contains("doc")
|| texto_lower.contains("fetch")
|| texto_lower.contains("falha")
|| texto_lower.contains("failed"),
"FalhaBuscarDocumentacao deve mencionar doc/fetch/falha/failed: {texto}"
);
}
#[test]
fn testa_mensagem_falha_buscar_biblioteca_acessivel_e_nao_vazia() {
let texto = t(Mensagem::FalhaBuscarBiblioteca);
assert!(
!texto.is_empty(),
"FalhaBuscarBiblioteca deve ser não-vazia"
);
}
#[test]
fn testa_mensagem_falha_criar_cliente_http_acessivel_e_nao_vazia() {
let texto = t(Mensagem::FalhaCriarClienteHttp);
assert!(
!texto.is_empty(),
"FalhaCriarClienteHttp deve ser não-vazia"
);
}
#[test]
fn testa_mensagem_falha_serializar_json_acessivel_e_nao_vazia() {
let texto = t(Mensagem::FalhaSerializarJson);
assert!(!texto.is_empty(), "FalhaSerializarJson deve ser não-vazia");
}
#[test]
fn testa_mensagem_sem_documentacao_disponivel_acessivel_e_nao_vazia() {
let texto = t(Mensagem::SemDocumentacaoDisponivel);
assert!(
!texto.is_empty(),
"SemDocumentacaoDisponivel deve ser não-vazia"
);
}
#[test]
fn testa_mensagem_biblioteca_nao_encontrada_api_en_pt() {
let en = Mensagem::BibliotecaNaoEncontradaApi.texto(Idioma::English);
let pt = Mensagem::BibliotecaNaoEncontradaApi.texto(Idioma::Portugues);
assert!(
!en.is_empty(),
"EN BibliotecaNaoEncontradaApi não deve ser vazia"
);
assert!(
!pt.is_empty(),
"PT BibliotecaNaoEncontradaApi não deve ser vazia"
);
assert_ne!(en, pt, "EN e PT devem ser strings diferentes (bilíngue)");
assert!(
en.to_lowercase().contains("library") || en.to_lowercase().contains("not found"),
"EN deve mencionar 'library' ou 'not found', obteve: {en}"
);
assert!(
pt.to_lowercase().contains("biblioteca") || pt.to_lowercase().contains("encontrada"),
"PT deve mencionar 'biblioteca' ou 'encontrada', obteve: {pt}"
);
}
#[test]
fn testa_confianca_score_en_eh_lowercase_sem_dois_pontos_v023() {
let en = Mensagem::ConfiancaScore.texto(Idioma::English);
assert_eq!(
en, "trust",
"ConfiancaScore EN deve ser 'trust' em v0.2.3, obteve: {en}"
);
}
#[test]
fn testa_confianca_score_pt_eh_lowercase_sem_dois_pontos_v023() {
let pt = Mensagem::ConfiancaScore.texto(Idioma::Portugues);
assert_eq!(
pt, "confiança",
"ConfiancaScore PT deve ser 'confiança' em v0.2.3, obteve: {pt}"
);
}