fix/audit-findings #5
3 changed files with 82 additions and 1 deletions
fix(build,check): report empty scans instead of scoring them 100
A directory with no supported source (Go/Java only) built "1 nodes / 0 edges; score 100.0" with no diagnostic, and `check --min-score 90 --max-cycles 0` passed it. The one-line summary also hid diagnostics entirely: the yams run's 7,428 warnings (505 files with grammar errors) never appeared. - scan records a warning diagnostic when no source file was scanned; - `build` prints the diagnostic and warning counts in its summary and warns on stderr when the model is empty; - `check` fails a model that contains no scanned source files. Test: diff_cli::a_scan_with_no_supported_source_is_reported_and_fails_check fails before this change (no diagnostic; check passes).
commit
3cf4d7660a
27
src/main.rs
27
src/main.rs
|
|
@ -182,16 +182,28 @@ fn run(cli: Cli) -> Result<()> {
|
|||
.count();
|
||||
write_json(&building, &json)?;
|
||||
render::render(&building, &html, &cytoscape_url)?;
|
||||
let warnings = building
|
||||
.diagnostics
|
||||
.iter()
|
||||
.filter(|diagnostic| diagnostic.level == "warning")
|
||||
.count();
|
||||
println!(
|
||||
"built {} nodes / {} edges; score {:.1}; imported {} Fold findings / {} p4c diagnostics\nJSON: {}\nHTML: {}",
|
||||
"built {} nodes / {} edges; score {:.1}; imported {} Fold findings / {} p4c diagnostics; {} diagnostics ({} warnings)\nJSON: {}\nHTML: {}",
|
||||
building.metrics.node_count,
|
||||
building.metrics.edge_count,
|
||||
building.metrics.structural_health.score,
|
||||
imported_fold,
|
||||
imported_p4c,
|
||||
building.diagnostics.len(),
|
||||
warnings,
|
||||
json.display(),
|
||||
html.display()
|
||||
);
|
||||
if !has_source_files(&building) {
|
||||
eprintln!(
|
||||
"brick: warning: no supported source files were found; the model is empty and its score describes nothing"
|
||||
);
|
||||
}
|
||||
}
|
||||
Command::Mcp { input } => {
|
||||
let building = read_json(&input)?;
|
||||
|
|
@ -240,6 +252,11 @@ fn run(cli: Cli) -> Result<()> {
|
|||
);
|
||||
}
|
||||
let metrics = &recomputed.metrics;
|
||||
if !has_source_files(&building) {
|
||||
failures.push(
|
||||
"the model contains no scanned source files; nothing was analysed".into(),
|
||||
);
|
||||
}
|
||||
if metrics.structural_health.score < min_score {
|
||||
failures.push(format!(
|
||||
"score {:.1} is below {:.1}",
|
||||
|
|
@ -413,6 +430,14 @@ fn write_json(building: &Building, path: &PathBuf) -> Result<()> {
|
|||
.with_context(|| format!("cannot write graph {}", path.display()))
|
||||
}
|
||||
|
||||
/// At least one source file was scanned into the model.
|
||||
fn has_source_files(building: &Building) -> bool {
|
||||
building
|
||||
.nodes
|
||||
.iter()
|
||||
.any(|node| node.kind == brick::model::NodeKind::File)
|
||||
}
|
||||
|
||||
fn read_json(path: &PathBuf) -> Result<Building> {
|
||||
let raw = fs::read_to_string(path)
|
||||
.with_context(|| format!("cannot read graph {}", path.display()))?;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,14 @@ pub fn scan_with_options(root: &Path, options: &ScanOptions) -> Result<Building>
|
|||
state.walk(&root)?;
|
||||
state.resolve_relationships();
|
||||
state.annotate_indirect_uses();
|
||||
// An analysis of nothing must not read as a healthy repository.
|
||||
if !state.nodes.iter().any(|node| node.kind == NodeKind::File) {
|
||||
state.diagnostics.push(Diagnostic {
|
||||
level: "warning".into(),
|
||||
message: "no supported source files were found; the model is empty".into(),
|
||||
path: None,
|
||||
});
|
||||
}
|
||||
|
||||
let mut building = Building {
|
||||
schema_version: SCHEMA_VERSION,
|
||||
|
|
|
|||
|
|
@ -305,3 +305,51 @@ fn check_recomputes_metrics_instead_of_trusting_the_file() {
|
|||
);
|
||||
assert!(text.contains("stored metrics"), "{text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_scan_with_no_supported_source_is_reported_and_fails_check() {
|
||||
// A Go/Java-only directory built "1 nodes / 0 edges; score 100.0" with no
|
||||
// diagnostic, and `check --min-score 90 --max-cycles 0` passed: an analysis of
|
||||
// nothing looked like a perfectly healthy repository.
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let project = directory.path().join("project");
|
||||
fs::create_dir(&project).unwrap();
|
||||
fs::write(project.join("main.go"), "package main\nfunc main() {}\n").unwrap();
|
||||
fs::write(project.join("App.java"), "class App {}\n").unwrap();
|
||||
let json = directory.path().join("model.json");
|
||||
let html = directory.path().join("model.html");
|
||||
let build = Command::new(env!("CARGO_BIN_EXE_brick"))
|
||||
.args([
|
||||
"build",
|
||||
project.to_str().unwrap(),
|
||||
"--json",
|
||||
json.to_str().unwrap(),
|
||||
"--html",
|
||||
html.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(build.status.success());
|
||||
let stderr = String::from_utf8_lossy(&build.stderr);
|
||||
assert!(stderr.contains("no supported source files"), "{stderr}");
|
||||
let model: Value = serde_json::from_slice(&fs::read(&json).unwrap()).unwrap();
|
||||
assert!(
|
||||
model["diagnostics"]
|
||||
.as_array()
|
||||
.is_some_and(|d| d.iter().any(|d| d["message"]
|
||||
.as_str()
|
||||
.is_some_and(|m| m.contains("no supported source files"))))
|
||||
);
|
||||
let check = Command::new(env!("CARGO_BIN_EXE_brick"))
|
||||
.args([
|
||||
"check",
|
||||
json.to_str().unwrap(),
|
||||
"--min-score",
|
||||
"90",
|
||||
"--max-cycles",
|
||||
"0",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(!check.status.success(), "an empty model passed check");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue