use std::process::Command;
fn boundary_cmd() -> Command {
Command::new(env!("CARGO_BIN_EXE_boundary"))
}
fn fixture(name: &str) -> String {
format!("{}/tests/fixtures/{name}", env!("CARGO_MANIFEST_DIR"))
}
fn analyze_json(fixture_name: &str) -> serde_json::Value {
let path = fixture(fixture_name);
let output = boundary_cmd()
.args(["analyze", &path, "--format", "json"])
.output()
.unwrap_or_else(|e| panic!("failed to run boundary on {fixture_name}: {e}"));
let stdout = String::from_utf8_lossy(&output.stdout);
serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("invalid JSON from {fixture_name}: {e}\noutput: {stdout}"))
}
fn violation_kinds(result: &serde_json::Value) -> Vec<String> {
result["violations"]
.as_array()
.unwrap_or(&vec![])
.iter()
.filter_map(|v| v["kind"].as_object())
.flat_map(|o| o.keys().cloned())
.collect()
}
#[test]
fn active_record_strict_mode_produces_layer_boundary_violation() {
let result = analyze_json("fr20-active-record-strict");
let kinds = violation_kinds(&result);
assert!(
kinds.contains(&"LayerBoundary".to_string()),
"strict mode should report a LayerBoundary violation; got: {kinds:?}"
);
}
#[test]
fn active_record_permissive_mode_no_layer_boundary_violation() {
let result = analyze_json("fr20-active-record-permissive");
let kinds = violation_kinds(&result);
assert!(
!kinds.contains(&"LayerBoundary".to_string()),
"active-record mode should NOT report LayerBoundary; got: {kinds:?}"
);
}
#[test]
fn active_record_pattern_detection_entry_present() {
let result = analyze_json("fr20-active-record-strict");
let patterns = result["pattern_detection"]["patterns"]
.as_array()
.expect("pattern_detection.patterns should be an array");
let has_ar = patterns
.iter()
.any(|p| p["name"].as_str() == Some("active-record"));
assert!(
has_ar,
"pattern_detection.patterns should contain an 'active-record' entry"
);
}