xtask/tests/no_kit.rs
//! **gunnar compiles and starts as a server WITHOUT the kit** (the owner, 2026-10-11 05:10Z).
//!
//! The kit — `private-gunnar-ops` (its xtask, units, sitetest, cutovertest, idp-certs and its
//! `terraform/` declaration) and `private-monetize-ops` — drives gunnar from the outside; gunnar
//! never reaches back into it. A cheap gate, no `cargo` run: every manifest, every lockfile and
//! every Rust source of the tree (the detached workspaces included) is read as text, and any
//! dependency on the kit, any path into it, or any file compiled in from it fails here.
use std::path::{Path, PathBuf};
use xtask::one_clone::{lockfiles, manifests};
/// The kit's own checkouts: a path or a git source naming one is a dependency on the kit.
const KIT_CHECKOUTS: &[&str] = &["private-gunnar-ops", "private-monetize-ops"];
/// The kit's crates (`private-gunnar-ops`; its `xtask` shares the name of gunnar's own, so it is
/// caught by its path, not its name).
const KIT_CRATES: &[&str] = &[
"gunnar-units",
"gunnar-sitetest",
"cutovertest",
"idp-certs",
];
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("xtask has a parent")
.to_path_buf()
}
/// A text line that is code, not prose: `#` (TOML) and `//` (Rust) comments are not dependencies.
fn code_lines<'a>(text: &'a str, comment: &'a str) -> impl Iterator<Item = &'a str> + 'a {
text.lines()
.map(str::trim)
.filter(move |l| !l.starts_with(comment))
.map(move |l| l.split(&format!(" {comment}")).next().unwrap_or(l))
}
/// A path or a source into the kit: one of its checkouts, or its `terraform/` declaration.
fn into_kit(line: &str) -> bool {
KIT_CHECKOUTS.iter().any(|k| line.contains(k)) || line.contains("terraform/")
}
/// Pure: **what a manifest's text takes from the kit** — a kit crate as a `<name> =` key or a
/// `[dependencies.<name>]` table, or a `path`/`git` into the kit.
fn kit_in_manifest(text: &str) -> Vec<String> {
let mut out = Vec::new();
for line in code_lines(text, "#") {
let key = line
.split('=')
.next()
.unwrap_or("")
.trim()
.trim_matches('"');
let table = line.trim_start_matches('[').trim_end_matches(']');
for name in KIT_CRATES {
if key == *name || table.ends_with(&format!(".{name}")) {
out.push(format!("the kit's crate {name}"));
}
}
if (line.contains("path") || line.contains("git")) && into_kit(line) {
out.push(format!("a path into the kit: {line}"));
}
}
out
}
/// Pure: **the kit's packages a lockfile holds** — by name, or by a source into the kit.
fn kit_in_lock(text: &str) -> Vec<String> {
let mut out: Vec<String> = KIT_CRATES
.iter()
.filter(|n| text.contains(&format!("name = \"{n}\"")))
.map(|n| format!("the kit's crate {n}"))
.collect();
out.extend(
text.lines()
.map(str::trim)
.filter(|l| l.starts_with("source =") && into_kit(l))
.map(|l| format!("a source in the kit: {l}")),
);
out
}
/// Pure: **what a Rust source compiles in from the kit** — an `include_str!`/`include_bytes!`
/// or a `#[path]` reaching into it.
fn kit_in_source(text: &str) -> Vec<String> {
code_lines(text, "//")
.filter(|l| {
(l.contains("include_str!") || l.contains("include_bytes!") || l.contains("#[path"))
&& into_kit(l)
})
.map(|l| format!("compiled in from the kit: {l}"))
.collect()
}
fn rust_sources(root: &Path, dir: &Path, out: &mut Vec<String>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
let name = entry.file_name().to_string_lossy().into_owned();
if path.is_dir() {
if name == "target" || name == ".git" || name.starts_with(".iso-out") {
continue;
}
rust_sources(root, &path, out);
} else if name.ends_with(".rs") {
if let Ok(rel) = path.strip_prefix(root) {
out.push(rel.to_string_lossy().replace('\\', "/"));
}
}
}
}
#[test]
fn gunnar_takes_nothing_from_the_kit() {
let root = root();
let (ms, ls) = (manifests(&root), lockfiles(&root));
let mut rs = Vec::new();
rust_sources(&root, &root, &mut rs);
// This gate's own red half spells the shapes out.
rs.retain(|r| r != "xtask/tests/no_kit.rs");
assert!(
ms.len() > 20 && ms.iter().any(|m| m == "crates/gunnar-server/Cargo.toml"),
"the walk sees the tree: {} manifests",
ms.len()
);
assert!(
ls.iter().any(|l| l == "Cargo.lock") && rs.len() > 100,
"the walk sees the lock and the sources: {ls:?}, {} sources",
rs.len()
);
let read = |f: &String| std::fs::read_to_string(root.join(f)).unwrap_or_default();
let mut found = Vec::new();
found.extend(ms.iter().flat_map(|m| {
kit_in_manifest(&read(m))
.into_iter()
.map(move |p| format!("{m}: {p}"))
}));
found.extend(ls.iter().flat_map(|l| {
kit_in_lock(&read(l))
.into_iter()
.map(move |p| format!("{l}: {p}"))
}));
found.extend(rs.iter().flat_map(|r| {
kit_in_source(&read(r))
.into_iter()
.map(move |p| format!("{r}: {p}"))
}));
assert!(
found.is_empty(),
"gunnar takes from the kit — it must compile and start as a server without it:\n {}",
found.join("\n ")
);
}
/// The red half: each shape is SEEN, and prose about the kit is not mistaken for it.
#[test]
fn a_dependency_on_the_kit_is_reported_in_each_shape() {
assert_eq!(
kit_in_manifest("gunnar-units = { path = \"../../private-gunnar-ops/units\" }").len(),
2
);
assert_eq!(
kit_in_manifest("[dependencies.idp-certs]\nversion = \"0.1\""),
vec!["the kit's crate idp-certs".to_string()]
);
assert_eq!(
kit_in_manifest("xtask = { path = \"../private-gunnar-ops/xtask\" }").len(),
1,
"the kit's xtask, by its path"
);
assert_eq!(
kit_in_manifest("ops = { git = \"ssh://git@github.com/x/private-monetize-ops\" }").len(),
1
);
assert_eq!(
kit_in_manifest("tf = { path = \"../ops/terraform/gen\" }").len(),
1
);
assert!(
kit_in_manifest("# the kit (`private-gunnar-ops/terraform/`) declares the boxes")
.is_empty(),
"prose is not a dependency"
);
assert!(kit_in_manifest("gunnar-server = { path = \"crates/gunnar-server\" }").is_empty());
assert_eq!(
kit_in_lock("[[package]]\nname = \"gunnar-sitetest\"\n").len(),
1
);
assert_eq!(kit_in_lock("[[package]]\nname = \"ops\"\nsource = \"git+ssh://git@github.com/x/private-gunnar-ops#abc\"\n").len(), 1);
assert!(
kit_in_lock("[[package]]\nname = \"xtask\"\nversion = \"0.1.0\"\n").is_empty(),
"gunnar's own xtask"
);
assert_eq!(kit_in_source("const T: &str = include_str!(\"../../../private-gunnar-ops/terraform/estate.auto.tfvars.json\");").len(), 1);
assert!(
kit_in_source("// include_str!(\"../private-gunnar-ops/x\") would be wrong").is_empty()
);
}