//! CI guard for business assumptions. //! //! Catches at PR time what would otherwise only fail at prod boot: //! - `docs/business/assumptions.toml` parses //! - All consistency rules pass (sums, bounds, founding ≤ standard) //! - Every `{{ ... }}` marker in the live site-docs corpus resolves //! //! Run with: cargo test --test assumptions use docengine::Assumptions; // Canonical assumptions.toml ships with the repo at server/docs/business/. // Cargo runs tests with cwd = crate manifest dir (MNW/server/), so the // relative path is just docs/... — no traversal. const ASSUMPTIONS_PATH: &str = "docs/business/assumptions.toml"; const SITE_DOCS_PATH: &str = "site-docs/public"; #[test] fn real_assumptions_file_parses_and_validates() { let a = Assumptions::load(ASSUMPTIONS_PATH) .unwrap_or_else(|e| panic!("failed to load {ASSUMPTIONS_PATH}: {e}")); a.validate() .unwrap_or_else(|e| panic!("assumptions failed validation:\n{e}")); } #[test] fn every_marker_in_site_docs_resolves() { let a = Assumptions::load(ASSUMPTIONS_PATH).expect("load"); let mut failures = Vec::new(); visit_markdown(std::path::Path::new(SITE_DOCS_PATH), &mut |path, body| { if !body.contains("{{") { return; } if let Err(e) = a.substitute(body) { failures.push(format!(" {}: {e}", path.display())); } }); if !failures.is_empty() { panic!( "{} doc(s) contain unresolved {{{{ … }}}} markers:\n{}", failures.len(), failures.join("\n") ); } } fn visit_markdown(dir: &std::path::Path, f: &mut impl FnMut(&std::path::Path, &str)) { let Ok(entries) = std::fs::read_dir(dir) else { return; }; for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { visit_markdown(&path, f); } else if path.extension().and_then(|s| s.to_str()) == Some("md") && let Ok(body) = std::fs::read_to_string(&path) { f(&path, &body); } } }