Test coverage¶
Detailed rule cases live with the module that implements the rule. Callers retain tests of successful wiring, denied and failed operations, and each control they enforce independently. An HTTP refusal, a database constraint, an actual git transport and a browser form are separate contracts.
Named evidence inventory¶
docs/testing-inventory.json lists every tracked Go test file, its behavior
family, owning package and named tests and fuzz targets. The inventory
records ownership and names;
it does not claim that similar names or files prove equivalent assertions.
Dependency hints come from direct source references and need investigation
before changing a fixture or execution lane.
Regenerate the inventory after adding, moving or removing test evidence:
make check checks inventory drift. A new package needs an explicit behavior
owner in the generator. Test helpers without named tests remain recorded.
The catalogue also records acceptance/self-test scripts, their support files
and workflow job names. The Makefile, CI workflows and scripts below define
which entry points run; a listed support file is not another executed test.
| Evidence | Owner and purpose |
|---|---|
| Full Go suite, including corpus and fuzz seeds | make test; owning rule, store, API, pipeline and transport packages |
| Empty-filter and security-name gates | scripts/acceptance/lib-selftest.sh, invariant-test-names.sh, invariant-tests.txt |
| Compiled process and generated client | scripts/acceptance/smoke.sh; TestSmoke also runs in the Go suite |
| Actual headless UI flows | scripts/acceptance/ui.sh; page rendering is tested independently |
| Published envelopes verified by independent code | scripts/dev/attest-interop.sh |
| Edge request budgets | scripts/acceptance/edge.sh against the real deploy/compose/edge/nginx.conf |
| End-to-end acceptance | scripts/acceptance/auth.sh, git.sh, publish-install.sh, cli.sh, gate.sh, enterprise-sso.sh; filtered Go evidence must name actual passing tests through gotest |
| Helm and deployment configuration | scripts/acceptance/helm.sh and CI's render/config checks |
| Prose and generated/spec/rules drift | scripts/prose/check.sh --self-test and Makefile/CI drift gates |
Postgres fixtures¶
Set AGENTRY_TEST_DATABASE_URL to a dedicated PostgreSQL 16 test server.
Its account needs permission to create/drop databases. API and scan binaries
call testdb.Main from TestMain: each process creates a random sibling
database, preserves connection query options, redirects its test DSN to it,
and drops it when the tests finish. Whole-database scan sweeps cannot see API
fixtures or state from a previous process. Scan migrates once per process;
each pipeline fixture still gets its own server dependencies, pool and objects.
Tests in one binary still share that binary's database. Use unique owned
rows and explicit cleanup for caches/queues; an isolated binary is not
permission to delete another test's fixtures or use process-global state in
parallel. Repeating a test with -count shares a process; running a new
go test command creates a new database. A killed process may not run its
cleanup, so disposable server/container ownership remains necessary.
Without the DSN, database-dependent tests explicitly skip. A local skip is not database verification, and a required lane must provision its dependencies. The store's fresh/concurrent migration tests keep their actual migration paths.
Ordinary callers and real account lifecycles¶
seedCaller builds a valid active, verified account and a hashed session
credential through the store's generated queries. register (and
registerUnverified, for an unconfirmed address) and login do the same in
two steps, for a test that needs the account before its session: the server
keeps no password, so an account and a session are what a sign-in through the
OpenID provider leaves behind. They are for tests whose subject is
authorization or resource behavior. Requests still pass through the real HTTP
authentication, CSRF and authorization middleware.
When the sign-in itself is the subject (account creation, linking, the
provider's claims, staff syncing, second-factor evidence, remembered
browsers), sign in through the stand-in OpenID provider (internal/testidp,
with newOIDCTestEnv, signInOIDC and mintAccount). Use joinOrg and the
real endpoints for invitation acceptance, session/PAT issuance and account
erasure. Seeding is not evidence those lifecycle endpoints work.
Consolidating a behavior family¶
For each removed assertion, name the test and case that still covers the same behavior dimensions and enforcement boundary in the PR. Keep distinct concurrency, failure direction, current/revoked ownership, kernel/transport, malicious/benign corpus and cross-format compatibility cases. Do not compute expected behavior through the production function being tested.
Verify changed security evidence by breaking the specific control once and seeing the remaining assertion fail for that reason. Update architecture, acceptance filters and the named-evidence catalogue together. Report actual executed/skipped counts, setup/invocation counts and comparable timings; combining declarations into subtests alone does not reduce execution cost.
The authorization evidence map records the retained route, role, principal and signing boundaries and their caller fixtures.
Configuration guard ownership¶
TestValidateGuards holds sixteen distinct refusal cases and checks the
setting each refusal names. The production fixtures configure file signing
and a sealing key before changing the one setting under test. An accepted
production configuration checks that fixture first. This exercises Validate;
loading an actual signing key remains separate signing evidence.
| Case | Setting under test | Other guards configured |
|---|---|---|
prod devMode |
Dev mode is refused in production | File signing and a sealing key |
staging devMode |
Dev mode is refused in staging | File signing and a sealing key |
prod unsigned |
Unsigned production is refused | Dev mode off and a sealing key |
no sealing key outside dev mode |
A sealing key is required | Dev environment, dev mode off |
The environment, storage, judge, proxy, audit and staff-rule cases remain independent. The named inventory and acceptance filters name the same test. Configuration defaults, entitlement mapping and consumer outage enforcement also keep their separate boundaries.
TestValidateCapsCredentialTTLs holds fifteen named cases. PAT and SSO
at/over-cap inputs use literal contract values so a changed production ceiling
cannot move the tested boundary with it:
| Credential | Accepted boundary | Refused input |
|---|---|---|
| PAT | 365 days | 365 days plus one hour |
| Organization-bound PAT | 90 days | 90 days plus one hour |
| SSO freshness | 24 hours | 25 hours |
The session cases and remembered browser cases stay in the same table, with their
independent 24-hour and 30-day contract/default checks in
TestTrustedDeviceIsASecondClassNotALongerSession. Zero/negative lifetimes
and organization-bound narrowing have their own assertions.
Gate outcome ownership¶
TestGateCases owns the severity/outcome/queue matrix through the literal
cases in pkg/gatecases/cases.json. Its baseline, grid and branch guards
remain separate. Clients consume the same published inputs and expectations;
the table is not derived from Evaluate.
These TestGateCases subcases cover the outcome of one finding at each
severity, and of none:
| Input | Published case |
|---|---|
clean |
baseline: no findings publish |
info |
baseline: a info finding publishes |
low auto |
baseline: a low finding publishes |
medium auto |
baseline: a medium finding publishes |
high review |
baseline: a high finding is held for review |
critical reject |
baseline: a critical finding is rejected |
low-confidence high does not gate alone |
a low-confidence high finding does not gate alone |
Each uses the same baseline, one finding of the given severity and confidence
(or no finding), no flags and no declared capabilities. The scanner ids in
the cases are ordinary ids, outside the unread-content set.
TestGateCasesBaselineIsTheRegistrysBaseline pins the table's policy to the
compiled baseline. The cases check outcome, queue and deciding branch.
Policy parsing/merge and tighten-only rules, empty-policy fallback, conditional review, judge flags, public queue routing, unread content and verdict ratings have their own tests. Corpus, pipeline signing/failure and registry state-machine tests keep their own boundaries.