Files
atomic-design-poc/docs/project/backlog/WP-54-openzaak-integration-harness.md
T
ehoandClaude Opus 4.8 dfd64baba8
CI / frontend (push) Successful in 2m20s
CI / backend (push) Successful in 1m51s
CI / e2e (push) Successful in 3m20s
CI / storybook-a11y (push) Failing after 8m35s
CI / semgrep (push) Successful in 1m5s
CI / api-client-drift (push) Successful in 1m52s
docs(backlog): add WP-53 (identity seam + citizen-scoping) and WP-54 (OpenZaak harness)
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>
2026-07-27 09:05:58 +02:00

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 via ZgwTokenProvider), selected when Zgw:Enabled=true. Config is the Zgw section of appsettings.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 default dotnet test and CI stay fast/offline.
  • Existing compose: repo root docker-compose.yml runs FE + BFF only (Swagger at :5000). Do not bolt OpenZaak onto it — OpenZaak is heavy (postgres + redis + celery). Use a separate compose file so docker compose up stays light for everyone else.
  • Test project: backend/tests/BigRegister.Tests/ (xunit). It uses Microsoft.AspNetCore.Mvc.Testing (TestWebApplicationFactory.cs) — the factory can be configured to override the Zgw config 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.yml and its import fixtures 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 up stays 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 import fixture or a small bootstrap script) creates the client credentials (matching ZgwOptions), 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 short backend/openzaak/README.md (how to bring it up, credentials, teardown).
  • New: backend/tests/BigRegister.Tests/OpenZaakIntegrationTests.cs (Category=Integration): configure the BFF (WebApplicationFactory) with Zgw: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.yml mirroring the e2e job's shape.

Steps

  1. Add the separate compose file bringing up OpenZaak + postgres + redis; pin image versions.
  2. 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).
  3. 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.
  4. 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 up yields a reachable OpenZaak with the seeded catalogus + zaak, and credentials matching ZgwOptions.
  • The Category=Integration test passes against it; the BFF returns the seeded zaak mapped to ApplicationSummaryDto through the real HTTP + JWT path.
  • Default dotnet test and npm run ci are 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.lezen or zaaktype resolution 403s — cover in the setup script.