use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;
fn binary() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_keyhog"))
}
fn aws_leak_fixture() -> (TempDir, PathBuf) {
let dir = TempDir::new().expect("tempdir");
let path = dir.path().join("aws_leak.env");
let fixture = concat!("AWS_ACCESS_KEY_ID = \"AKIA", "QYLPMN5HFIQR7XYA\"\n");
std::fs::write(&path, fixture).expect("write fixture");
(dir, path)
}
#[test]
fn require_gpu_with_no_gpu_forced_exits_two() {
let (_dir, path) = aws_leak_fixture();
let output = Command::new(binary())
.arg("scan")
.arg(&path)
.env("KEYHOG_REQUIRE_GPU", "1")
.env("KEYHOG_NO_GPU", "1")
.output()
.expect("spawn keyhog scan");
assert_eq!(
output.status.code(),
Some(2),
"KEYHOG_REQUIRE_GPU=1 with no usable GPU must exit 2 (fail closed), \
not silently scan on CPU; stderr={}",
String::from_utf8_lossy(&output.stderr)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("KEYHOG_REQUIRE_GPU"),
"exit-2 diagnostic should name KEYHOG_REQUIRE_GPU; stderr={stderr}"
);
}
fn host_has_usable_gpu() -> bool {
let out = Command::new(binary())
.arg("backend")
.output()
.expect("spawn keyhog backend");
let stdout = String::from_utf8_lossy(&out.stdout);
let gpu_line = stdout
.lines()
.find(|l| l.trim_start().starts_with("gpu:"))
.unwrap_or("");
!gpu_line.contains("not detected") && !gpu_line.contains("software renderer")
}
#[test]
fn require_gpu_on_no_gpu_host_exits_two() {
if host_has_usable_gpu() {
return;
}
let (_dir, path) = aws_leak_fixture();
let output = Command::new(binary())
.arg("scan")
.arg(&path)
.env("KEYHOG_REQUIRE_GPU", "1")
.env_remove("KEYHOG_NO_GPU")
.output()
.expect("spawn keyhog scan");
assert_eq!(
output.status.code(),
Some(2),
"on a no-GPU host KEYHOG_REQUIRE_GPU=1 must fail closed with exit 2 \
(the CI auto-skip must not mask the requirement); stderr={}",
String::from_utf8_lossy(&output.stderr)
);
}