use std::collections::BTreeSet;
use std::fs;
fn cited_test_names(doc: &str) -> BTreeSet<String> {
let mut names = BTreeSet::new();
for line in doc.lines() {
if !line.starts_with('|') {
continue;
}
let cells: Vec<&str> = line.split('|').map(str::trim).collect();
if cells.len() != 5 {
continue;
}
let test_cell = cells[2];
if let Some(name) = test_cell.strip_prefix('`').and_then(|c| c.strip_suffix('`')) {
if name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') && name.contains('_') {
names.insert(name.to_string());
}
}
}
names
}
#[test]
fn every_test_cited_by_the_threat_model_exists() {
let doc = fs::read_to_string("THREAT_MODEL.md").expect("THREAT_MODEL.md is missing");
let cited = cited_test_names(&doc);
assert!(
cited.len() >= 15,
"only {} citations parsed — the table format probably changed and this check has \
stopped guarding anything: {cited:?}",
cited.len()
);
let mut defined = BTreeSet::new();
let dirs = ["tests", "tests/unit", "tests/unit/server", "src"];
for dir in dirs {
let Ok(entries) = fs::read_dir(dir) else {
continue;
};
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_none_or(|e| e != "rs") {
continue;
}
let Ok(source) = fs::read_to_string(&path) else {
continue;
};
for line in source.lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("async fn ").or_else(|| line.strip_prefix("fn ")) else {
continue;
};
if let Some(name) = rest.split('(').next() {
defined.insert(name.trim().to_string());
}
}
}
}
let missing: Vec<&String> = cited.iter().filter(|name| !defined.contains(*name)).collect();
assert!(
missing.is_empty(),
"THREAT_MODEL.md cites tests that no longer exist: {missing:?}\n\
Either restore the test or remove the guarantee's row — a cited test that is gone \
means the guarantee is no longer proven."
);
}