- Created docs/operations/manual-platform-smoke.md with comprehensive smoke test runbook for KU-12 quarterly manual platform testing - Added troubleshooting table covering all 14 doctor checks - Cross-referenced runbook from installation.md and quickstart.md - Added CI gate test (doctor_runbook_coverage.rs) to verify troubleshooting table completeness Acceptance criteria: ✓ Step 1: pdftract doctor as first section in runbook ✓ Troubleshooting table covers all FAIL-capable checks ✓ installation.md mentions pdftract doctor with runbook link ✓ quickstart.md uses pdftract doctor as first example command ✓ CI gate parses runbook and asserts all checks are present ✓ mdBook build succeeds ✓ No broken internal links Closes: pdftract-653ah
85 lines
3 KiB
Rust
85 lines
3 KiB
Rust
//! CI gate: Verify runbook troubleshooting table covers all doctor checks
|
|
//!
|
|
//! This test ensures that every check in the doctor registry has a corresponding
|
|
//! row in the troubleshooting table in docs/operations/manual-platform-smoke.md.
|
|
//!
|
|
//! Bead: pdftract-653ah (runbook integration)
|
|
|
|
use std::collections::HashSet;
|
|
|
|
fn main() {
|
|
// Load the runbook
|
|
let runbook_path = std::path::Path::new("docs/operations/manual-platform-smoke.md");
|
|
let runbook_content = std::fs::read_to_string(runbook_path)
|
|
.expect("Runbook file not found. Has docs/operations/manual-platform-smoke.md been created?");
|
|
|
|
// Extract check names from the troubleshooting table
|
|
// The table has rows like: "| tesseract install (FAIL) | ... |"
|
|
let mut table_checks = HashSet::new();
|
|
for line in runbook_content.lines() {
|
|
if let Some(start) = line.find("| ") {
|
|
if let Some(end) = line.find(" (FAIL)") {
|
|
let check_name = line[start + 2..end].trim();
|
|
table_checks.insert(check_name.to_string());
|
|
}
|
|
if let Some(end) = line.find(" (WARN)") {
|
|
let check_name = line[start + 2..end].trim();
|
|
table_checks.insert(check_name.to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get all check names from the doctor registry
|
|
// We use the known check list instead of runtime registry
|
|
let expected_checks = vec![
|
|
"pdftract binary",
|
|
"tesseract install",
|
|
"tesseract languages",
|
|
"leptonica install",
|
|
"libtiff",
|
|
"libopenjp2",
|
|
"pdfium native lib",
|
|
"network reachability",
|
|
"cache directory",
|
|
"profile search path",
|
|
"ulimit -n",
|
|
"available RAM",
|
|
"system locale",
|
|
"temp dir writable",
|
|
];
|
|
|
|
// Verify each expected check is in the table
|
|
let mut missing_checks = Vec::new();
|
|
for check in &expected_checks {
|
|
if !table_checks.contains(*check) {
|
|
missing_checks.push(*check);
|
|
}
|
|
}
|
|
|
|
if !missing_checks.is_empty() {
|
|
eprintln!(
|
|
"ERROR: Runbook troubleshooting table is missing checks: {:?}",
|
|
missing_checks
|
|
);
|
|
eprintln!("Please add rows to docs/operations/manual-platform-smoke.md for each missing check.");
|
|
std::process::exit(1);
|
|
}
|
|
|
|
// Verify table doesn't have orphaned checks (checks in table but not in registry)
|
|
let expected_set: HashSet<String> = expected_checks.iter().map(|s| s.to_string()).collect();
|
|
let orphaned: Vec<_> = table_checks
|
|
.difference(&expected_set)
|
|
.collect();
|
|
|
|
if !orphaned.is_empty() {
|
|
eprintln!(
|
|
"ERROR: Runbook troubleshooting table has orphaned checks (in table but not in registry): {:?}",
|
|
orphaned
|
|
);
|
|
eprintln!("Please remove these rows or add the checks to the doctor registry.");
|
|
std::process::exit(1);
|
|
}
|
|
|
|
println!("✓ Runbook troubleshooting table covers all doctor checks");
|
|
println!("✓ No orphaned checks in table");
|
|
}
|