mirror of
https://github.com/Momoko-Ayase/Senbei.git
synced 2026-11-04 10:31:29 -05:00
194 lines
6.1 KiB
Rust
194 lines
6.1 KiB
Rust
//! CLI behavior of `--strict-integrity`.
|
|
//!
|
|
//! The flag is off by default and only changes the exit status (see the unit
|
|
//! tests in `src/main.rs` for the errors-versus-suspect matrix). These tests
|
|
//! drive the binary: help text, flag parsing, the error exit, and the quiet
|
|
//! contract. The suspect-only exit policy is covered separately by unit tests
|
|
//! so these process tests do not depend on the optional protected corpus.
|
|
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
fn senbei() -> Command {
|
|
Command::new(env!("CARGO_BIN_EXE_senbei"))
|
|
}
|
|
|
|
fn run(args: &[&str]) -> std::process::Output {
|
|
senbei().args(args).output().expect("spawn senbei")
|
|
}
|
|
|
|
fn assert_silent(output: &std::process::Output) {
|
|
assert!(
|
|
output.stdout.is_empty() && output.stderr.is_empty(),
|
|
"quiet>=2 must stay silent, stdout={:?} stderr={:?}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn write_plain(dir: &Path) -> std::path::PathBuf {
|
|
let path = dir.join("plain.bin");
|
|
std::fs::write(&path, b"not a protected image").unwrap();
|
|
path
|
|
}
|
|
|
|
fn list_logs(dir: &Path) -> Vec<std::path::PathBuf> {
|
|
std::fs::read_dir(dir)
|
|
.into_iter()
|
|
.flatten()
|
|
.filter_map(|entry| entry.ok())
|
|
.map(|entry| entry.path())
|
|
.filter(|path| {
|
|
path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.is_some_and(|name| name.starts_with("senbei-") && name.ends_with(".log"))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn help_documents_strict_integrity_behavior() {
|
|
let output = run(&["--help"]);
|
|
assert!(output.status.success());
|
|
let text = String::from_utf8(output.stdout).unwrap();
|
|
assert!(text.contains("--strict-integrity"));
|
|
assert!(text.contains("--quiet"));
|
|
assert!(text.contains("exit non-zero"));
|
|
assert!(text.contains("suspect"));
|
|
assert!(text.contains("Off by default"));
|
|
assert!(text.contains("still written"));
|
|
assert!(text.contains("exits 0 unless"));
|
|
assert!(text.contains("a file errors"));
|
|
assert!(text.contains("Quiet levels are unchanged"));
|
|
}
|
|
|
|
#[test]
|
|
fn short_help_and_version_ignore_the_flag_position() {
|
|
let help = run(&["-h", "--strict-integrity"]);
|
|
assert!(help.status.success());
|
|
assert!(String::from_utf8_lossy(&help.stdout).contains("--strict-integrity"));
|
|
|
|
let version = run(&["--version"]);
|
|
assert!(version.status.success());
|
|
assert!(String::from_utf8_lossy(&version.stdout).starts_with("Senbei "));
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_flag_still_exits_2() {
|
|
let output = run(&["--strict"]);
|
|
assert_eq!(output.status.code(), Some(2));
|
|
let err = String::from_utf8_lossy(&output.stderr);
|
|
assert!(err.contains("unknown option '--strict'"));
|
|
assert!(String::from_utf8_lossy(&output.stdout).contains("--strict-integrity"));
|
|
}
|
|
|
|
#[test]
|
|
fn missing_input_exits_2_with_the_flag() {
|
|
let output = run(&["--strict-integrity", "--no-pause"]);
|
|
assert_eq!(output.status.code(), Some(2));
|
|
assert!(String::from_utf8_lossy(&output.stdout).contains("--strict-integrity"));
|
|
}
|
|
|
|
#[test]
|
|
fn errors_exit_1_by_default_and_when_strict_and_write_nothing() {
|
|
let td = tempfile::tempdir().unwrap();
|
|
let input = write_plain(td.path());
|
|
for extra in [None, Some("--strict-integrity")] {
|
|
let out = td
|
|
.path()
|
|
.join(if extra.is_some() { "strict" } else { "default" });
|
|
let mut args = vec![
|
|
input.to_str().unwrap(),
|
|
"--out",
|
|
out.to_str().unwrap(),
|
|
"--no-log",
|
|
"--no-pause",
|
|
];
|
|
if let Some(flag) = extra {
|
|
args.push(flag);
|
|
}
|
|
let output = run(&args);
|
|
assert_eq!(output.status.code(), Some(1), "{output:?}");
|
|
assert!(
|
|
!out.join("plain.unpack.bin").exists(),
|
|
"an error must not leave an artifact"
|
|
);
|
|
let text = String::from_utf8_lossy(&output.stdout);
|
|
assert!(text.contains("0 unpacked"));
|
|
assert!(text.contains("1 errors"));
|
|
assert!(text.contains("0 suspect"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn double_quiet_stays_silent_for_errors_in_both_modes() {
|
|
let td = tempfile::tempdir().unwrap();
|
|
let input = write_plain(td.path());
|
|
let out = td.path().join("out");
|
|
for extra in [None, Some("--strict-integrity")] {
|
|
let mut args = vec![
|
|
input.to_str().unwrap(),
|
|
"--out",
|
|
out.to_str().unwrap(),
|
|
"--no-log",
|
|
"--no-pause",
|
|
"-q",
|
|
"-q",
|
|
];
|
|
if let Some(flag) = extra {
|
|
args.push(flag);
|
|
}
|
|
let output = run(&args);
|
|
assert_eq!(output.status.code(), Some(1));
|
|
assert_silent(&output);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn single_quiet_keeps_the_summary_and_the_error() {
|
|
let td = tempfile::tempdir().unwrap();
|
|
let input = write_plain(td.path());
|
|
let out = td.path().join("out");
|
|
let output = run(&[
|
|
input.to_str().unwrap(),
|
|
"--out",
|
|
out.to_str().unwrap(),
|
|
"--no-log",
|
|
"--no-pause",
|
|
"-q",
|
|
"--strict-integrity",
|
|
]);
|
|
assert_eq!(output.status.code(), Some(1));
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
// One -q only suppresses per-file lines. The banner and the summary still
|
|
// print; that is the existing quiet contract and strict mode must not
|
|
// change it.
|
|
assert!(stdout.contains("Senbei "), "{stdout}");
|
|
assert!(stdout.contains("1 errors"), "{stdout}");
|
|
assert!(stdout.contains("0 suspect"), "{stdout}");
|
|
}
|
|
|
|
#[test]
|
|
fn strict_does_not_change_what_the_log_records() {
|
|
let td = tempfile::tempdir().unwrap();
|
|
let input = write_plain(td.path());
|
|
let out = td.path().join("out");
|
|
let output = run(&[
|
|
input.to_str().unwrap(),
|
|
"--out",
|
|
out.to_str().unwrap(),
|
|
"--strict-integrity",
|
|
"--no-pause",
|
|
"-q",
|
|
"-q",
|
|
]);
|
|
assert_eq!(output.status.code(), Some(1));
|
|
assert_silent(&output);
|
|
let logs = list_logs(&out);
|
|
assert_eq!(logs.len(), 1);
|
|
let text = std::fs::read_to_string(&logs[0]).unwrap();
|
|
assert!(text.contains("summary:"));
|
|
assert!(text.contains("1 errors"));
|
|
assert!(text.contains("0 suspect"));
|
|
}
|