#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::fs;
use std::path::Path;
fn workspace() -> &'static Path {
Path::new(env!("CARGO_MANIFEST_DIR"))
}
fn read(rel: &str) -> String {
fs::read_to_string(workspace().join(rel))
.unwrap_or_else(|e| panic!("read {rel}: {e}"))
}
fn cargo_package_field(field: &str) -> String {
let toml = read("Cargo.toml");
let mut in_package = false;
for line in toml.lines() {
let trimmed = line.trim();
if trimmed.starts_with('[') {
in_package = trimmed == "[package]";
continue;
}
if !in_package {
continue;
}
if let Some(rest) = trimmed.strip_prefix(&format!("{field} = ")) {
return rest
.trim()
.trim_start_matches('"')
.split('"')
.next()
.unwrap_or_default()
.to_string();
}
}
panic!("Cargo.toml [package] missing field `{field}`");
}
fn count_src_lib_tests() -> usize {
let mut count = 0;
walk_for_count(&workspace().join("src"), &mut count);
count
}
fn walk_for_count(dir: &Path, count: &mut usize) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let p = entry.path();
if p.is_dir() {
walk_for_count(&p, count);
} else if p.extension().is_some_and(|e| e == "rs") {
if let Ok(content) = fs::read_to_string(&p) {
for line in content.lines() {
let t = line.trim();
if t == "#[test]" || t.starts_with("#[tokio::test") {
*count += 1;
}
}
}
}
}
}
#[test]
fn readme_version_matches_cargo_toml() {
let cargo_version = cargo_package_field("version");
let readme = read("README.md");
let badge = format!("v{cargo_version}");
assert!(
readme.contains(&cargo_version) || readme.contains(&badge),
"Cargo.toml version `{cargo_version}` does not appear in README. \
Either bump the README claim or revert the Cargo.toml change. \
Soft-mention forms (~X.Y, latest, etc.) don't satisfy this gate."
);
}
#[test]
fn readme_lib_test_count_is_in_the_right_ballpark() {
let readme = read("README.md");
let actual = count_src_lib_tests();
let mut claim: Option<usize> = None;
for line in readme.lines() {
let lower = line.to_lowercase();
if !lower.contains("unit test") && !lower.contains("lib test") {
continue;
}
let mut buf = String::new();
for ch in line.chars() {
if ch.is_ascii_digit() {
buf.push(ch);
} else if !buf.is_empty() {
if ch == ',' {
continue;
}
break;
}
}
if let Ok(n) = buf.parse::<usize>() {
if n >= 100 {
claim = Some(n);
break;
}
}
}
let Some(claim) = claim else {
eprintln!(
"[docs_accuracy] README has no `N unit tests` / `N lib tests` \
claim — skipping. To enforce, add a claim like \
`1,685 unit tests`."
);
return;
};
let lower = claim.saturating_sub(claim * 15 / 100);
let upper = claim + claim * 15 / 100;
assert!(
actual >= lower && actual <= upper,
"README cites {claim} unit/lib tests but `src/` has {actual} \
`#[test]` annotations (±15% band: {lower}..={upper}). \
Update the README claim or investigate the test-count drift."
);
}
#[test]
fn readme_coverage_claims_match_ci_floors() {
let ci = read(".github/workflows/ci.yml");
let readme = read("README.md");
let floor = |name: &str| -> Option<u32> {
let needle = format!("{name}: \"");
let idx = ci.find(&needle)?;
let after = &ci[idx + needle.len()..];
let close = after.find('"')?;
after[..close].parse::<f32>().ok().map(|f| f as u32)
};
let regions = floor("COVERAGE_REGIONS_FLOOR")
.expect("ci.yml missing COVERAGE_REGIONS_FLOOR");
let readme_lower = readme.to_lowercase();
let region_pat = format!("{regions}% region");
assert!(
readme_lower.contains(®ion_pat),
"ci.yml COVERAGE_REGIONS_FLOOR is {regions}% but README's \
region-coverage claim doesn't match. Search: `{region_pat}`."
);
}
#[test]
fn readme_wcag_version_matches_accessibility_module() {
let accessibility = read("src/accessibility.rs");
let readme = read("README.md");
let wcag_version = ["2.2", "2.1", "2.0"]
.iter()
.find(|v| accessibility.contains(&format!("WCAG {v}")))
.copied()
.unwrap_or("2.1");
let claim = format!("WCAG {wcag_version}");
assert!(
readme.contains(&claim),
"src/accessibility.rs targets `{claim}` but README doesn't \
mention it. Update README or fix the module-level docstring."
);
}
#[test]
fn readme_msrv_matches_cargo_toml() {
let msrv = cargo_package_field("rust-version");
let readme = read("README.md");
let forms = [
format!("Rust {msrv}"),
format!("rust {msrv}"),
format!("MSRV: {msrv}"),
format!("MSRV {msrv}"),
format!("rust-version = \"{msrv}\""),
];
let found = forms.iter().any(|f| readme.contains(f));
if !found {
eprintln!(
"[docs_accuracy] README has no MSRV claim — soft gate. \
Cargo.toml MSRV is `{msrv}`. Consider adding to README \
so consumers see it without reading Cargo.toml."
);
}
}
#[test]
fn security_md_exists_and_mentions_disclosure() {
let security = read("SECURITY.md");
assert!(
security
.to_lowercase()
.contains("reporting a vulnerability")
|| security.to_lowercase().contains("disclos"),
"SECURITY.md must describe a vulnerability disclosure process"
);
}
#[test]
fn sbom_cyclondx_version_matches_module_claim() {
let sbom = read("src/sbom.rs");
let docstring_v = sbom.contains("CycloneDX 1.5");
let emit_v = sbom.contains("\"specVersion\": \"1.5\"");
assert!(
docstring_v && emit_v,
"src/sbom.rs CycloneDX version drift: docstring says 1.5 = {docstring_v}, \
emit says 1.5 = {emit_v}"
);
}
#[cfg(test)]
mod helpers {
use super::*;
#[test]
fn count_src_lib_tests_is_nonzero() {
let n = count_src_lib_tests();
assert!(n > 100, "expected >100 lib tests in src/, got {n}");
}
#[test]
fn cargo_package_name_is_ssg() {
assert_eq!(cargo_package_field("name"), "ssg");
}
}