use brick::scan; use serde_json::Value; use std::fs; use std::path::Path; use std::process::Command; fn fixture(name: &str) -> String { Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/policy") .join(name) .display() .to_string() } #[test] fn check_evaluates_policy_with_complete_evidence_and_clean_negative() { let directory = tempfile::tempdir().unwrap(); let project = directory.path().join("project"); fs::create_dir_all(project.join("src/api")).unwrap(); fs::create_dir_all(project.join("src/storage")).unwrap(); fs::write( project.join("src/api/handler.rs"), "fn handle() { crate::storage::db::query(); }\n", ) .unwrap(); fs::write(project.join("src/storage/db.rs"), "pub fn query() {}\n").unwrap(); let model = scan::scan(&project).unwrap(); let graph = directory.path().join("graph.json"); fs::write(&graph, serde_json::to_vec_pretty(&model).unwrap()).unwrap(); let violation_result = directory.path().join("violation-result.json"); let violating = Command::new(env!("CARGO_BIN_EXE_brick")) .args([ "check", graph.to_str().unwrap(), "--min-score", "101", "--policy", &fixture("forbid-api-storage.json"), "--policy-result-json", violation_result.to_str().unwrap(), ]) .output() .unwrap(); assert!(!violating.status.success()); let violation_stderr = String::from_utf8_lossy(&violating.stderr); assert!(violation_stderr.contains("score")); assert!(violation_stderr.contains("api-must-not-call-storage matched 1 dependencies; max 0")); let result: Value = serde_json::from_slice(&fs::read(violation_result).unwrap()).unwrap(); assert_eq!(result["policy_result_schema_version"], 1); assert_eq!(result["policy_schema_version"], 1); assert_eq!(result["building_schema_version"], 4); assert_eq!(result["passed"], false); assert_eq!( result["rules"][0]["matched_edges"] .as_array() .unwrap() .len(), 1 ); assert_eq!( result["rules"][0]["matched_edges"][0]["edge"]["evidence"][0]["backend"], "tree-sitter-rust:0.24.2:brick-source-v1:heuristic-resolver-v2" ); let clean_result = directory.path().join("clean-result.json"); let clean = Command::new(env!("CARGO_BIN_EXE_brick")) .args([ "check", graph.to_str().unwrap(), "--min-score", "0", "--policy", &fixture("clean-ui-storage.json"), "--policy-result-json", clean_result.to_str().unwrap(), ]) .output() .unwrap(); assert!( clean.status.success(), "{}", String::from_utf8_lossy(&clean.stderr) ); let clean_value: Value = serde_json::from_slice(&fs::read(clean_result).unwrap()).unwrap(); assert_eq!(clean_value["passed"], true); assert!( clean_value["rules"][0]["matched_edges"] .as_array() .unwrap() .is_empty() ); } #[test] fn check_rejects_policy_output_without_policy_and_unknown_wire_values() { let directory = tempfile::tempdir().unwrap(); fs::write(directory.path().join("lib.rs"), "fn example() {}\n").unwrap(); let graph = directory.path().join("graph.json"); fs::write( &graph, serde_json::to_vec_pretty(&scan::scan(directory.path()).unwrap()).unwrap(), ) .unwrap(); let missing = Command::new(env!("CARGO_BIN_EXE_brick")) .args([ "check", graph.to_str().unwrap(), "--min-score", "0", "--policy-result-json", directory.path().join("result.json").to_str().unwrap(), ]) .output() .unwrap(); assert!(!missing.status.success()); assert!(String::from_utf8_lossy(&missing.stderr).contains("requires --policy")); for invalid in ["invalid-unknown-kind.json", "invalid-unknown-key.json"] { let output = Command::new(env!("CARGO_BIN_EXE_brick")) .args([ "check", graph.to_str().unwrap(), "--min-score", "0", "--policy", &fixture(invalid), ]) .output() .unwrap(); assert!(!output.status.success(), "{invalid}"); assert!(String::from_utf8_lossy(&output.stderr).contains("invalid architecture policy")); } }