fn api_routes() -> Vec<(String, String)> {
let run_rs_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("objectiveai-api")
.join("src")
.join("run.rs");
let src = std::fs::read_to_string(&run_rs_path)
.unwrap_or_else(|e| panic!("read {}: {e}", run_rs_path.display()));
let re = regex::Regex::new(
r#"\.route\(\s*"([^"]+)"\s*,\s*(?:axum::routing::)?(get|post|delete|put)\("#,
)
.expect("regex compiles");
re.captures_iter(&src)
.map(|cap| (cap.get(1).unwrap().as_str().to_string(), cap.get(2).unwrap().as_str().to_string()))
.collect()
}
fn leaf_rel_path(path: &str, method: &str) -> std::path::PathBuf {
let segments: Vec<&str> = path
.trim_start_matches('/')
.split('/')
.filter(|s| !s.is_empty())
.collect();
let mut rel = std::path::PathBuf::from("api");
for seg in &segments {
rel.push(seg);
}
rel.push(format!("{method}.rs"));
rel
}
#[test]
fn every_api_route_has_a_cli_leaf() {
let cli_src_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut missing: Vec<String> = Vec::new();
for (path, method) in api_routes() {
let rel = leaf_rel_path(&path, &method);
let abs = cli_src_dir.join(&rel);
if !abs.exists() {
missing.push(format!("{} {} -> src/{}", method.to_uppercase(), path, rel.display()));
}
}
assert!(
missing.is_empty(),
"uncovered API endpoints (each route in objectiveai-api/src/run.rs needs a matching CLI leaf):\n {}",
missing.join("\n "),
);
}
#[test]
fn every_api_leaf_flattens_agent_id_arg() {
let cli_src_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
const MARKER: &str = "pub agent_id: crate::api::agent_id_arg::AgentIdArg";
let mut missing: Vec<String> = Vec::new();
for (path, method) in api_routes() {
let rel = leaf_rel_path(&path, &method);
let abs = cli_src_dir.join(&rel);
let Ok(src) = std::fs::read_to_string(&abs) else {
continue;
};
if !src.contains(MARKER) {
missing.push(format!("{} {} -> src/{}", method.to_uppercase(), path, rel.display()));
}
}
assert!(
missing.is_empty(),
"API leaves missing `{}` flatten (every endpoint must accept `--agent-id`):\n {}",
MARKER,
missing.join("\n "),
);
}