use std::path::Path;
fn read(rel: &str) -> String {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(rel);
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("cannot read {rel}: {e}"))
}
fn declared_actions(tool_src: &str) -> Vec<String> {
let start = tool_src
.find("\"enum\": [")
.expect("tool schema has an action enum");
let rest = &tool_src[start..];
let end = rest.find(']').expect("action enum is closed");
rest[..end]
.split('"')
.map(str::trim)
.filter(|t| !t.is_empty() && t.chars().all(|c| c.is_ascii_lowercase() || c == '_'))
.filter(|t| *t != "enum")
.map(str::to_string)
.collect()
}
fn claimed_actions(readme: &str, tool: &str) -> usize {
let row = readme
.lines()
.find(|l| l.contains(&format!("`{tool}`")) && l.contains(" actions"))
.unwrap_or_else(|| panic!("README has no action-count row for {tool}"));
let after = row.split_once(" actions").expect("row names a count").0;
after
.rsplit(|c: char| !c.is_ascii_digit())
.find(|t| !t.is_empty())
.expect("count is a number")
.parse()
.expect("count parses")
}
#[test]
fn readme_action_counts_match_the_tool_schemas() {
let readme = read("README.md");
for (tool, src) in [
("whatsapp_send", "src/brain/tools/whatsapp_send.rs"),
("telegram_send", "src/brain/tools/telegram_send.rs"),
] {
let actual = declared_actions(&read(src)).len();
let claimed = claimed_actions(&readme, tool);
assert_eq!(
claimed, actual,
"README says {tool} has {claimed} actions; {src} declares {actual}. \
Update the README row in the same commit as the action."
);
}
}
#[test]
fn every_whatsapp_action_is_reachable() {
let src = read("src/brain/tools/whatsapp_send.rs");
for action in declared_actions(&src) {
assert!(
src.contains(&format!("\"{action}\" =>")) || src.contains(&format!("\"{action}\" |")),
"action '{action}' is in the schema with no match arm"
);
}
}
#[test]
fn every_telegram_action_is_reachable() {
let src = read("src/brain/tools/telegram_send.rs");
for action in declared_actions(&src) {
assert!(
src.contains(&format!("\"{action}\" =>")) || src.contains(&format!("\"{action}\" |")),
"action '{action}' is in the schema with no match arm"
);
}
}
#[test]
fn every_whatsapp_config_key_is_documented() {
let types = read("src/config/types.rs");
let readme = read("README.md");
let decl = types
.find("pub struct WhatsAppConfig")
.expect("WhatsAppConfig exists");
let open = decl + types[decl..].find('{').expect("struct has a body") + 1;
let body = &types[open..open + types[open..].find("\n}").expect("struct closes")];
for line in body.lines() {
let Some(field) = line.trim().strip_prefix("pub ") else {
continue;
};
let Some(name) = field.split(':').next() else {
continue;
};
let name = name.trim();
if name == "session_idle_hours" {
continue;
}
assert!(
readme.contains(name),
"[channels.whatsapp] key '{name}' appears nowhere in README.md"
);
}
}