Module 10: Testing your policy#
You are in the CPEX tutorial. Runs without the IdP.
Goal: test policy the way you test code. Load a policy, drive routes with a fake backend, and assert the outcome, so a policy change that breaks a rule fails CI.
The problem#
Policy decides who sees what. A careless edit can silently open a route or over-redact a field. You want the allow and deny matrix pinned by tests that run on every change, without standing up an IdP or a real backend for the cases that do not need one.
Build it#
A test loads a policy into a manager and calls routes through mediate() with a fake backend, then asserts. Table-driven cases keep the matrix readable. From tests/policy_tests.rs:
async fn manager_with(policy: &str) -> Arc<PluginManager> {
let mgr = Arc::new(PluginManager::default());
cpex::install_builtins(&mgr);
mgr.load_config_yaml(policy).expect("policy should load");
mgr.initialize().await.expect("initialize");
mgr
}
#[tokio::test]
async fn module4_external_email_denied_with_custom_code() {
let mgr = manager_with(M04).await;
let outcome = mediate(&mgr, &Caller::anonymous(), "send_email",
json!({ "to": "x@evil.example", "external": true }),
|a| backends::dispatch("send_email", a)).await;
assert!(matches!(outcome, Outcome::Denied { code, .. } if code == "email.external_blocked"));
}Anonymous callers exercise structural rules (authentication gates, argument guards, result pipelines) with no Keycloak. For identity-dependent rules, mint tokens the way the module binaries do.
Run it#
cargo test -p cpex-tutorialrunning 2 tests
test module1_gates_by_authentication ... ok
test module4_external_email_denied_with_custom_code ... okThe example binary runs the same idea in the tutorial’s output format:
cargo run -p cpex-tutorial --example m10_testingTry it#
- Break a policy. Edit
examples/tutorial/policies/m04.yamlto drop the external-recipient guard (thedeny(...)line), then runcargo test -p cpex-tutorial. Expect:module4_external_email_denied_with_custom_codefails, catching the regression. - Add a case. In
examples/tutorial/tests/policy_tests.rs, add a row to thecasesarray inmodule1_gates_by_authentication, for example:RunCase { tool: "search_repos", args: json!({ "visibility": "internal" }), want_allowed: true, want_code: None },cargo test -p cpex-tutorial. Expect: it passes (search_reposis open inm01.yaml). - Wire it into CI.
make testruns the whole workspace test suite, including these.
Checkpoint#
Do these tests need Keycloak?
What does a test actually assert on?
Go deeper#
Next#
Capstone: the three-backend agent: assemble every control you have built into the full Overview scenario.