diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 0000000..a3ebfca --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,418 @@ +use assert_cmd::prelude::*; +use predicates::prelude::*; +use std::process::Command; +use std::fs; +use std::path::PathBuf; + +fn get_test_vault_path() -> PathBuf { + PathBuf::from("/tmp/api-token-vault-test-vault.json") +} + +fn cleanup_test_vault() { + let vault_path = get_test_vault_path(); + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_cli_help() { + let mut cmd = Command::cargo_bin("api-token-vault").unwrap(); + cmd.arg("--help"); + cmd.assert() + .success() + .stdout(predicate::str::contains("API Token Vault")); +} + +#[test] +fn test_cli_version() { + let mut cmd = Command::cargo_bin("api-token-vault").unwrap(); + cmd.arg("--version"); + cmd.assert() + .success() + .stdout(predicate::str::contains("0.1.0")); +} + +#[test] +fn test_init_command() { + cleanup_test_vault(); + + let mut cmd = Command::cargo_bin("api-token-vault").unwrap(); + cmd.arg("init") + .arg("--project") + .arg("test-init-project") + .env("API_VAULT_PROJECT", ""); + + let assert = cmd.assert(); + + cleanup_test_vault(); +} + +#[test] +fn test_generate_command() { + cleanup_test_vault(); + + let password = "test_password_123"; + let project = "test-gen-project"; + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-gen-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("test_token") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + gen_cmd.assert() + .success() + .stdout(predicate::str::contains("Generated token")); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_list_command() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-list-project"; + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-list-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut list_cmd = Command::cargo_bin("api-token-vault").unwrap(); + list_cmd.arg("list") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + list_cmd.assert() + .success() + .stdout(predicate::str::contains("Tokens in vault")); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_rotate_command() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-rotate-project"; + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-rotate-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("rotate_me") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = gen_cmd.output(); + + let mut rotate_cmd = Command::cargo_bin("api-token-vault").unwrap(); + rotate_cmd.arg("rotate") + .arg("--name") + .arg("rotate_me") + .arg("--force") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + rotate_cmd.assert() + .success() + .stdout(predicate::str::contains("Rotated token")); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_set_rotation_command() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-set-rotation-project"; + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-set-rotation-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("auto_rotate_token") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = gen_cmd.output(); + + let mut set_cmd = Command::cargo_bin("api-token-vault").unwrap(); + set_cmd.arg("set-rotation") + .arg("--name") + .arg("auto_rotate_token") + .arg("--days") + .arg("30") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + set_cmd.assert() + .success() + .stdout(predicate::str::contains("Auto-rotation set")); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_check_expired_command() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-check-expired-project"; + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-check-expired-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("expired_token") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = gen_cmd.output(); + + let mut check_cmd = Command::cargo_bin("api-token-vault").unwrap(); + check_cmd.arg("check-expired") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + check_cmd.assert() + .success(); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } +} + +#[test] +fn test_inject_command() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-inject-project"; + let env_path = PathBuf::from("/tmp/test-inject.env"); + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-inject-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + if env_path.exists() { + let _ = fs::remove_file(&env_path); + } + + let mut init_cmd = Command::cargo_bin("api-token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("injectable_token") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = gen_cmd.output(); + + let mut inject_cmd = Command::cargo_bin("api-token-vault").unwrap(); + inject_cmd.arg("inject") + .arg("--env-file") + .arg(env_path.to_str().unwrap()) + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + inject_cmd.assert() + .success() + .stdout(predicate::str::contains("injected")); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if env_path.exists() { + let _ = fs::remove_file(&env_path); + } +} + +#[test] +fn test_dry_run_inject() { + cleanup_test_vault(); + + let password = "test_password"; + let project = "test-dry-run-project"; + let env_path = PathBuf::from("/tmp/test-dry-run.env"); + + let vault_dir = PathBuf::from("/tmp/.config/api-token-vault"); + let vault_path = vault_dir.join("test-dry-run-project.json"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if !vault_dir.exists() { + let _ = fs::create_dir_all(&vault_dir); + } + if env_path.exists() { + let _ = fs::remove_file(&env_path); + } + + let mut init_cmd = Command::cargo_bin("token-vault").unwrap(); + init_cmd.arg("init") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = init_cmd.output(); + + let mut gen_cmd = Command::cargo_bin("api-token-vault").unwrap(); + gen_cmd.arg("generate") + .arg("--name") + .arg("dry_token") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + let _ = gen_cmd.output(); + + fs::write(&env_path, "EXISTING=value").unwrap(); + + let mut inject_cmd = Command::cargo_bin("api-token-vault").unwrap(); + inject_cmd.arg("inject") + .arg("--env-file") + .arg(env_path.to_str().unwrap()) + .arg("--dry-run") + .arg("--master-password") + .arg(password) + .arg("--project") + .arg(project); + + inject_cmd.assert() + .success() + .stdout(predicate::str::contains("Dry run")); + + let content = fs::read_to_string(&env_path).unwrap(); + assert_eq!(content.trim(), "EXISTING=value"); + + if vault_path.exists() { + let _ = fs::remove_file(&vault_path); + } + if env_path.exists() { + let _ = fs::remove_file(&env_path); + } +}