The two highest-value OpenZaak roadmap gaps, each written self-contained (a "current state" handoff section) so a fresh session can execute from the file + repo alone: - WP-53: replace the stubbed owner/BSN with a real per-request CallerIdentity (pluggable stub, not DigiD), threading it into Authz, the ZGW JWT user claims, and a citizen-scoped read (rol__…__inpBsn). Production-blocking for a real deployment. - WP-54: a separate docker-compose OpenZaak + scripted bootstrap + opt-in Category=Integration test — makes 50/51/52 developable against a live instance instead of only fixtures; kept out of the default gate. Indexed in the backlog README (rows + phase-9 ordering note) and cited from openzaak-integration.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.7 KiB
WP-54 — Docker OpenZaak integration-test harness
Status: todo Phase: 9 — OpenZaak / ZGW integration
Why
Everything ZGW so far is verified against fixtures + a stub HttpMessageHandler — nothing
exercises a real OpenZaak. That is fine for unit-testing the mapper/JWT/pagination, but it does
not prove the BFF actually talks to OpenZaak (auth accepted, real response shapes, real
pagination, zaaktype resolution). This WP stands up a local OpenZaak via docker, seeds a
minimal catalog + a client credential matching ZgwOptions, and adds an opt-in integration
test that points the BFF at it with Zgw:Enabled=true. It closes the "no live instance" gap
called out in the roadmap and makes WP-50/51/52 developable against something real.
Context — current state (read before designing; self-contained handoff)
- What exists to test:
backend/src/BigRegister.Api/Zgw/OpenZaakZaakSource.cs(lists zaken, follows{count,next,previous,results}, resolves zaaktype labels from Catalogi, Bearer auth viaZgwTokenProvider), selected whenZgw:Enabled=true. Config is theZgwsection ofappsettings.json(ZrcBaseUrl,ZtcBaseUrl,ClientId,Secret,UserId,UserRepresentation) →Zgw/ZgwOptions.cs. - Current tests (the pattern to extend, not replace):
ZgwZaakMapperTests(inline JSON),OpenZaakZaakSourceTests(stub handler). These stay as fast unit tests; the new integration test is a separate, opt-in category so the defaultdotnet testand CI stay fast/offline. - Existing compose: repo root
docker-compose.ymlruns FE + BFF only (Swagger at :5000). Do not bolt OpenZaak onto it — OpenZaak is heavy (postgres + redis + celery). Use a separate compose file sodocker compose upstays light for everyone else. - Test project:
backend/tests/BigRegister.Tests/(xunit). It usesMicrosoft.AspNetCore.Mvc.Testing(TestWebApplicationFactory.cs) — the factory can be configured to override theZgwconfig section to point at the compose instance. - CLAUDE.md / backlog GREEN: the default local gate and CI must remain runnable without
docker/OpenZaak. Anything requiring the harness is explicitly separate (like e2e is today —
a distinct job, not chained into
npm run ci).
OpenZaak facts that shape the harness (from the ZGW research):
- OpenZaak is the reference impl of the 5 ZGW APIs; the published
docker-compose.yml(github.com/open-zaak/open-zaak) brings up openzaak (web + celery), postgres, redis and (optionally) Open Notificaties. A one-time bootstrap is required: create a superuser, register an Application in the Autorisaties API with a Client ID/Secret + scopes (zaken.lezen,catalogi.lezen, …), and import/create a Catalogus with a zaaktype (published) so there is something to read. - Auth = the HS256 JWT the BFF already mints; the harness's client credentials must match
ZgwOptions.ClientId/Secret. - Reading a zaak needs read scope on both Zaken and Catalogi (zaaktype resolution).
Read first
- openzaak-integration.md (the seam + config keys).
- ADR-0005.
- WP-19 (
WP-19-e2e-smoke.md) — the precedent for a heavy, separate, opt-in test job (mirror its "not chained into the default gate" structure). - Upstream: OpenZaak docker docs (https://open-zaak.readthedocs.io/) + the repo's
docker-compose.ymland itsimportfixtures for a demo catalogus.
Decisions (pre-made, don't relitigate)
- Separate compose file (e.g.
backend/openzaak/docker-compose.openzaak.yml), never merged into the root compose.docker compose upstays FE+BFF only. - Opt-in test category. Tag the integration test
[Trait("Category", "Integration")]; exclude it from the default run (dotnet test --filter Category!=Integration) and from the standard CI jobs. Provide a documented command / optional manual CI job to run it. - Bootstrap is scripted, not manual. A checked-in setup (compose
importfixture or a small bootstrap script) creates the client credentials (matchingZgwOptions), a catalogus, and one published zaaktype + one zaak — so the test is deterministic and repeatable. - Provider stays stubbed (WP-53) — this WP is about the ZGW round-trip, not real user auth.
Files
- New:
backend/openzaak/docker-compose.openzaak.yml+ bootstrap fixture/script + a shortbackend/openzaak/README.md(how to bring it up, credentials, teardown). - New:
backend/tests/BigRegister.Tests/OpenZaakIntegrationTests.cs(Category=Integration): configure the BFF (WebApplicationFactory) withZgw:Enabled=true+ the compose URLs/creds, hit/admin/cases, assert the seeded zaak comes back mapped. - Edit:
docs/reference/openzaak-integration.md(add a "Run against real OpenZaak" section); optionally a manual/gated CI job in.github/workflows/ci.ymlmirroring the e2e job's shape.
Steps
- Add the separate compose file bringing up OpenZaak + postgres + redis; pin image versions.
- Script the bootstrap: superuser, Autorisaties Application (Client ID/Secret =
ZgwOptions), a catalogus + one published zaaktype + one zaak with a natuurlijk-persoon rol (the seeded BSN). - Write the opt-in integration test: point the BFF at the compose instance, assert the mapped
zaak (id = uuid, type = zaaktype label, status) via
/admin/cases. - Document
docker compose -f … up+ the run command; optionally add a manual CI job.
Acceptance criteria
docker compose -f backend/openzaak/docker-compose.openzaak.yml upyields a reachable OpenZaak with the seeded catalogus + zaak, and credentials matchingZgwOptions.- The Category=Integration test passes against it; the BFF returns the seeded zaak mapped to
ApplicationSummaryDtothrough the real HTTP + JWT path. - Default
dotnet testandnpm run ciare unaffected (integration test excluded, no docker needed);docker compose up(root) is unchanged.
Verification
docker compose -f backend/openzaak/docker-compose.openzaak.yml up -d →
dotnet test --filter Category=Integration → green; then teardown.
Out of scope
Documenten/DRC + Notificaties services in the harness (add when WP-51/52 land), a permanent always-on CI job (keep it opt-in/manual — OpenZaak startup is slow), performance testing.
Risks
- OpenZaak startup is slow + resource-heavy → keep it opt-in; a permanent CI job would blow the same runner budget WP's storybook cap just fixed.
- ZGW API/version drift vs the pinned image can change response shapes → pin image versions and keep the fixture in the repo.
- Bootstrap client scopes must include
catalogi.lezenor zaaktype resolution 403s — cover in the setup script.