use assert_cmd::Command;
use predicates::prelude::*;
use std::sync::Mutex;
static ENV_MUTEX: Mutex<()> = Mutex::new(());
#[test]
fn test_api_mode_with_invalid_key() {
let _lock = ENV_MUTEX.lock().unwrap();
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.env("ANTHROPIC_API_KEY", "invalid-key-123")
.arg("--model")
.arg("claude")
.arg("--accurate")
.arg("-y")
.write_stdin("test");
let output = cmd.output().unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(!stdout.trim().is_empty(), "Should output token count");
let stderr = String::from_utf8(output.stderr).unwrap();
assert!(
stderr.contains("falling back to estimation"),
"Expected fallback warning, got: {}",
stderr
);
}
#[test]
fn test_api_mode_missing_key_verbose_error() {
let _lock = ENV_MUTEX.lock().unwrap();
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.env_remove("ANTHROPIC_API_KEY")
.arg("--model")
.arg("claude")
.arg("--accurate")
.arg("-y")
.write_stdin("test");
cmd.assert()
.failure()
.stderr(predicate::str::contains("export ANTHROPIC_API_KEY="))
.stderr(predicate::str::contains("omit --accurate flag"));
}
#[test]
fn test_api_mode_non_interactive_without_yes() {
let _lock = ENV_MUTEX.lock().unwrap();
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.env_remove("ANTHROPIC_API_KEY")
.arg("--model")
.arg("claude")
.arg("--accurate")
.write_stdin("test");
cmd.assert()
.failure()
.stderr(predicate::str::contains("non-interactive"))
.stderr(predicate::str::contains("-y"));
}
#[test]
fn test_api_mode_only_applies_to_claude() {
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.arg("--model").arg("gpt-4").arg("--accurate").write_stdin("test");
cmd.assert().success();
}
#[test]
fn test_accurate_flag_without_yes_non_interactive() {
let _lock = ENV_MUTEX.lock().unwrap();
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.env("ANTHROPIC_API_KEY", "sk-test-key")
.arg("--model")
.arg("claude")
.arg("--accurate")
.write_stdin("test");
cmd.assert()
.failure()
.stderr(predicate::str::contains("consent"))
.stderr(predicate::str::contains("non-interactive"));
}
#[test]
fn test_yes_flag_requires_accurate() {
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.arg("--model").arg("claude").arg("-y").write_stdin("test");
cmd.assert().success();
}
#[test]
fn test_estimation_fallback_message() {
let _lock = ENV_MUTEX.lock().unwrap();
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.env("ANTHROPIC_API_KEY", "sk-invalid")
.arg("--model")
.arg("claude")
.arg("--accurate")
.arg("-y")
.write_stdin("test");
let output = cmd.output().unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(!stdout.trim().is_empty(), "Should output token count");
let stderr = String::from_utf8(output.stderr).unwrap();
if !stderr.is_empty() {
assert!(
stderr.contains("Falling back") || stderr.contains("API"),
"Unexpected stderr: {}",
stderr
);
}
}
#[test]
fn test_all_claude_models_require_consent() {
let models = vec!["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"];
for model in models {
let mut cmd = Command::cargo_bin("token-count").unwrap();
cmd.arg("--model").arg(model).arg("--accurate").write_stdin("test");
cmd.assert().failure().stderr(predicate::str::contains("non-interactive"));
}
}