xtask/tests/readme_claims.rs
//! ★ **README-2 (owner 2026-10-26): every feature the front page claims has a test.**
//!
//! The front page's claims (the bullet list under the engine sentence, the long comma list
//! split into its items, and the engine sentence itself) are mapped here, one row each, to a
//! real test in this tree that holds the claim. The test is red when:
//!
//! * the README claims something with no row here (a new claim needs a test first);
//! * a row's claim is no longer in the README (the table may not outlive the words);
//! * a row's test is not in its file as a test (`fn <name>(` with `#[test]` or
//! `#[tokio::test]` just above it).
//!
//! README-1 (same day): "geo-replication" overstated it. A gunnar.rs instance's twin runs in
//! the same zone as its main (the Gunnar guest lays front, main and twin in ONE zone); a
//! self-hoster joins a twin anywhere that reaches the primary (`gunnar control twin join`).
//! The page now says "replication to a twin", and where the twin can be.
//!
//! Both `.nornir/README.md` and the generated root `README.md` are read: the root is the
//! source rendered verbatim under a two-line header.
use std::path::{Path, PathBuf};
/// `(the claim as the README words it, the file holding its test, the test)`.
const CLAIMS: &[(&str, &str, &str)] = &[
(
"two engine variants",
"crates/gunnar-server/src/storage.rs",
"the_engine_is_reported_as_name_description_and_source_repository",
),
(
"Built-in replication to a twin gunnar: self-hosted, the twin is any box that reaches the primary (`gunnar control twin join`); a gunnar.rs instance's twin runs in the same zone as its main",
"crates/gunnar-cli/tests/twin_push_is_framed.rs",
"a_pushed_pack_is_a_frame_before_its_refs_and_the_twin_and_a_restore_hold_it",
),
(
"Vetra hardware appliance variant: OS-less, hardened kernel",
"crates/gunnar-cli/tests/feature_split.rs",
"the_appliance_links_no_worktree_no_client_transport_and_no_subprocess",
),
(
"Encrypted repositories, quantum-safe by default: files are sealed on the client, the key ring is wrapped only to members' ML-KEM-768 + X25519 keys, and the server holds ciphertext only",
"crates/gunnar-cli/tests/encrypted_repo.rs",
"crypt_init_starts_quantum_safe_by_default",
),
("Free to host your own", "crates/gunnar/src/main.rs", "a_bare_gunnar_serves_on_loopback"),
("NVMe", "crates/gunnar-recipe/tests/cli_matrix.rs", "the_build_kernel_line_plans_the_donor_first"),
("io_uring", "crates/gunnar-server/src/storage/writer.rs", "select_is_decided_once_and_never_builds_uring_the_kernel_refuses"),
("XFS on raid0", "crates/gunnar-server/tests/slow_disk_placement.rs", "an_unsigned_config_is_read_and_reported_as_integrity_only"),
("OS-less appliance kernel", "crates/gunnar-recipe/tests/machine_named.rs", "the_shipped_kernel_already_has_both_symbols_the_machine_would_add"),
(
"WireGuard",
"crates/gunnar-server/src/pairing.rs",
"a_wireguard_box_with_no_fed_front_asks_the_door_again_before_raising_the_tunnel",
),
("Prometheus", "crates/gunnar-cli/tests/metrics_endpoint.rs", "a_client_scrapes_every_counter_with_its_kind"),
("Git LFS", "crates/gunnar-cli/tests/lfs_http.rs", "a_clone_over_http_materialises_its_lfs_content_and_a_push_puts_more_back"),
("smart HTTP", "crates/gunnar-cli/tests/http_push_is_attributable.rs", "a_push_names_its_credential_over_ssh_and_over_http"),
("webhooks", "crates/gunnar-server/tests/lean_egress.rs", "a_webhook_delivery_leaves_through_the_door"),
(
"replication to a twin",
"crates/gunnar-cli/tests/twin_push_is_framed.rs",
"a_pushed_pack_is_a_frame_before_its_refs_and_the_twin_and_a_restore_hold_it",
),
("sshsig/ed25519 signed pushes", "crates/gunnar-cli/tests/verify_chain.rs", "tampered_unknown_and_unverifiable_signatures_are_kept_apart"),
("transparency log", "crates/gunnar-cli/tests/transparency.rs", "a_push_proves_its_place_in_the_log_offline"),
];
/// Bullets that are no claim about the code, and why.
const NOT_CODE_CLAIMS: &[(&str, &str)] = &[(
"The exact same code is hosted at https://www.gunnar.rs",
"where it is hosted, not what the code does",
)];
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("the workspace root")
.to_owned()
}
/// The README's claims: each bullet of the first list (the long comma list split into its
/// items), and "two engine variants" when the engine sentence says it.
fn claims(readme: &str) -> Vec<String> {
let mut out = Vec::new();
if readme.contains("two engine variants") {
out.push("two engine variants".to_owned());
}
let mut in_list = false;
for line in readme.lines() {
if let Some(item) = line.strip_prefix("* ") {
in_list = true;
let item = item.trim().trim_end_matches('.');
if item.matches(", ").count() >= 4 {
out.extend(item.split(", ").map(|c| c.trim().to_owned()));
} else {
out.push(item.to_owned());
}
} else if in_list {
break;
}
}
out
}
/// Whether `file` holds `name` as a test: `fn name(` with a test attribute in the six lines
/// above it.
fn is_a_test(file: &str, name: &str) -> bool {
let lines: Vec<&str> = file.lines().collect();
lines.iter().enumerate().any(|(i, l)| {
l.contains(&format!("fn {name}("))
&& lines[i.saturating_sub(6)..i]
.iter()
.any(|a| a.contains("#[test]") || a.contains("#[tokio::test"))
})
}
#[test]
fn every_readme_claim_has_a_test_and_every_row_a_claim() {
for page in [".nornir/README.md", "README.md"] {
let readme = std::fs::read_to_string(root().join(page)).expect("the README");
let said = claims(&readme);
assert!(
said.len() >= 15,
"{page}: the claims were not found ({} of them): {said:?}",
said.len()
);
let unmapped: Vec<&String> = said
.iter()
.filter(|c| {
!CLAIMS.iter().any(|(claim, _, _)| claim == c)
&& !NOT_CODE_CLAIMS.iter().any(|(claim, _)| claim == c)
})
.collect();
assert!(
unmapped.is_empty(),
"{page} claims {unmapped:?} with no test in this table: add a real test, or say less"
);
let gone: Vec<&str> = CLAIMS
.iter()
.map(|(c, _, _)| *c)
.filter(|c| !said.iter().any(|s| s == c))
.collect();
assert!(
gone.is_empty(),
"{page} no longer claims {gone:?}: drop the rows"
);
}
}
#[test]
fn every_rows_test_is_in_its_file() {
let mut missing = Vec::new();
for (claim, path, name) in CLAIMS {
match std::fs::read_to_string(root().join(path)) {
Ok(file) if is_a_test(&file, name) => {}
Ok(_) => missing.push(format!("{claim}: {path} has no test `{name}`")),
Err(e) => missing.push(format!("{claim}: {path}: {e}")),
}
}
assert!(
missing.is_empty(),
"claims whose test is gone:\n {}",
missing.join("\n ")
);
}
#[test]
fn the_claim_reader_and_the_test_finder_read_what_they_say() {
let page = "Two lines, with two engine variants here.\n\n* One thing.\n* a, b, c, d, e.\n\n## Next\n* not a claim\n";
assert_eq!(
claims(page),
["two engine variants", "One thing", "a", "b", "c", "d", "e"]
);
assert!(is_a_test("#[test]\nfn a_test() {}", "a_test"));
assert!(is_a_test(
" #[tokio::test(flavor = \"multi_thread\")]\n async fn b() {}",
"b"
));
assert!(
!is_a_test("fn helper() {}", "helper"),
"a helper is no test"
);
}