xtask/tests/score.rs
//! **`cargo xtask score`: each scoring rule, on small fixture trees.**
//!
//! Every tree here is written to a fresh temporary directory and scored with
//! [`xtask::score::score`]. The scorer reads files only, so a fixture is just files.
//! `good` meets every bar (30 of 30); `bad` misses every bar (6 of 30); the other
//! tests change one thing and name the check that must move.
use std::fs;
use std::path::{Path, PathBuf};
use xtask::score::{self, Area, Card};
/// A fixture: `<tmp>/xtask-score-<name>-<pid>/monetize` is the repository root, and
/// `<tmp>/…/sib` is a sibling checkout beside it.
struct Tree {
top: PathBuf,
}
impl Tree {
fn new(name: &str) -> Tree {
let top = std::env::temp_dir().join(format!("xtask-score-{name}-{}", std::process::id()));
let _ = fs::remove_dir_all(&top);
fs::create_dir_all(top.join("monetize")).unwrap();
Tree { top }
}
fn root(&self) -> PathBuf {
self.top.join("monetize")
}
/// Write `text` at `rel` under the root (`../` reaches the siblings).
fn file(&self, rel: &str, text: &str) -> &Tree {
let p = self.root().join(rel);
fs::create_dir_all(p.parent().unwrap()).unwrap();
fs::write(&p, text).unwrap();
self
}
fn remove(&self, rel: &str) -> &Tree {
let p = self.root().join(rel);
if p.is_dir() {
fs::remove_dir_all(&p).unwrap();
} else {
fs::remove_file(&p).unwrap();
}
self
}
fn score(&self) -> Card {
score::score(&self.root())
}
}
impl Drop for Tree {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.top);
}
}
fn krate(name: &str) -> String {
format!("[package]\nname = \"{name}\"\nversion.workspace = true\nedition = \"2021\"\n")
}
fn tests_text(n: usize) -> String {
let mut s = String::new();
for i in 0..n {
s.push_str(&format!("#[test]\nfn t{i}() {{ assert_eq!(1, 1); }}\n"));
}
s
}
fn guide(op_names: &[&str], alert_table: bool, checklist: bool) -> String {
let mut g = String::from("# The guide\n\n### 5.1 The guest op table\n\n| Op | Name | Asked | Answer |\n|---|---|---|---|\n");
for (i, n) in op_names.iter().enumerate() {
g.push_str(&format!("| {} | `{n}` | at load | x |\n", i + 1));
}
g.push_str("\n### 5.2 The lifecycle\n\nText.\n");
if alert_table {
g.push_str("\n## Alerts\n\n| Code | Severity | Who |\n|---|---|---|\n| stall | high | operator |\n");
}
if checklist {
g.push_str("\n## 11. The release checklist\n\n1. Freeze.\n");
}
for i in 0..300 {
g.push_str(&format!("Line {i} of the guide.\n"));
}
g
}
/// A tree that meets every bar.
fn good(name: &str) -> Tree {
let t = Tree::new(name);
t.file(
"Cargo.toml",
"[workspace]\nmembers = [\"crates/*\"]\n\n[workspace.package]\nversion = \"0.2.0\"\n",
)
.file(
"Cargo.lock",
"version = 4\n\n[[package]]\nname = \"monetize-core\"\nversion = \"0.2.0\"\ndependencies = [\n \"sib-crate\",\n]\n\n[[package]]\nname = \"sib-crate\"\nversion = \"1.4.0\"\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\n",
)
.file("README.md", "# monetize\n")
.file("docs/integration-guide.md", &guide(&["id", "meters"], true, true))
.file(
"xtask/src/main.rs",
"fn task_ci() { let s = vec![nornir_xtask_support::lockcheck::step(&root), Step::cargo(\"test\", &[\"test\", \"--workspace\", \"--locked\"])]; }\n",
)
// The sibling checkout and the in-tree crate that path-depends on it.
.file("../sib/crates/sib-crate/Cargo.toml", "[package]\nname = \"sib-crate\"\nversion = \"1.4.0\"\n")
.file(
"crates/core/Cargo.toml",
&format!("{}\n[dependencies]\nsib-crate = {{ path = \"../../../sib/crates/sib-crate\" }}\n", krate("monetize-core")),
)
.file("crates/core/README.md", "# core\n");
// Runtime code: no unwrap, no spawn, named refusals, alerts, counters, the sandbox.
let mut lib = String::from("//! The core.\n\npub struct OpCounters { pub calls: u64 }\n\n");
for i in 0..60 {
lib.push_str(&format!(
"pub fn r{i}() -> String {{ format!(\"REFUSED [code-{i}] no\") }}\n"
));
lib.push_str(&format!(
"pub fn a{i}() {{ eprintln!(\"ALERT estate {i} is stuck\"); }}\n"
));
}
lib.push_str(
"#[cfg(test)]\nmod tests {\n #[test]\n fn inner() { Some(1).unwrap(); }\n}\n",
);
t.file("crates/core/src/lib.rs", &lib)
.file("crates/core/tests/many.rs", &tests_text(1000))
// The three trait suites, each run by a test.
.file("crates/monetize-product-guest/Cargo.toml", &krate("monetize-product-guest"))
.file(
"crates/monetize-product-guest/src/lib.rs",
"//! The guest SDK.\npub mod conformance;\npub mod ops {\n pub const ID: u32 = 1;\n pub const METERS: u32 = 2;\n pub const ALL: &[(u32, &str)] = &[\n (ID, \"id\"),\n (METERS, \"meters\"),\n ];\n}\npub mod grants {\n pub const HTTP: &str = \"http\";\n pub const HMAC: &str = \"hmac\";\n pub const VOCABULARY: &[&str] = &[HTTP, HMAC];\n pub const PRODUCT: &[&str] = &[];\n pub const VENDOR: &[&str] = &[HTTP, HMAC];\n}\n",
)
.file("crates/monetize-product-guest/src/conformance.rs", "//! The product suite.\npub fn six_ops() {}\n")
.file("crates/monetize-product-guest/tests/conformance.rs", "#[test]\nfn lake() { monetize_product_guest::conformance::six_ops(); }\n")
.file("crates/monetize-cloud/Cargo.toml", &krate("monetize-cloud"))
.file("crates/monetize-cloud/src/lib.rs", "//! Clouds.\npub mod conformance;\n")
.file("crates/monetize-cloud/src/conformance.rs", "//! The cloud suite.\npub fn leaves_nothing() {}\n")
.file("crates/monetize-cloud/tests/sandbox.rs", "use monetize_cloud::conformance;\n#[test]\nfn sandbox() { conformance::leaves_nothing(); }\n")
.file("crates/monetize-pay/Cargo.toml", &krate("monetize-pay"))
.file("crates/monetize-pay/src/lib.rs", "//! Payments.\npub mod conformance;\n")
.file("crates/monetize-pay/src/conformance.rs", "//! The payment suite.\npub fn refund() {}\n")
.file("crates/monetize-pay/tests/sandbox.rs", "#[test]\nfn sandbox() { monetize_pay::conformance::refund(); }\n")
.file("crates/monetize-product-wasm/Cargo.toml", &krate("monetize-product-wasm"))
.file(
"crates/monetize-product-wasm/src/lib.rs",
"//! The guest host.\npub struct Limits { pub fuel: u64, pub deadline: u64, pub max_memory_bytes: u64 }\npub const BODY_CAP: usize = 16;\npub fn hosts(http_hosts: &[String]) -> usize { http_hosts.len() }\npub fn roots(guest_trust_roots: &str) -> String { format!(\"{guest_trust_roots}.p7s\") }\n",
)
.file("crates/monetize-product-wasm/tests/security.rs", "#[test]\nfn a_changed_digest_is_refused() {}\n");
t
}
/// A tree that misses every bar.
fn bad(name: &str) -> Tree {
let t = Tree::new(name);
t.file("Cargo.toml", "[workspace]\nmembers = [\"crates/*\"]\n\n[workspace.package]\nversion = \"0.2.0\"\n")
// The lock names the sibling at a version its manifest no longer carries.
.file(
"Cargo.lock",
"version = 4\n\n[[package]]\nname = \"monetize-core\"\nversion = \"0.2.0\"\n\n[[package]]\nname = \"sib-crate\"\nversion = \"1.3.0\"\n",
)
.file("../sib/crates/sib-crate/Cargo.toml", "[package]\nname = \"sib-crate\"\nversion = \"1.4.0\"\n")
.file(
"crates/core/Cargo.toml",
&format!("{}\n[dependencies]\nsib-crate = {{ path = \"../../../sib/crates/sib-crate\" }}\nmonetize-product-gunnar = {{ path = \"../../products/gunnar\" }}\n", krate("monetize-core")),
)
.file("docs/integration-guide.md", "# Short\n\nYour guest must still answer them for a ONE box that dials.\n")
.file(
"crates/core/src/lib.rs",
"pub fn f() { let v: Option<u8> = None; v.unwrap(); v.expect(\"x\"); }\npub fn g() { std::process::Command::new(\"sh\"); }\npub fn h(s: &S) { let _ = s.save_order(); }\npub fn k() { std::thread::spawn(|| {}); std::thread::spawn(|| {}); std::thread::spawn(|| {}); std::thread::spawn(|| {}); std::thread::spawn(|| {}); std::thread::spawn(|| {}); }\npub fn r() -> &'static str { \"REFUSED [one]\" }\n",
)
.file("crates/core/tests/plugin.rs", "#[test]\n#[ignore]\nfn native() { monetize_product_gunnar::plugin(); }\n")
.file("crates/monetize-product-guest/Cargo.toml", &krate("monetize-product-guest"))
.file(
"crates/monetize-product-guest/src/lib.rs",
"pub mod ops {\n pub const ALL: &[(u32, &str)] = &[\n (ID, \"id\"),\n ];\n}\npub mod grants {\n pub const ALL: &[&str] = &[HTTP, GRPC, SIGN, ENV, STATE, HMAC];\n}\n",
)
.file("crates/monetize-product-wasm/Cargo.toml", &krate("monetize-product-wasm"))
.file("crates/monetize-product-wasm/src/lib.rs", "pub fn load() {}\n")
.file("crates/a/Cargo.lock", "version = 4\n")
.file("crates/a/Cargo.toml", &krate("a"))
.file("crates/b/Cargo.lock", "version = 4\n")
.file("crates/b/Cargo.toml", &krate("b"))
.file("crates/c/Cargo.lock", "version = 4\n")
.file("crates/c/Cargo.toml", &krate("c"))
// A28 M1 (T14): the bar is five locks outside xtask; this tree has six.
.file("crates/d/Cargo.lock", "version = 4\n")
.file("crates/d/Cargo.toml", &krate("d"))
.file("crates/e/Cargo.lock", "version = 4\n")
.file("crates/e/Cargo.toml", &krate("e"));
t
}
fn check<'a>(card: &'a Card, id: &str) -> &'a score::Check {
card.areas
.iter()
.flat_map(|a| a.checks.iter())
.find(|c| c.id == id)
.unwrap_or_else(|| panic!("no check `{id}` in the card:\n{}", card.render()))
}
fn area(card: &Card, a: Area) -> u8 {
card.areas
.iter()
.find(|s| s.area == a)
.map(|s| s.score)
.unwrap()
}
#[test]
fn every_area_has_four_bars_and_a_baseline_from_a25() {
let t = good("shape");
let card = t.score();
assert_eq!(card.areas.len(), 6);
let names: Vec<&str> = card.areas.iter().map(|a| a.area.name()).collect();
assert_eq!(
names,
[
"tests",
"robustness",
"security",
"observability",
"docs",
"release"
]
);
for a in &card.areas {
assert_eq!(
a.checks.len(),
4,
"{} has {} bars",
a.area.name(),
a.checks.len()
);
assert_eq!(
a.score as usize,
1 + a.checks.iter().filter(|c| c.pass).count()
);
}
assert_eq!(
score::BASELINE.iter().map(|(_, b)| *b as u32).sum::<u32>(),
16,
"A25 §4: 16 of 30"
);
assert_eq!(card.max(), 30);
}
#[test]
fn a_tree_that_meets_every_bar_scores_30() {
let t = good("good");
let card = t.score();
let missed: Vec<String> = card
.areas
.iter()
.flat_map(|a| a.checks.iter())
.filter(|c| !c.pass)
.map(|c| format!("{}: {}", c.id, c.evidence.join("; ")))
.collect();
assert!(missed.is_empty(), "missed:\n{}", missed.join("\n"));
assert_eq!(card.total(), 30);
}
#[test]
fn a_tree_that_misses_every_bar_scores_6() {
let t = bad("bad");
let card = t.score();
let met: Vec<&str> = card
.areas
.iter()
.flat_map(|a| a.checks.iter())
.filter(|c| c.pass)
.map(|c| c.id)
.collect();
assert!(met.is_empty(), "met: {met:?}\n{}", card.render());
assert_eq!(card.total(), 6);
for a in &card.areas {
assert_eq!(a.score, 1);
}
}
#[test]
fn every_check_carries_evidence() {
for t in [good("ev-good"), bad("ev-bad")] {
let card = t.score();
for c in card.areas.iter().flat_map(|a| a.checks.iter()) {
assert!(!c.evidence.is_empty(), "{} has no evidence", c.id);
assert!(!c.bar.is_empty(), "{} has no bar", c.id);
}
}
}
#[test]
fn test_code_does_not_count_as_runtime_code() {
let t = good("cfgtest");
// An in-file test module, a test-gated module file, a test-gated item, a tests/
// directory and an examples/ directory: none of these is runtime code.
t.file(
"crates/core/src/extra.rs",
"//! Extra.\npub fn ok() -> u8 { 1 }\n#[cfg(test)]\nmod tests;\n#[cfg(any(test, feature = \"testkit\"))]\npub fn helper() { Some(1).unwrap(); let _ = save(); }\n#[cfg(test)]\nmod inline {\n fn x() { let s = \"}\"; Some(s).unwrap(); std::thread::spawn(|| {}); }\n fn y() { Some(2).expect(\"y\"); }\n}\npub fn after() -> u8 { 2 }\n",
)
.file("crates/core/src/extra/tests.rs", "fn z() { Some(1).unwrap(); std::process::Command::new(\"sh\"); }\n")
.file("crates/core/examples/demo.rs", "fn main() { Some(1).unwrap(); std::process::Command::new(\"sh\"); }\n")
.file("crates/core/tests/more.rs", "#[test]\nfn m() { Some(1).unwrap(); let _ = store.save(1); }\n");
let card = t.score();
assert!(check(&card, "robustness.unwrap").pass, "{}", card.render());
assert!(
check(&card, "robustness.no-spawn").pass,
"{}",
card.render()
);
assert!(
check(&card, "robustness.no-swallowed-save").pass,
"{}",
card.render()
);
assert!(check(&card, "robustness.threads").pass, "{}", card.render());
// …and the code after the test module is runtime code again.
t.file("crates/core/src/extra.rs", "//! Extra.\n#[cfg(test)]\nmod inline {\n fn x() {}\n}\npub fn after() { Some(2).unwrap(); }\n");
let card = t.score();
let c = check(&card, "robustness.unwrap");
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/src/extra.rs")),
"{c:?}"
);
}
/// R5a: a raw string in a test module (`r#" {name}=""#`) holds quotes and
/// braces that are text, not code. The mask read them as code: a test module ended in
/// the middle and its unwraps counted as runtime (estate-svg's and monetize-cli's, 32).
#[test]
fn a_raw_string_does_not_end_a_test_module() {
let t = good("rawstr");
t.file(
"crates/core/src/raw.rs",
"//! Raw.\npub fn ok() -> u8 { 1 }\n#[cfg(test)]\nmod tests {\n fn x() { let a = r#\" {name}=\"\"#; Some(a).unwrap(); }\n fn y() { let b = r#\"viewBox=\"0 0 1 2\"\"#; let c = r##\"}\"#\"##; Some(b).unwrap(); Some(c).unwrap(); }\n fn z() { Some(br#\"}\"#).unwrap(); }\n}\npub fn after() -> u8 { 2 }\n",
);
let card = t.score();
let c = check(&card, "robustness.unwrap");
assert!(
!c.evidence
.iter()
.any(|e| e.contains("crates/core/src/raw.rs")),
"{c:?}"
);
// …and the code after the module is runtime code again.
t.file("crates/core/src/raw.rs", "//! Raw.\n#[cfg(test)]\nmod tests {\n fn x() { let a = r#\"x=\"\"#; }\n}\npub fn after() { Some(2).unwrap(); }\n");
let card = t.score();
let c = check(&card, "robustness.unwrap");
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/src/raw.rs:6")),
"{c:?}"
);
}
#[test]
fn tooling_crates_are_not_runtime_code() {
let t = good("tooling");
t.file("crates/mock-upcloud/Cargo.toml", &krate("mock-upcloud"))
.file("crates/mock-upcloud/src/lib.rs", "//! Mock.\npub fn run() { std::process::Command::new(\"podman\"); Some(1).unwrap(); }\n")
.file("uitest/Cargo.toml", &krate("uitest"))
.file("uitest/src/lib.rs", "pub fn run() { std::process::Command::new(\"chrome\"); }\n");
let card = t.score();
assert!(
check(&card, "robustness.no-spawn").pass,
"{}",
card.render()
);
assert!(
check(&card, "docs.crates").pass,
"uitest is tooling: {}",
card.render()
);
}
#[test]
fn the_supervisor_may_spawn_and_nothing_else() {
let t = good("spawn");
t.file("crates/monetize-admin/Cargo.toml", &krate("monetize-admin"))
.file("crates/monetize-admin/README.md", "# admin\n")
.file(
"crates/monetize-admin/src/lib.rs",
"//! Admin.\npub mod supervise;\n",
)
.file(
"crates/monetize-admin/src/supervise.rs",
"pub fn keep() { std::process::Command::new(std::env::current_exe().ok()?); }\n",
);
assert!(check(&t.score(), "robustness.no-spawn").pass);
t.file(
"crates/monetize-admin/src/other.rs",
"pub fn bad() { tokio::process::Command::new(\"cargo\"); }\n",
);
let card = t.score();
let c = check(&card, "robustness.no-spawn");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/monetize-admin/src/other.rs:1")),
"{c:?}"
);
}
#[test]
fn a_swallowed_save_is_named_and_a_swallowed_send_is_not() {
let t = good("letunder");
// A send, a write to a buffer, and the mark-unused idiom over a tuple or a name
// (`let _ = (set, journal);`) swallow no save.
t.file(
"crates/core/src/send.rs",
"pub fn f(tx: &Tx) { let _ = tx.send(1); let _ = writeln!(out, \"x\"); }\npub fn g(set: S, journal: J) {\n let _ = (set, journal);\n let _ = &journal;\n let _ = journal;\n}\n",
);
let card = t.score();
assert!(
check(&card, "robustness.no-swallowed-save").pass,
"{}",
card.render()
);
t.file(
"crates/core/src/save.rs",
"pub fn f(s: &S) {\n let _ = s.journal.append(1);\n}\n",
);
let card = t.score();
let c = check(&card, "robustness.no-swallowed-save");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/src/save.rs:2")),
"{c:?}"
);
}
#[test]
fn the_unwrap_bar_is_per_thousand_runtime_lines() {
let t = good("density");
// 2,000 runtime lines and 2 unwraps: exactly the bar.
let mut body = String::from("//! Lines.\n");
for i in 0..1997 {
body.push_str(&format!("pub const C{i}: u32 = {i};\n"));
}
body.push_str("pub fn a() { Some(1).unwrap(); }\npub fn b() { Some(1).expect(\"b\"); }\n");
t.file("crates/core/src/lines.rs", &body);
let card = t.score();
assert!(check(&card, "robustness.unwrap").pass, "{}", card.render());
t.file(
"crates/core/src/one_more.rs",
"pub fn c() { Some(1).unwrap(); }\n",
);
assert!(!check(&t.score(), "robustness.unwrap").pass);
// unwrap_or, unwrap_or_default and unwrap_or_else are not unwraps.
t.file("crates/core/src/one_more.rs", "pub fn c() { Some(1).unwrap_or(2); Some(1).unwrap_or_default(); Some(1).unwrap_or_else(|| 3); }\n");
assert!(check(&t.score(), "robustness.unwrap").pass);
}
#[test]
fn an_ignored_test_misses_the_ignore_bar() {
let t = good("ignore");
t.file(
"crates/core/tests/slow.rs",
"#[test]\n#[ignore = \"needs KVM\"]\nfn slow() {}\n",
);
let card = t.score();
let c = check(&card, "tests.no-ignore");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/tests/slow.rs")),
"{c:?}"
);
}
#[test]
fn a_suite_that_nothing_runs_misses_the_conformance_bar() {
let t = good("conformance");
t.remove("crates/monetize-pay/tests/sandbox.rs");
let card = t.score();
let c = check(&card, "tests.conformance");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("payment") && e.contains("0 test")),
"{c:?}"
);
// A missing suite is named as missing.
t.remove("crates/monetize-cloud/src/conformance.rs");
let card = t.score();
let c = check(&card, "tests.conformance");
assert!(
c.evidence
.iter()
.any(|e| e.contains("cloud") && e.contains("missing")),
"{c:?}"
);
assert_eq!(
card.facts
.iter()
.find(|(k, _)| k == "conformance_suites_present")
.map(|(_, v)| *v),
Some(2)
);
}
#[test]
fn a_test_on_a_native_plugin_breaks_the_owners_rule() {
let t = good("native");
t.file(
"crates/core/tests/native.rs",
"#[test]\nfn n() { let l = Linked::commercial(); }\n",
);
let card = t.score();
let c = check(&card, "tests.production-code");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/tests/native.rs")),
"{c:?}"
);
assert_eq!(area(&card, Area::Tests), 4);
}
#[test]
fn lock_drift_names_the_lock_and_the_crate() {
let t = good("drift");
assert!(check(&t.score(), "release.lock-drift").pass);
// The sibling moved; the lock did not.
t.file(
"../sib/crates/sib-crate/Cargo.toml",
"[package]\nname = \"sib-crate\"\nversion = \"1.5.0\"\n",
);
let card = t.score();
let c = check(&card, "release.lock-drift");
assert!(!c.pass);
assert!(
c.evidence.iter().any(|e| e.contains("Cargo.lock")
&& e.contains("sib-crate 1.4.0")
&& e.contains("1.5.0")),
"{c:?}"
);
}
#[test]
fn lock_drift_cannot_be_proved_without_the_sibling() {
let t = good("nosib");
fs::remove_dir_all(t.top.join("sib")).unwrap();
let card = t.score();
let c = check(&card, "release.lock-drift");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("sib-crate") && e.contains("not checked out")),
"{c:?}"
);
}
#[test]
fn a_refused_crate_twice_in_a_lock_is_drift() {
let t = good("twice");
t.file(
"Cargo.lock",
"version = 4\n\n[[package]]\nname = \"monetize-core\"\nversion = \"0.2.0\"\n\n[[package]]\nname = \"sib-crate\"\nversion = \"1.4.0\"\n\n[[package]]\nname = \"korp-installer\"\nversion = \"0.2.6\"\nsource = \"registry+x\"\n\n[[package]]\nname = \"korp-installer\"\nversion = \"0.2.7\"\nsource = \"registry+x\"\n",
);
let c = check(&t.score(), "release.lock-drift").clone();
assert!(!c.pass);
assert!(
c.evidence.iter().any(|e| e.contains("korp-installer")),
"{c:?}"
);
}
#[test]
fn more_than_five_locks_outside_xtask_misses_the_structure_bar() {
// A28 M1 (T14): the root and five detached workspaces, xtask one of them, so five outside xtask.
let t = good("locks");
t.file(
"xtask/Cargo.toml",
"[package]\nname = \"xtask\"\nversion = \"0.1.0\"\n",
)
.file("xtask/Cargo.lock", "version = 4\n");
for c in ["a", "b", "d", "e"] {
t.file(&format!("crates/{c}/Cargo.toml"), &krate(c))
.file(&format!("crates/{c}/Cargo.lock"), "version = 4\n")
.file(&format!("crates/{c}/src/lib.rs"), "//! A crate.\n")
.file(&format!("crates/{c}/tests/t.rs"), "#[test]\nfn t() {}\n");
}
assert!(
check(&t.score(), "release.locks").pass,
"5 locks outside xtask is the bar"
);
t.file("crates/f/Cargo.toml", &krate("f"))
.file("crates/f/Cargo.lock", "version = 4\n");
let card = t.score();
let c = check(&card, "release.locks");
assert!(!c.pass);
assert!(c.evidence.iter().any(|e| e.contains("6")), "{c:?}");
}
#[test]
fn the_op_table_must_name_exactly_the_ops() {
let t = good("ops");
t.file("docs/integration-guide.md", &guide(&["id"], true, true));
let card = t.score();
let c = check(&card, "docs.op-table");
assert!(!c.pass);
assert!(c.evidence.iter().any(|e| e.contains("meters")), "{c:?}");
}
#[test]
fn a_known_stale_claim_is_found_where_it_is() {
let t = good("stale");
t.file(
"crates/core/src/wire.rs",
"/// Holger's are keep-only). Absent in a guest.\npub struct W;\n",
);
let card = t.score();
let c = check(&card, "docs.no-stale");
assert!(!c.pass);
assert!(
c.evidence
.iter()
.any(|e| e.contains("crates/core/src/wire.rs:1")),
"{c:?}"
);
}
#[test]
fn a_crate_without_docs_or_tests_is_named() {
let t = good("undoc");
t.file("crates/bare/Cargo.toml", &krate("bare"))
.file("crates/bare/src/lib.rs", "pub fn f() {}\n");
let card = t.score();
assert!(check(&card, "docs.crates")
.evidence
.iter()
.any(|e| e.contains("crates/bare")));
assert!(!check(&card, "docs.crates").pass);
assert!(!check(&card, "tests.suite").pass);
assert!(check(&card, "tests.suite")
.evidence
.iter()
.any(|e| e.contains("crates/bare")));
}
#[test]
fn the_dead_grants_miss_the_grant_bar() {
let t = good("grants");
t.file(
"crates/monetize-product-guest/src/lib.rs",
"//! SDK.\npub mod conformance;\npub mod ops {\n pub const ALL: &[(u32, &str)] = &[\n (ID, \"id\"),\n (METERS, \"meters\"),\n ];\n}\npub mod grants {\n pub const VOCABULARY: &[&str] = &[HTTP, SIGN, HMAC];\n pub const PRODUCT: &[&str] = &[HTTP, SIGN, HMAC];\n}\n",
);
let card = t.score();
let c = check(&card, "security.no-dead-grants");
assert!(!c.pass);
assert!(c.evidence.iter().any(|e| e.contains("SIGN")), "{c:?}");
}
/// ★ Owner decision 6 (2026-10-26): **`env` is no generic door.** A product kind that grants
/// nothing does not pass while the host's vocabulary still carries `env` for anyone.
#[test]
fn an_env_door_left_in_the_vocabulary_misses_the_grant_bar() {
let t = good("grants-env");
t.file(
"crates/monetize-product-guest/src/lib.rs",
"//! SDK.\npub mod conformance;\npub mod ops {\n pub const ALL: &[(u32, &str)] = &[\n (ID, \"id\"),\n (METERS, \"meters\"),\n ];\n}\npub mod grants {\n pub const VOCABULARY: &[&str] = &[HTTP, ENV, HMAC];\n pub const PRODUCT: &[&str] = &[];\n}\n",
);
let card = t.score();
let c = check(&card, "security.no-dead-grants");
assert!(!c.pass, "{c:?}");
assert!(
c.evidence
.iter()
.any(|e| e.contains("ENV") && e.contains("VOCABULARY")),
"{c:?}"
);
}
/// ★ Owner decision 6: **the vocabulary is split by guest kind.** One shared list (the old
/// `grants::ALL`) is not the product kind's set, however short it is.
#[test]
fn a_vocabulary_not_split_by_kind_misses_the_grant_bar() {
let t = good("grants-unsplit");
t.file(
"crates/monetize-product-guest/src/lib.rs",
"//! SDK.\npub mod conformance;\npub mod ops {\n pub const ALL: &[(u32, &str)] = &[\n (ID, \"id\"),\n (METERS, \"meters\"),\n ];\n}\npub mod grants {\n pub const ALL: &[&str] = &[HTTP, HMAC];\n}\n",
);
let card = t.score();
let c = check(&card, "security.no-dead-grants");
assert!(!c.pass, "{c:?}");
assert!(
c.evidence.iter().any(|e| e.contains("grants::PRODUCT")),
"{c:?}"
);
}
#[test]
fn score_json_is_written_with_every_area_and_check() {
let t = good("json");
let card = t.score();
let out = t.top.join("out/score.json");
score::write_json(&card, &out).unwrap();
let j = fs::read_to_string(&out).unwrap();
assert!(j.contains("\"total\": 30"), "{j}");
assert!(j.contains("\"baseline_total\": 16"), "{j}");
for a in [
"tests",
"robustness",
"security",
"observability",
"docs",
"release",
] {
assert!(
j.contains(&format!("\"area\": \"{a}\"")),
"{a} missing in {j}"
);
}
assert!(j.contains("\"id\": \"release.lock-drift\""));
// Balanced and escaped: the evidence carries quotes and backslashes.
assert_eq!(j.matches('{').count(), j.matches('}').count());
assert_eq!(score::json_string("a\"b\\c\n"), "\"a\\\"b\\\\c\\n\"");
}
#[test]
fn the_report_prints_each_area_with_its_evidence() {
let t = bad("render");
let text = t.score().render();
for a in [
"tests",
"robustness",
"security",
"observability",
"docs",
"release",
] {
assert!(text.contains(a), "{a} missing:\n{text}");
}
assert!(text.contains("6 / 30"), "{text}");
assert!(text.contains("baseline 16"), "{text}");
assert!(
text.contains("crates/core/src/lib.rs:1"),
"the evidence names the line:\n{text}"
);
}
#[test]
fn the_scorer_reads_only_the_tree() {
// No network, no cargo, no git: a tree with no .git scores, and twice the same.
let t = good("static");
assert!(!Path::new(&t.root().join(".git")).exists());
let (a, b) = (t.score(), t.score());
assert_eq!(a.to_json(), b.to_json());
}
/// ★ **The CI plan is read where it lives** (2026-10-25: monetize's plan moved from
/// `xtask/src/main.rs` to `xtask/src/ci.rs`, and the bar read it as missing): a main.rs that
/// only dispatches and a ci.rs that holds the steps meet the bar; a tree with neither does not.
#[test]
fn the_ci_bar_reads_the_plan_in_xtask_ci_rs() {
let t = good("ci-rs");
t.file(
"xtask/src/main.rs",
"fn task_ci() { xtask::ci::plan(&root); }\n",
);
let c = check(&t.score(), "release.ci").clone();
assert!(!c.pass, "the plan is nowhere yet: {c:?}");
t.file(
"xtask/src/ci.rs",
"pub fn plan() { let s = vec![nornir_xtask_support::lockcheck::step(root), Step::cargo(\"test\", &[\"test\", \"--workspace\", \"--locked\"])]; }\n",
);
let c = check(&t.score(), "release.ci").clone();
assert!(c.pass, "the plan in xtask/src/ci.rs: {c:?}");
assert!(
c.evidence.iter().any(|e| e.contains("xtask/src/ci.rs")),
"the evidence names where it read: {c:?}"
);
}