use std::path::Path;
use crate::audit::orchestrate::{self, AuditBundle};
use crate::finding::Finding;
use crate::scan::{scan_workspace, ScanReport};
#[derive(Debug, Default)]
pub struct RunControl {
scram: bool,
}
impl RunControl {
#[must_use]
pub const fn new() -> Self {
Self { scram: false }
}
pub const fn trip_scram(&mut self) {
self.scram = true;
}
#[must_use]
pub const fn should_continue(&self) -> bool {
!self.scram
}
}
#[derive(Debug)]
pub struct PipelineRun {
pub scan: ScanReport,
pub audit: AuditBundle,
pub findings: Vec<Finding>,
pub completed: bool,
}
pub fn run(root: &Path, control: &mut RunControl) -> std::io::Result<PipelineRun> {
let mut findings: Vec<Finding> = Vec::new();
let scan = scan_workspace(root, None)?;
for (i, mu) in scan.marked_unknowns.iter().enumerate() {
findings.push(mu.to_finding(i as u64));
}
if !control.should_continue() {
return Ok(PipelineRun {
scan,
audit: AuditBundle::default(),
findings,
completed: false,
});
}
let audit = orchestrate::run(&scan, root);
let _ = &mut findings;
Ok(PipelineRun {
scan,
audit,
findings,
completed: true,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scram_tripped_above_the_loop_aborts_before_audit() {
let mut tripped = RunControl::new();
tripped.trip_scram();
let out = run(Path::new("src"), &mut tripped).expect("scan walk succeeds");
assert!(!out.completed, "a tripped SCRAM must abort the run");
assert!(out.audit.audit.audits.is_empty());
}
#[test]
fn uninterrupted_run_completes_and_runs_both_stages() {
let mut control = RunControl::new();
let out = run(Path::new("src"), &mut control).expect("pipeline runs");
assert!(out.completed, "an untripped run completes");
let _ = &out.scan;
}
#[test]
fn marked_unknown_markers_emit_into_the_finding_population() {
use crate::finding::{ExistenceCertainty, FindingBody, Magnitude};
let mut control = RunControl::new();
let fixture = Path::new("tests")
.join("fixtures")
.join("marked_unknown_markers");
let out = run(&fixture, &mut control).expect("pipeline runs over the fixture");
assert!(out.completed);
let markers: Vec<_> = out
.findings
.iter()
.filter(|f| matches!(f.body, FindingBody::MarkedUnknown { .. }))
.collect();
assert_eq!(
markers.len(),
3,
"three markers emit into the population; got: {:?}",
out.findings
);
let red_flag = out
.findings
.iter()
.find(|f| f.source.ends_with("red-flag"))
.expect("a red-flag finding");
assert_eq!(red_flag.severity, crate::finding::Severity::High);
assert!(matches!(
red_flag.body,
FindingBody::MarkedUnknown {
existence_certainty: ExistenceCertainty::Sure,
..
}
));
let aura = out
.findings
.iter()
.find(|f| f.source.ends_with("aura"))
.expect("an aura finding");
assert!(matches!(
aura.body,
FindingBody::MarkedUnknown {
magnitude: Magnitude::Aura,
..
}
));
for f in &markers {
if let FindingBody::MarkedUnknown { trigger, .. } = &f.body {
assert!(!trigger.trim().is_empty(), "guard 3: non-empty trigger");
}
}
}
}