use std::fs;
use std::path::PathBuf;
fn book_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("book")
}
#[test]
fn book_toml_exists_and_declares_title() {
let body = fs::read_to_string(book_dir().join("book.toml")).expect("book.toml exists");
assert!(body.contains("title = \"captchaforge\""));
assert!(body.contains("[output.html]"));
assert!(body.contains("[output.html.search]"));
}
#[test]
fn summary_md_has_required_sections() {
let body = fs::read_to_string(book_dir().join("src/SUMMARY.md")).unwrap();
for required in [
"# Getting Started",
"# Architecture",
"# Per-Vendor Cookbook",
"# Integration",
"# Deployment",
"# Operations",
"# Reference",
"# Contributing",
"# Project",
] {
assert!(body.contains(required), "SUMMARY.md missing section {required:?}");
}
}
#[test]
fn every_summary_link_resolves_to_a_file() {
let summary = fs::read_to_string(book_dir().join("src/SUMMARY.md")).unwrap();
let mut missing: Vec<String> = Vec::new();
for line in summary.lines() {
let Some(bracket_close) = line.find("](") else { continue };
let url_start = bracket_close + 2; let Some(rel_close) = line[url_start..].find(')') else { continue };
let path = &line[url_start..url_start + rel_close];
if path.is_empty() || path.starts_with("http") {
continue;
}
let p = book_dir().join("src").join(path);
if !p.is_file() {
missing.push(path.to_string());
}
}
assert!(
missing.is_empty(),
"SUMMARY.md references missing files:\n {}",
missing.join("\n ")
);
}
#[test]
fn book_has_404_page() {
assert!(book_dir().join("src/404.md").is_file());
}