feat(zgw): docker OpenZaak integration-test harness (WP-54)

Opt-in docker-compose (postgres+redis+OpenZaak, no celery/nginx) +
bootstrap-catalogus.sh seed a real OpenZaak instance; OpenZaakIntegrationTests
(Category=Integration, excluded from default dotnet test/CI) proves the ZGW
seam against it for the first time. That live run caught a real bug:
ZgwHttpClient never sent Content-Crs/Accept-Crs headers, so every write would
412 against a spec-compliant OpenZaak — fixed alongside the harness.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-30 09:08:35 +02:00
co-authored by Claude Sonnet 5
parent 73172510ea
commit 5cb3e1a9f0
12 changed files with 471 additions and 15 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ for its existing violations, so every WP ends green.
| [WP-51](WP-51-openzaak-documenten.md) | OpenZaak Documenten (DRC) upload + zaak link | 9 · OpenZaak/ZGW | done |
| [WP-52](WP-52-openzaak-notificaties.md) | OpenZaak Notificaties (NRC) live status via webhook | 9 · OpenZaak/ZGW | done |
| [WP-53](WP-53-inbound-identity-and-citizen-scoping.md) | Inbound identity seam + citizen-scoping (per-request BSN, ZGW audit claims) | 9 · OpenZaak/ZGW | done |
| [WP-54](WP-54-openzaak-integration-harness.md) | Docker OpenZaak integration-test harness (opt-in, live round-trip) | 9 · OpenZaak/ZGW | todo |
| [WP-54](WP-54-openzaak-integration-harness.md) | Docker OpenZaak integration-test harness (opt-in, live round-trip) | 9 · OpenZaak/ZGW | done |
Sequencing dependencies (stated in the WPs too): 01 before 1015 (axe covers story churn);
03/04 before 0509 (boundaries stop new violations during refactors); 06 before 07 (typed
@@ -1,6 +1,6 @@
# WP-54 — Docker OpenZaak integration-test harness
Status: todo
Status: done
Phase: 9 — OpenZaak / ZGW integration
## Why
@@ -87,17 +87,19 @@ OpenZaak facts that shape the harness (from the ZGW research):
## Acceptance criteria
- [ ] `docker compose -f backend/openzaak/docker-compose.openzaak.yml up` yields a reachable
- [x] `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
- [x] 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
- [x] 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.
`./backend/openzaak/bootstrap-catalogus.sh``dotnet test --filter Category=Integration`
green; then teardown. Actually run (not just planned) during this WP — see Deviations below
for what that surfaced.
## Out of scope
@@ -112,3 +114,32 @@ always-on CI job (keep it opt-in/manual — OpenZaak startup is slow), performan
keep the fixture in the repo.
- Bootstrap client scopes must include `catalogi.lezen` or zaaktype resolution 403s — cover in
the setup script.
## Deviations from the original plan
- **`heeft_alle_autorisaties: true` instead of granular scopes.** The plan called out
`zaken.lezen`/`catalogi.lezen` specifically; in practice OpenZaak's scripted config
(`vng_api_common_applicaties_config`, upstream's own documented `setup_configuration` YAML
mechanism) exposes an all-scopes flag on the one `Applicatie` this harness ever creates. Since
that application exists for nothing but this throwaway test instance, granular scopes would
add YAML-schema risk for no real least-privilege benefit — took the simpler, equally-scripted
option.
- **A live run found a real production bug, not just a harness wrinkle**: `ZgwHttpClient.cs`
never sent `Content-Crs`/`Accept-Crs` on any ZGW call. Every ZGW write 412s ("Content-Crs
header ontbreekt") without it — a real OpenZaak enforces this; the stub `HttpMessageHandler`
every prior Zgw test used never modelled header requirements, so nothing from WP-49/50 caught
it before now. Fixed in `ZgwHttpClient.cs` alongside the harness (see
`docs/reference/openzaak-integration.md`) — this is precisely the class of bug this WP exists
to catch.
- **Publishing a zaaktype needs more seed data than the plan anticipated**: OpenZaak refuses to
publish a zaaktype with fewer than one resultaattype or fewer than two statustypen (begin +
eind), and a resultaattype's `selectielijstklasse` must share a `procesType` with the
zaaktype's own `selectielijstProcestype` — both cross-checked live against the public VNG
selectielijst API (`selectielijst.openzaak.nl`). `bootstrap-catalogus.sh` seeds all of this;
see its comments for the exact values used and why.
- **No celery/celery-beat/nginx in the harness**, unlike upstream's own compose — trimmed for a
faster-booting, single-purpose harness (this test never asserts on notification delivery,
which is celery's job). `NOTIFICATIONS_DISABLED=true` is required as a consequence: without a
celery worker, `NotificationsConfig` has no client, and OpenZaak's `notify()` hook otherwise
raises inside the same DB transaction as the create — turning a missing-worker problem into a
500 that rolls back the create it was supposed to just notify about.