use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::process::Stdio; struct TestEnvironment { homedir: PathBuf, main_id: String, short_id: String, short_unchecked_id: String, long_id: String, } impl TestEnvironment { fn gpg_cmd(&self) -> Command { let mut cmd = Command::new("gpg"); cmd.arg("--homedir").arg(&self.homedir); cmd } fn gpg_create_key_cmd(&self) -> Command { let mut cmd = self.gpg_cmd(); cmd.stdout(Stdio::null()).stderr(Stdio::null()); cmd.args(["--batch"]).args(["--passphrase", ""]); cmd } } fn setup_homedir

(test_name: P) -> Result> where P: AsRef, { let dir = Path::new(env!("CARGO_TARGET_TMPDIR")) .join(test_name) .join("gpg-home"); if dir.exists() { std::fs::remove_dir_all(&dir)?; } std::fs::create_dir_all(&dir)?; let test_env = TestEnvironment { homedir: dir, main_id: "".to_string(), short_id: "".to_string(), short_unchecked_id: "".to_string(), long_id: "".to_string(), }; assert!( test_env .gpg_create_key_cmd() .args([ "--quick-generate-key", "test@example.org", "ed25519", "sign", "1d", ]) .spawn()? .wait()? .success(), "Failed to create main key" ); let list_keys_output = String::from_utf8( test_env .gpg_cmd() .args(["--with-colons", "--fixed-list-mode", "--list-keys"]) .output() .expect("GPG command failed") .stdout, )?; let main_id = list_keys_output .lines() .map(|line| line.split(':').collect::>()) .skip_while(|line| line[0] != "pub") .find(|line| line[0] == "fpr") .map(|line| line[9]) .unwrap(); assert!( test_env .gpg_create_key_cmd() .args(["--quick-add-key", main_id, "ed25519", "auth", "1d"]) .spawn()? .wait()? .success(), "Failed to create short expiry subkey" ); assert!( test_env .gpg_create_key_cmd() .args(["--quick-add-key", main_id, "ed25519", "auth", "2d"]) .spawn()? .wait()? .success(), "Failed to create short expiry subkey that won't be checked" ); assert!( test_env .gpg_create_key_cmd() .args(["--quick-add-key", main_id, "ed25519", "auth", "5d"]) .spawn()? .wait()? .success(), "Failed to create long expiry subkey" ); let list_keys_output = String::from_utf8( test_env .gpg_cmd() .args(["--with-colons", "--fixed-list-mode", "--list-keys"]) .output() .expect("GPG command failed") .stdout, )?; let mut subkeys: Vec<(&str, u64)> = list_keys_output .lines() .map(|line| line.split(':').collect::>()) .filter(|line| line[0] == "sub") .map(|line| { ( line[4], line[6].parse::().expect("Failed to parse expiry time"), ) }) .collect(); subkeys.sort_by_key(|(_, expire)| *expire); let key_ids: Vec<&str> = subkeys .into_iter() .map(|(id, _)| id) .flat_map(|id| { list_keys_output .lines() .map(|line| line.split(':').collect::>()) .find(|line| line[0] == "fpr" && line[9].ends_with(id)) .map(|line| line[9]) }) .collect(); Ok(TestEnvironment { main_id: main_id.to_string(), short_id: key_ids[0].to_string(), short_unchecked_id: key_ids[1].to_string(), long_id: key_ids[2].to_string(), ..test_env }) } #[test] fn test_default() -> Result<(), Box> { let TestEnvironment { homedir, short_id, short_unchecked_id, long_id, .. } = setup_homedir("test_default")?; let prog_output = String::from_utf8( Command::new(env!("CARGO_BIN_EXE_gpg-expire-warner")) .env("GNUPGHOME", homedir) .args(["--days", "2", &short_id, &long_id]) .output()? .stdout, )?; assert!( prog_output.contains(&format!("{short_id}: 1 days")) || prog_output.contains(&format!("{short_id}: 0 days")), "Short expiry key {} not found in output: {}", short_id, prog_output, ); assert!( !prog_output.contains(&short_unchecked_id), "Unchecked short expiry key {} found in output: {}", short_unchecked_id, prog_output, ); assert!( !prog_output.contains(&long_id), "Long expiry key {} found in output: {}", long_id, prog_output, ); Ok(()) } #[test] fn test_set_expire() -> Result<(), Box> { let TestEnvironment { homedir, short_id, short_unchecked_id, long_id, .. } = setup_homedir("test_set_expire")?; assert!( Command::new(env!("CARGO_BIN_EXE_gpg-expire-warner")) .env("GNUPGHOME", &homedir) .args(["--days", "2", &short_id, &long_id, "--expire", "10d"]) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn()? .wait()? .success(), "Failed to set expiration", ); let prog_output = String::from_utf8( Command::new(env!("CARGO_BIN_EXE_gpg-expire-warner")) .env("GNUPGHOME", &homedir) .args(["--days", "7", &short_id, &long_id]) .output()? .stdout, )?; assert!( !prog_output.contains(&short_id), "Short expiry key {} found in output: {}", short_id, prog_output, ); assert!( !prog_output.contains(&short_unchecked_id), "Unchecked short expiry key {} found in output: {}", short_unchecked_id, prog_output, ); assert!( prog_output.contains(&long_id), "Long expiry key {} not found in output: {}", long_id, prog_output, ); Ok(()) } #[test] fn test_set_expire_main() -> Result<(), Box> { let TestEnvironment { homedir, main_id, short_id, short_unchecked_id, long_id, } = setup_homedir("test_set_expire_main")?; { let expire_output = String::from_utf8( Command::new(env!("CARGO_BIN_EXE_gpg-expire-warner")) .env("GNUPGHOME", &homedir) .args(["--days", "2", &main_id, &short_id, &long_id]) .args(["--expire", "10d"]) .output()? .stdout, )?; println!("{expire_output}"); assert!( expire_output.contains(&main_id), "Main key {main_id} not found in output: {expire_output}", ); } { let check_output = String::from_utf8( Command::new(env!("CARGO_BIN_EXE_gpg-expire-warner")) .env("GNUPGHOME", &homedir) .args(["--days", "7", &main_id, &short_id, &long_id]) .output()? .stdout, )?; assert!( !check_output.contains(&main_id), "Main key {short_id} found in output: {check_output}", ); assert!( !check_output.contains(&short_id), "Short expiry key {short_id} found in output: {check_output}", ); assert!( !check_output.contains(&short_unchecked_id), "Unchecked short expiry key {short_unchecked_id} found in output: {check_output}", ); assert!( check_output.contains(&long_id), "Long expiry key {long_id} not found in output: {check_output}", ); } Ok(()) }