#![cfg(unix)]
use std::fs;
use std::io::Read;
use std::os::unix::process::ExitStatusExt;
use std::process::{Command, Stdio};
const SIGPIPE: i32 = 13;
const SECRET_COUNT: usize = 500;
const SECRET_LEN: usize = 200;
fn run_with_closed_stdout(args: &[&str]) -> std::process::Output {
let temp_dir = tempfile::tempdir().unwrap();
let config_path = temp_dir.path().join("secretspec.toml");
let mut config = String::from(
r#"[project]
name = "sigpipe-test"
revision = "1.0"
require_reason = false
[providers]
env = "env://"
[profiles.default]
"#,
);
for i in 0..SECRET_COUNT {
config.push_str(&format!(
"SECRET_{i} = {{ description = \"secret {i}\", providers = [\"env\"] }}\n"
));
}
fs::write(&config_path, config).unwrap();
let mut command = Command::new(env!("CARGO_BIN_EXE_secretspec"));
command
.args(["--file", config_path.to_str().unwrap()])
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
for i in 0..SECRET_COUNT {
command.env(format!("SECRET_{i}"), "v".repeat(SECRET_LEN));
}
let mut child = command.spawn().unwrap();
let mut stdout = child.stdout.take().unwrap();
let mut head = [0u8; 16];
stdout.read_exact(&mut head).unwrap();
drop(stdout);
child.wait_with_output().unwrap()
}
#[test]
fn export_dies_on_sigpipe_instead_of_reporting_a_broken_pipe() {
let output = run_with_closed_stdout(&["export", "--provider", "env", "--format", "dotenv"]);
assert_eq!(
output.status.signal(),
Some(SIGPIPE),
"expected termination by SIGPIPE, got {}:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stderr.is_empty(),
"a closed pipe should be silent, got:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn check_json_dies_on_sigpipe_instead_of_panicking() {
let output = run_with_closed_stdout(&["check", "--provider", "env", "--json"]);
assert_eq!(
output.status.signal(),
Some(SIGPIPE),
"expected termination by SIGPIPE, got {}:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stderr.is_empty(),
"a closed pipe should be silent, got:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn check_human_report_dies_on_sigpipe_without_a_diagnostic() {
let output = run_with_closed_stdout(&["check", "--provider", "env", "--no-prompt"]);
assert_eq!(
output.status.signal(),
Some(SIGPIPE),
"expected termination by SIGPIPE, got {}:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stderr.is_empty(),
"a closed pipe should be silent, got:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}