Testing Policy#

Policy is code, and it deserves tests. The behaviors worth covering are the ones the scenario demonstrates: a route allows the right callers, denies the wrong ones, and redacts the right fields. Because APL is declarative and evaluated by apl-core, you can test a route by evaluating it against fixture identities and asserting the outcome, without standing up a live backend.

What to test#

For each route, cover the outcomes its policy produces:

  • Allow: a caller with the required attributes passes and the operation forwards.
  • Deny: a caller missing a required attribute is rejected, with the expected reason code.
  • Redaction: a field is present for an entitled caller and redacted for an unentitled one (the “same request, different data” outcomes).
  • Information flow: a session that acquired a taint label is blocked on a later operation that gates on it.
  • Delegation: a passing caller mints a token with the requested scope, and a post-check denies when the granted scope is short.

Evaluating a route in a test#

Compile the config and evaluate a route against an attribute bag standing in for a caller. Assert the decision and the transformed payload. The apl-core and apl-cpex crates expose the evaluator used by the runtime; their test suites (for example crates/apl-core/tests) are the working reference for the exact entry points and fixtures.

A redaction test, in shape:

  • build a bag for an HR caller with perm.view_ssn, evaluate get_employee, assert ssn is present;
  • build a bag for an HR caller without perm.view_ssn, evaluate the same route, assert ssn is redacted;
  • build a bag for a non-HR caller, evaluate, assert deny at require(role.hr).

Integration coverage#

Unit-evaluating a route proves the policy logic. It does not prove the plugins it dispatches behave correctly end to end. For effects that call out (a PDP resolver, a delegator, a PII scanner), add an integration test that exercises the real plugin through the manager, so the interaction is covered and not just the policy’s intent. Test the failure paths too: a PDP that denies, a token exchange that returns a short scope, a scanner that flags content. Those are the branches policy exists to handle.

Running#

cargo test --workspace

See crates/cpex-core/examples for runnable programs that load a config and invoke routes, which double as a starting point for integration tests against your own policy.