#[test]
fn test_user_manual_does_not_claim_overlay_is_repeatable() {
let manual = include_str!("../docs/user-manual.md");
let repeatable_claims: Vec<_> = manual
.lines()
.filter(|line| line.contains("--overlay") && line.contains("repeatable"))
.collect();
assert!(
repeatable_claims.is_empty(),
"`--overlay` accepts one explicit override, but the manual says it is repeatable: \
{repeatable_claims:?}"
);
}
#[test]
fn test_feature_manifest_lists_every_builtin_parser() {
let parser_count = apexe::scanner::ParserPipeline::new(None).parser_count();
let manifest = include_str!("../docs/FEATURE_MANIFEST.md");
let expected_inventory =
format!("({parser_count} built-in parsers: Man, BSD Usage, GNU, Click, Cobra, Clap)");
assert!(
manifest.contains(&expected_inventory),
"feature manifest must match the runtime parser inventory: {expected_inventory}"
);
assert!(
manifest.contains("Man, BSD Usage, GNU, Click, Cobra, Clap format parsers"),
"feature manifest module map must include every built-in parser"
);
}
#[test]
fn test_every_document_states_the_same_parser_count() {
let parser_count = apexe::scanner::ParserPipeline::new(None).parser_count();
let spelled = match parser_count {
5 => "Five",
6 => "Six",
7 => "Seven",
other => panic!("add the word for {other} parsers to this test"),
};
let manual = include_str!("../docs/user-manual.md");
assert!(
manual.contains(&format!("{spelled} built-in parsers")),
"docs/user-manual.md must say `{spelled} built-in parsers` to match the runtime"
);
let readme = include_str!("../README.md");
assert!(
readme.contains(&format!("{parser_count} built-in parsers")),
"README.md must say `{parser_count} built-in parsers` to match the runtime"
);
let crate_doc = include_str!("../src/scanner/mod.rs");
for parser in ["Man", "BSD Usage", "GNU", "Click", "Cobra", "Clap"] {
assert!(
manual.contains(&format!("| **{parser}** |")),
"docs/user-manual.md's Tier 1 table omits the {parser} parser row"
);
assert!(
crate_doc.contains(parser),
"src/scanner/mod.rs Tier 1 list omits the {parser} parser"
);
}
}
#[test]
fn test_user_manual_names_the_wired_circuit_breaker() {
use apexe::module::{build_executor, ExecutorOptions};
let installed = |enabled: bool| -> Vec<String> {
let opts = ExecutorOptions {
modules_dir: None,
timeout_ms: 30_000,
acl_path: None,
filter: apexe::module::ModuleFilter::default(),
audit_path: None,
enable_logging: false,
log_arguments: false,
enable_approval: false,
enable_circuit_breaker: enabled,
enable_retry: false,
approval_store: None,
};
build_executor(&opts)
.expect("an executor with no modules still builds")
.middlewares()
};
assert!(
installed(true).iter().any(|name| name == "circuit_breaker"),
"build_executor must wire the circuit breaker on by default, as §9.5 says"
);
assert!(
!installed(false)
.iter()
.any(|name| name == "circuit_breaker"),
"--no-circuit-breaker must actually remove it, or the flag is decorative"
);
let wired = std::any::type_name::<apexe::module::HealthOnlyCircuitBreaker>()
.rsplit("::")
.next()
.expect("a type name always has a last segment");
let manual = include_str!("../docs/user-manual.md");
assert!(
manual.contains(wired),
"docs/user-manual.md §9.5 must name the middleware that is actually wired ({wired})"
);
assert!(
!manual.contains("**`CircuitBreakerMiddleware`**"),
"docs/user-manual.md still documents the replaced middleware as the wired one"
);
}
#[tokio::test]
async fn test_user_manual_describes_what_the_approval_gate_actually_does() {
use apcore::approval::{ApprovalHandler, ApprovalRequest};
let gate = apexe::module::ApprovalGate::new();
let mut request = ApprovalRequest::default();
request.module_id = "cli.rm".to_string();
let outcome = gate
.request_approval(&request)
.await
.expect("the gate answers rather than erroring");
assert_eq!(
outcome.status, "rejected",
"with no prompt deliverable the gate must fail closed"
);
let reason = outcome.reason.unwrap_or_default();
assert!(
reason.contains("no MCP elicitation support"),
"the refusal must say the prompt could not be delivered, not just that it was \
refused: {reason}"
);
assert!(
reason.contains("--acl"),
"the refusal must name what to use instead: {reason}"
);
let manual = include_str!("../docs/user-manual.md");
assert!(
manual.contains("ApprovalGate"),
"docs/user-manual.md must name the approval handler that is actually wired"
);
assert!(
manual.contains("0.18 or later"),
"docs/user-manual.md must state the apcore-mcp version the prompt needs"
);
assert!(
!manual.contains("blocks until the connected MCP client's user responds"),
"docs/user-manual.md still describes the pre-0.18 promise"
);
for (number, line) in manual.lines().enumerate() {
let lowered = line.to_lowercase();
if !lowered.contains("enable-approval") {
continue;
}
assert!(
!(lowered.contains("deny") || lowered.contains("denies")),
"docs/user-manual.md:{} still describes --enable-approval as a deny gate: {line}",
number + 1
);
}
}
#[test]
fn test_every_document_marks_sse_as_deprecated() {
assert!(
apexe::mcp::McpServerBuilder::new()
.transport("sse")
.build()
.is_ok(),
"SSE must build without an acknowledgement flag"
);
for (name, text) in [
(
"docs/user-manual.md",
include_str!("../docs/user-manual.md"),
),
("docs/quickstart.md", include_str!("../docs/quickstart.md")),
("README.md", include_str!("../README.md")),
] {
let lines: Vec<&str> = text.lines().collect();
let mut mentions = 0usize;
for (index, line) in lines.iter().enumerate() {
if !line.contains("--transport sse") {
continue;
}
mentions += 1;
let window = match index {
0 => line.to_lowercase(),
_ => format!("{}\n{}", lines[index - 1], line).to_lowercase(),
};
assert!(
window.contains("deprecated"),
"{name} presents `--transport sse` without its caveat: {line}"
);
}
assert!(
mentions > 0,
"{name} no longer mentions `--transport sse` at all — this guard has \
stopped guarding anything; re-anchor it on whatever spelling replaced it"
);
}
}
#[tokio::test]
async fn test_user_manual_states_that_filters_gate_execution() {
use apcore::ErrorCode;
use apexe::module::{build_executor, ExecutorOptions, ModuleFilter};
let dir = tempfile::TempDir::new().unwrap();
let modules = vec![apcore_toolkit::ScannedModule::new(
"cli.cp".to_string(),
"Copy".to_string(),
serde_json::json!({"type": "object"}),
serde_json::json!({"type": "object"}),
vec!["cli".to_string()],
"exec:///bin/cp".to_string(),
)];
apexe::output::YamlOutput::without_verification()
.write(&modules, dir.path(), false)
.unwrap();
let executor = build_executor(&ExecutorOptions {
modules_dir: Some(dir.path()),
timeout_ms: 1_000,
acl_path: None,
filter: ModuleFilter {
prefix: Some("cli.git".to_string()),
tags: None,
},
audit_path: None,
enable_logging: false,
log_arguments: false,
enable_approval: false,
enable_circuit_breaker: false,
enable_retry: false,
approval_store: None,
})
.unwrap();
let err = executor
.call("cli.cp", serde_json::json!({}), None, None)
.await
.expect_err("a filtered-out module must not be callable");
assert_eq!(err.code, ErrorCode::ModuleNotFound);
let manual = include_str!("../docs/user-manual.md");
assert!(
manual.contains("applied at\n**registration** time")
|| manual.contains("applied at **registration** time"),
"docs/user-manual.md must say the tool filter is applied at registration time"
);
assert!(
manual.contains(&format!("{:?}", err.code)),
"docs/user-manual.md must name the error a filtered-out module returns ({:?})",
err.code
);
}
#[test]
fn test_user_manual_documents_transport_authentication() {
use apexe::auth::{resolve_auth, AuthMode, AuthOptions, ResolvedAuth};
let manual = include_str!("../docs/user-manual.md");
let loopback = resolve_auth("http", "127.0.0.1", &AuthOptions::default()).unwrap();
assert!(
loopback.require_auth(),
"HTTP on loopback must require a credential by default"
);
assert!(
matches!(
loopback,
ResolvedAuth::Token {
generated: true,
..
}
),
"the loopback default must be a generated token"
);
assert!(
manual.contains("Authorization: Bearer"),
"docs/user-manual.md must state the header the generated token goes in"
);
assert!(
manual.contains("--auth-token") && manual.contains("APEXE_AUTH_TOKEN"),
"docs/user-manual.md must document both ways to pin the token"
);
let stdio = resolve_auth("stdio", "127.0.0.1", &AuthOptions::default()).unwrap();
assert!(!stdio.require_auth(), "stdio must not require a credential");
let refused = resolve_auth(
"http",
"0.0.0.0",
&AuthOptions {
mode: Some(AuthMode::None),
..AuthOptions::default()
},
)
.expect_err("--auth none on a public bind must refuse to start");
let flag = "--allow-unauthenticated-bind";
assert!(refused.message.contains(flag), "{}", refused.message);
assert!(
manual.contains(flag),
"docs/user-manual.md must document `{flag}`, which the refusal points operators at"
);
}
#[tokio::test]
async fn test_every_document_discloses_that_a2a_has_no_authentication() {
let err = apexe::a2a::A2aServerBuilder::new()
.url("http://0.0.0.0:8000")
.agent_card()
.await
.expect_err("a non-loopback A2A bind must refuse without the acknowledgement");
assert!(
err.message.contains("no transport authentication"),
"the refusal must say why it refuses: {}",
err.message
);
assert!(
err.message.contains("--allow-unauthenticated-bind"),
"the refusal must name the acknowledgement flag: {}",
err.message
);
for (name, text) in [
(
"docs/user-manual.md",
include_str!("../docs/user-manual.md"),
),
("README.md", include_str!("../README.md")),
] {
assert!(
text.contains("no transport authentication"),
"{name} introduces `apexe a2a` without disclosing that it has none"
);
assert!(
text.contains("--allow-unauthenticated-bind"),
"{name} must name the flag a non-loopback A2A bind demands"
);
}
}
#[test]
fn test_every_dependency_table_states_the_required_version() {
let manifest = include_str!("../Cargo.toml");
let readme = include_str!("../README.md");
let features = include_str!("../docs/FEATURE_MANIFEST.md");
let required = |crate_name: &str| -> String {
let line = manifest
.lines()
.map(str::trim)
.find(|line| {
line.strip_prefix(crate_name)
.is_some_and(|rest| rest.trim_start().starts_with('='))
})
.unwrap_or_else(|| panic!("Cargo.toml has no dependency line for {crate_name}"));
let (_, after) = line.split_once('"').expect("a quoted version");
let (version, _) = after.split_once('"').expect("a closed quote");
version.to_string()
};
for crate_name in [
"apcore",
"apcore-a2a",
"apcore-cli",
"apcore-mcp",
"apcore-toolkit",
] {
let version = required(crate_name);
let readme_row = format!("-rust) {version} |");
let readme_mentions = readme
.lines()
.filter(|line| line.contains(crate_name) && line.starts_with('|'))
.count();
assert!(
readme_mentions > 0,
"README.md dependency table lost its {crate_name} row"
);
assert!(
readme
.lines()
.any(|line| line.contains(crate_name) && line.contains(&readme_row)),
"README.md must state {crate_name} {version} to match Cargo.toml"
);
let manifest_row = format!("| `{crate_name}` | {version} |");
assert!(
features.contains(&manifest_row),
"docs/FEATURE_MANIFEST.md must state `{manifest_row}` to match Cargo.toml"
);
}
}
#[test]
fn test_user_manual_matches_the_a2a_denial_apcore_actually_maps() {
let denial = apcore::ModuleError::new(
apcore::ErrorCode::ACLDenied,
"Access denied: caller 'None' cannot access module 'cli.cp'".to_string(),
);
let mapped = apcore_a2a::ErrorMapper::to_jsonrpc_error(&denial);
let manual = include_str!("../docs/user-manual.md");
assert!(
manual.contains(&format!("`-{}`", -mapped.code)),
"docs/user-manual.md must state the JSON-RPC code {} that apcore-a2a maps an ACL \
denial to",
mapped.code
);
assert!(
manual.contains(&format!("`{}`", mapped.message)),
"docs/user-manual.md must quote the message apcore-a2a returns: {:?}",
mapped.message
);
assert!(
!mapped.message.contains("cli.cp"),
"the caveat exists because the reason is withheld; upstream now leaks the module \
name, so §9.1 needs rewriting: {:?}",
mapped.message
);
}