use std::{error::Error, fs, process::Command};
#[test]
fn json_analysis_distinguishes_violations_from_incomplete_work() -> Result<(), Box<dyn Error>> {
let scratch = tempfile::tempdir()?;
fs::create_dir(scratch.path().join("src"))?;
fs::write(
scratch.path().join("src/lib.rs"),
"pub struct Unclassified;\n",
)?;
let manifest = scratch.path().join("Cargo.toml");
fs::write(
&manifest,
"[workspace]\n[package]\nname = \"unclassified\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
)?;
let output = Command::new(env!("CARGO_BIN_EXE_pipecircuit"))
.args(["arch", "check", "--format", "json", "--manifest-path"])
.arg(&manifest)
.output()?;
assert_eq!(output.status.code(), Some(1));
let report: serde_json::Value = serde_json::from_slice(&output.stdout)?;
assert!(
report
.get("findings")
.and_then(serde_json::Value::as_array)
.is_some_and(|findings| findings.iter().any(|finding| finding
.get("code")
.and_then(serde_json::Value::as_str)
== Some("arch.role.missing")))
);
fs::write(&manifest, "malformed manifest")?;
let output = Command::new(env!("CARGO_BIN_EXE_pipecircuit"))
.args(["arch", "check", "--format", "json", "--manifest-path"])
.arg(&manifest)
.output()?;
assert_eq!(output.status.code(), Some(2));
let report: serde_json::Value = serde_json::from_slice(&output.stdout)?;
assert_eq!(
report.get("status").and_then(serde_json::Value::as_str),
Some("incomplete")
);
assert!(!output.stderr.is_empty());
Ok(())
}
#[test]
fn installed_version_is_explicit_and_rejects_trailing_arguments() -> Result<(), Box<dyn Error>> {
for flag in ["--version", "-V"] {
let output = Command::new(env!("CARGO_BIN_EXE_pipecircuit"))
.arg(flag)
.output()?;
assert!(output.status.success());
assert_eq!(
String::from_utf8(output.stdout)?,
format!("pipecircuit {}\n", env!("CARGO_PKG_VERSION"))
);
assert!(output.stderr.is_empty());
}
let output = Command::new(env!("CARGO_BIN_EXE_pipecircuit"))
.args(["--version", "unexpected"])
.output()?;
assert_eq!(output.status.code(), Some(2));
assert!(output.stdout.is_empty());
assert!(String::from_utf8(output.stderr)?.contains("does not accept arguments"));
Ok(())
}
#[cfg(unix)]
#[test]
fn visualization_opens_a_saved_snapshot_and_preserves_violation_status()
-> Result<(), Box<dyn Error>> {
use std::os::unix::fs::PermissionsExt as _;
let scratch = tempfile::Builder::new()
.prefix("pipecircuit browser path ")
.tempdir()?;
fs::create_dir(scratch.path().join("src"))?;
fs::write(
scratch.path().join("src/lib.rs"),
"pub struct Unclassified;\n",
)?;
fs::write(
scratch.path().join("Cargo.toml"),
"[workspace]\n[package]\nname = \"unclassified\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
)?;
let launcher = scratch.path().join(if cfg!(target_os = "macos") {
"open"
} else {
"xdg-open"
});
fs::write(
&launcher,
"#!/bin/sh\nprintf '%s' \"$1\" > \"$PIPEC_TEST_OPENED\"\n",
)?;
fs::set_permissions(&launcher, fs::Permissions::from_mode(0o700))?;
let mut paths = vec![scratch.path().to_owned()];
paths.extend(std::env::split_paths(
&std::env::var_os("PATH").ok_or("missing PATH")?,
));
let marker = scratch.path().join("opened");
let output = Command::new(env!("CARGO_BIN_EXE_pipecircuit"))
.current_dir(scratch.path())
.args(["arch", "visualize"])
.env("PATH", std::env::join_paths(paths)?)
.env("TMPDIR", scratch.path())
.env("PIPEC_TEST_OPENED", &marker)
.output()?;
assert_eq!(output.status.code(), Some(1));
let stdout = String::from_utf8(output.stdout)?;
let saved = stdout
.lines()
.find_map(|line| line.strip_prefix("Saved view: "))
.ok_or("saved path missing")?;
assert!(fs::read_to_string(saved)?.contains("arch.role.missing"));
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
loop {
if fs::read_to_string(&marker).is_ok_and(|opened| opened == saved) {
break;
}
if std::time::Instant::now() >= deadline {
return Err("browser did not receive the saved file path".into());
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(output.stderr.is_empty());
Ok(())
}