use std::path::{Path, PathBuf};
use anyhow::Result;
use crate::warehouse::iceberg::IcebergWarehouse;
use crate::warehouse::surface_coverage::GateReport;
use nornir_testmatrix::discover::SERVED_WORKSPACES;
use super::gate_coverage_for_repo;
#[derive(Debug, Clone)]
pub struct WorkspaceSpec {
pub workspace: String,
pub repo: String,
pub repo_root: PathBuf,
pub cli_commands: Vec<String>,
}
impl WorkspaceSpec {
pub fn new(workspace: impl Into<String>, repo_root: impl Into<PathBuf>) -> Self {
let workspace = workspace.into();
Self {
repo: workspace.clone(),
workspace,
repo_root: repo_root.into(),
cli_commands: Vec::new(),
}
}
}
pub fn served_specs(roots_dir: &Path) -> Vec<WorkspaceSpec> {
SERVED_WORKSPACES
.iter()
.map(|w| WorkspaceSpec::new(*w, roots_dir.join(w)))
.collect()
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceGate {
pub workspace: String,
pub coverage: GateReport,
pub utfallsrum: Option<(usize, usize)>,
pub reachable: Option<(usize, usize)>,
}
impl WorkspaceGate {
pub fn from_report(workspace: impl Into<String>, coverage: GateReport) -> Self {
Self { workspace: workspace.into(), coverage, utfallsrum: None, reachable: None }
}
pub fn with_utfallsrum(mut self, met: usize, total: usize) -> Self {
self.utfallsrum = Some((met, total));
self
}
pub fn with_reachable(mut self, green: usize, total: usize) -> Self {
self.reachable = Some((green, total));
self
}
pub fn is_green(&self) -> bool {
self.coverage.is_green() && self.reachable.map(|(g, t)| g == t).unwrap_or(true)
}
pub fn first_failure(&self) -> Option<String> {
if let Some(node) = self.coverage.gap.missing.first() {
return Some(format!("uncovered surface `{}`", node.key_str()));
}
if let Some(stale) = self.coverage.stale.first() {
return Some(format!("stale allowlist entry `{}`", stale.key));
}
if let Some((g, t)) = self.reachable {
if g != t {
return Some(format!("{}/{} resolved metro lines green", g, t));
}
}
None
}
}
#[derive(Debug, Clone, Default)]
pub struct MegaGateReport {
pub run_id: String,
pub workspaces: Vec<WorkspaceGate>,
}
impl MegaGateReport {
pub fn is_green(&self) -> bool {
self.workspaces.iter().all(|w| w.is_green())
}
pub fn first_failure(&self) -> Option<(String, String)> {
self.workspaces
.iter()
.find_map(|w| w.first_failure().map(|k| (w.workspace.clone(), k)))
}
pub fn summary(&self) -> String {
let green = self.workspaces.iter().filter(|w| w.is_green()).count();
format!(
"{}/{} workspaces green — {}",
green,
self.workspaces.len(),
if self.is_green() { "GREEN" } else { "RED (mega gate)" },
)
}
}
pub fn gather_all(
wh: &IcebergWarehouse,
served: &[WorkspaceSpec],
run_id: &str,
) -> Result<MegaGateReport> {
let mut workspaces = Vec::with_capacity(served.len());
for spec in served {
let state = gate_coverage_for_repo(
wh,
&spec.workspace,
&spec.repo,
&spec.repo_root,
spec.cli_commands.clone(),
run_id,
)?;
workspaces.push(WorkspaceGate::from_report(&spec.workspace, state.report));
}
Ok(MegaGateReport { run_id: run_id.to_string(), workspaces })
}
#[cfg(test)]
mod tests {
use super::*;
use nornir_testmatrix::coverage::{AllowEntry, GateReport};
use nornir_testmatrix::discover::{cli_commands, Surface, SurfaceNode};
use std::collections::BTreeSet;
fn surface_ab() -> Surface {
let mut s = Surface::new();
s.extend(cli_commands(["a", "b"]));
s
}
fn green_report(ws: &str) -> GateReport {
let surface = surface_ab();
let covered: BTreeSet<String> = surface.nodes.iter().map(SurfaceNode::key_str).collect();
GateReport::compute("r", ws, &surface, &covered, &Default::default())
}
fn red_report(ws: &str) -> GateReport {
let surface = surface_ab();
let covered: BTreeSet<String> = ["cli_command:a@na".to_string()].into_iter().collect();
GateReport::compute("r", ws, &surface, &covered, &Default::default())
}
#[test]
fn mega_is_green_iff_every_workspace_green() {
let report = MegaGateReport {
run_id: "r".into(),
workspaces: vec![
WorkspaceGate::from_report("alpha", green_report("alpha")),
WorkspaceGate::from_report("beta", green_report("beta")),
],
};
assert!(report.is_green(), "all-green sweep is green: {}", report.summary());
assert!(report.first_failure().is_none());
}
#[test]
fn mega_names_first_failing_workspace_and_key() {
let report = MegaGateReport {
run_id: "r".into(),
workspaces: vec![
WorkspaceGate::from_report("alpha", green_report("alpha")),
WorkspaceGate::from_report("beta", red_report("beta")), ],
};
assert!(!report.is_green());
let (ws, key) = report.first_failure().expect("a failure");
assert_eq!(ws, "beta", "names the offending workspace");
assert!(key.contains("cli_command:b@na"), "names the uncovered surface: {key}");
}
#[test]
fn reachable_shortfall_reds_a_coverage_green_workspace() {
let wg = WorkspaceGate::from_report("alpha", green_report("alpha")).with_reachable(1, 2);
assert!(!wg.is_green(), "an unmet reachable rollup reds the workspace");
assert_eq!(wg.first_failure().as_deref(), Some("1/2 resolved metro lines green"));
let wg2 = WorkspaceGate::from_report("alpha", green_report("alpha")).with_reachable(2, 2);
assert!(wg2.is_green());
}
#[test]
fn stale_allowlist_entry_is_named_as_the_failure() {
let surface = surface_ab();
let covered: BTreeSet<String> =
surface.nodes.iter().map(SurfaceNode::key_str).collect();
let allow = nornir_testmatrix::coverage::Allowlist {
entries: vec![AllowEntry { key: "cli_command:a@na".into(), reason: "old".into() }],
};
let report = GateReport::compute("r", "alpha", &surface, &covered, &allow);
let wg = WorkspaceGate::from_report("alpha", report);
assert!(!wg.is_green(), "a stale allowlist entry reds the workspace");
assert!(
wg.first_failure().unwrap().contains("stale allowlist entry `cli_command:a@na`"),
"names the stale entry: {:?}",
wg.first_failure(),
);
}
#[test]
fn served_specs_cover_the_canonical_dimension() {
let specs = served_specs(Path::new("/tmp/roots"));
assert_eq!(specs.len(), SERVED_WORKSPACES.len());
let nornir = specs.iter().find(|s| s.workspace == "nornir").unwrap();
assert_eq!(nornir.repo, "nornir");
assert_eq!(nornir.repo_root, PathBuf::from("/tmp/roots/nornir"));
}
}