use std::path::Path;
use std::process::Command;
fn boundary_cmd() -> Command {
Command::new(env!("CARGO_BIN_EXE_boundary"))
}
fn copy_fixture_to_tempdir(name: &str) -> tempfile::TempDir {
let tmpdir = tempfile::tempdir().expect("failed to create temp dir");
let src = std::path::PathBuf::from(format!(
"{}/tests/fixtures/{name}",
env!("CARGO_MANIFEST_DIR")
));
for entry in walkdir::WalkDir::new(&src) {
let entry = entry.expect("failed to read dir entry");
let rel = entry.path().strip_prefix(&src).unwrap();
let dest = tmpdir.path().join(rel);
if entry.file_type().is_dir() {
std::fs::create_dir_all(&dest).unwrap();
} else {
std::fs::copy(entry.path(), &dest).unwrap();
}
}
tmpdir
}
fn seed_history(dir: &Path, score: f64) {
let boundary_dir = dir.join(".boundary");
std::fs::create_dir_all(&boundary_dir).unwrap();
let history_path = boundary_dir.join("history.ndjson");
let line = format!(
r#"{{"timestamp":"2024-01-01T00:00:00Z","git_commit":null,"git_branch":null,"result":{{"score":{{"overall":{score},"structural_presence":100.0,"layer_conformance":100.0,"dependency_compliance":100.0,"interface_coverage":100.0}},"violations":[],"component_count":3,"dependency_count":0,"files_analyzed":3}}}}"#
);
std::fs::write(history_path, format!("{line}\n")).unwrap();
}
#[test]
fn progress_track_creates_snapshot_that_persists() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
let path = tmpdir.path().to_str().unwrap();
let track = boundary_cmd()
.args(["check", path, "--track"])
.output()
.expect("failed to run boundary check --track");
assert!(
track.status.success(),
"check --track should exit 0 before asserting on follow-up"
);
let check = boundary_cmd()
.args(["check", path, "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
assert!(
check.status.success(),
"subsequent --no-regression should exit 0 after snapshot was saved with --track"
);
}
#[test]
fn progress_track_creates_file_at_known_path() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--track"])
.output()
.expect("failed to run boundary check --track");
assert!(
tmpdir.path().join(".boundary/history.ndjson").exists(),
"snapshot file should exist at .boundary/history.ndjson after --track"
);
}
#[test]
fn progress_no_regression_no_history_exits_zero() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
let output = boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
assert!(
output.status.success(),
"--no-regression should exit 0 when no baseline has been established"
);
}
#[test]
fn progress_no_regression_improved_score_exits_zero() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
seed_history(tmpdir.path(), 75.0);
let output = boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"--no-regression should exit 0 when score improved from 75 to 100: stdout={stdout}"
);
}
#[test]
fn progress_no_regression_unchanged_score_exits_zero() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
seed_history(tmpdir.path(), 70.0);
let output = boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"--no-regression should exit 0 when score is unchanged at 100: stdout={stdout}"
);
}
#[test]
fn progress_no_regression_score_dropped_exits_nonzero() {
let tmpdir = copy_fixture_to_tempdir("adapters-override");
seed_history(tmpdir.path(), 90.0);
let output = boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
assert!(
!output.status.success(),
"--no-regression should exit non-zero when score dropped from 90 to 80"
);
}
#[test]
fn progress_regression_report_includes_scores() {
let tmpdir = copy_fixture_to_tempdir("adapters-override");
seed_history(tmpdir.path(), 90.0);
let output = boundary_cmd()
.args(["check", tmpdir.path().to_str().unwrap(), "--no-regression"])
.output()
.expect("failed to run boundary check --no-regression");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
combined.contains("90"),
"output should include the previous score (90): {combined}"
);
assert!(
combined.contains("49") || combined.contains("50"),
"output should include the current score (~49): {combined}"
);
}
#[test]
fn progress_track_and_no_regression_appends_snapshot() {
let tmpdir = copy_fixture_to_tempdir("full-ddd-module");
seed_history(tmpdir.path(), 100.0);
let output = boundary_cmd()
.args([
"check",
tmpdir.path().to_str().unwrap(),
"--track",
"--no-regression",
])
.output()
.expect("failed to run boundary check --track --no-regression");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"--track --no-regression should exit 0 when score is unchanged: stdout={stdout}"
);
let history = std::fs::read_to_string(tmpdir.path().join(".boundary/history.ndjson")).unwrap();
let entry_count = history.lines().filter(|l| !l.trim().is_empty()).count();
assert_eq!(
entry_count, 2,
"snapshot history should contain 2 entries after --track appends: {history}"
);
}