| 1 | //! Installed help must not load configuration or migrate credentials. |
| 2 | |
| 3 | use std::fs; |
| 4 | use std::process::Command; |
| 5 | |
| 6 | use codewhale_secrets::{FileKeyringStore, KeyringStore}; |
| 7 | use tempfile::TempDir; |
| 8 | |
| 9 | const SENTINEL: &str = "cw-help-must-not-read-credentials-91a7"; |
| 10 | |
| 11 | #[test] |
| 12 | fn passthrough_help_leaves_configuration_and_legacy_credentials_untouched() { |
| 13 | for malformed in [false, true] { |
| 14 | let fixture = TempDir::new().expect("isolated help fixture"); |
| 15 | let home = fixture.path().join("home"); |
| 16 | let primary = home.join(".codewhale"); |
| 17 | fs::create_dir_all(&primary).expect("create isolated config directory"); |
| 18 | let config = primary.join("config.toml"); |
| 19 | let config_bytes = if malformed { |
| 20 | format!("invalid = [{SENTINEL}\n") |
| 21 | } else { |
| 22 | "provider = \"deepseek\"\n".to_string() |
| 23 | }; |
| 24 | fs::write(&config, &config_bytes).expect("write config fixture"); |
| 25 | let legacy = home.join(".deepseek/secrets/secrets.json"); |
| 26 | FileKeyringStore::new(&legacy) |
| 27 | .set("deepseek", SENTINEL) |
| 28 | .expect("seed synthetic legacy credential"); |
| 29 | let legacy_bytes = fs::read(&legacy).expect("read legacy fixture"); |
| 30 | |
| 31 | for subcommand in ["exec", "init", "setup", "mcp", "review", "rc"] { |
| 32 | for flag in ["--help", "-h"] { |
| 33 | let output = Command::new(env!("CARGO_BIN_EXE_codewhale")) |
| 34 | .env_clear() |
| 35 | .env("HOME", &home) |
| 36 | .env("USERPROFILE", &home) |
| 37 | .env("CODEWHALE_SECRET_BACKEND", "file") |
| 38 | // No CODEWHALE_HOME: this is the legacy-migration path. |
| 39 | .current_dir(&home) |
| 40 | .args([subcommand, flag]) |
| 41 | .output() |
| 42 | .expect("run installed help"); |
| 43 | assert!(output.status.success(), "{subcommand} {flag}"); |
| 44 | assert!(output.stderr.is_empty(), "{subcommand} {flag}"); |
| 45 | let help = String::from_utf8(output.stdout).expect("UTF-8 help"); |
| 46 | assert!( |
| 47 | help.contains(&format!("Usage: codewhale {subcommand}")), |
| 48 | "{subcommand} must show its own help" |
| 49 | ); |
| 50 | assert!(!help.contains(SENTINEL)); |
| 51 | assert_eq!(fs::read_to_string(&config).unwrap(), config_bytes); |
| 52 | assert_eq!(fs::read(&legacy).unwrap(), legacy_bytes); |
| 53 | assert!( |
| 54 | !primary.join("secrets").exists(), |
| 55 | "help must not migrate credentials" |
| 56 | ); |
| 57 | assert!( |
| 58 | !primary.join("telemetry").exists(), |
| 59 | "help must not initialize telemetry" |
| 60 | ); |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 |