ref:main
//! Static guard against double-prefixed API paths.
//!
//! `Client::url()` prepends `/api/v1` to every path it is given, so callers must
//! pass API-relative paths (`"/registry/tokens"`). Passing `"/api/v1/registry/tokens"`
//! produced `…/api/v1/api/v1/registry/tokens` and a 404 that only showed up at
//! runtime — the whole `anvil registry token` surface shipped broken this way.
//!
//! `Client::url()` also carries a `debug_assert!`, but that only fires when the
//! offending call is actually executed. This test scans the source instead, so a
//! bad path is caught even if nothing covers that command.
use std::path::Path;
/// Every `Client` method that routes through `Client::url()`.
const URL_METHODS: &[&str] = &[
"get",
"get_with_query",
"post",
"put",
"patch",
"delete_empty",
"post_empty",
"get_raw",
"get_sse_stream",
"upload_file",
"download_file",
];
/// Strip *all* whitespace so a call site matches the same way whether it was
/// written on one line or wrapped across several by rustfmt.
fn flatten(src: &str) -> String {
src.chars().filter(|c| !c.is_whitespace()).collect()
}
fn rust_sources(dir: &Path, out: &mut Vec<std::path::PathBuf>) {
for entry in std::fs::read_dir(dir).expect("read_dir") {
let path = entry.expect("dir entry").path();
if path.is_dir() {
rust_sources(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
#[test]
fn client_call_sites_are_not_api_prefixed() {
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut files = Vec::new();
rust_sources(&src_dir, &mut files);
assert!(!files.is_empty(), "found no sources under {src_dir:?}");
let mut offenders = Vec::new();
for file in &files {
let flat = flatten(&std::fs::read_to_string(file).expect("read source"));
for method in URL_METHODS {
// Matches `.get("/api/v1…`, `.delete_empty(&format!("/api/v1…`,
// and the same with arbitrary whitespace between tokens.
// Only literal paths are checkable statically; paths built from
// variables are covered by the debug_assert! in Client::url().
for prefix in [
format!(".{method}(\"/api/v1"),
format!(".{method}(&format!(\"/api/v1"),
format!(".{method}(format!(\"/api/v1"),
] {
if flat.contains(&prefix) {
offenders.push(format!(
"{}: .{method}() called with an /api/v1-prefixed path",
file.strip_prefix(env!("CARGO_MANIFEST_DIR"))
.unwrap_or(file)
.display()
));
}
}
}
}
assert!(
offenders.is_empty(),
"Client::url() already prepends /api/v1 — these call sites double it:\n {}\n\n\
Pass an API-relative path instead, e.g. \"/registry/tokens\".",
offenders.join("\n ")
);
}