S-04a — the deferred S-04 acceptance criterion: an integration test that drives the real OpenZaakGateway against a real OpenZaak, exercising the live ZGW JWT auth and POST /zaken/api/v1/zaken contract (CRS headers, default-fill, the returned zaak URL) that the stubbed-HttpMessageHandler unit tests cannot.
Closes#46 Closes#53 (adr-proposal — recorded as ADR-0006) Closes#55 (CI execution — folded into this PR)
Approach (ADR-0006)
The test targets the compose stack, not a Testcontainers graph: Testcontainers .NET has no docker-compose support (it would mean hand-rolling the 5-service stack in C#), and the test can't be hermetic anyway — a published zaaktype pulls in the external selectielijst.openzaak.nl reference API. No new NuGet dependency is added.
Gated lane.Acl.IntegrationTests ([Trait("Category","Integration")]) is excluded from the fast lanes: make unit filters Category!=Integration and Stryker is pinned to Acl.Tests. make integration (→ infra/run-integration.sh) owns the stack lifecycle: up → seed → test → always tear down.
Publishing is opt-in in the seed.seed_catalogus.py gains an OZ_PUBLISH=1 path that adds the relations OpenZaak's publish requires (two statustypen, a roltype, and a resultaattype with its Selectielijst procestype matched onto the zaaktype). The default seed (ADR-0002) still leaves the zaaktype a concept.
It caught a real bug
Against real OpenZaak the gateway POST returned 400: JsonContent streams the body without a Content-Length, so .NET frames it Transfer-Encoding: chunked, which OpenZaak's uwsgi rejects. A stub handler accepts either framing, so only the integration test surfaced it. Fixed by buffering the body; guarded in the fast lane by a unit test asserting a Content-Length is sent (which also keeps the Stryker score above the ratchet).
Running it on CI
A process on the hosted runner can't reach the stack's published ports (sibling containers via the host daemon — gitea-actions-gotchas.md §5), so infra/run-integration.sh runs both the seed and the test as containers joined to the OpenZaak network, reaching it by container IP (a single-label host like openzaak isn't URL-valid for OpenZaak's own URLValidator; an IPv4 literal is). Code is delivered by image build (services/acl/Dockerfile.integration) and docker cp — never bind-mounts. dotnet runs inside the test image, so the integration CI job needs only Docker.
Definition of Done
Linked Gitea issue (above).
Failing test committed before the implementation (red test(acl) → green fix(acl)).
Implementation makes the test pass.
Conventional Commits referencing the issue (refs #46).
CI green — all six jobs (lint, build, unit, mutation, integration, compose-smoke).
docker compose up smoke unaffected (full stack untouched).
Docs updated — ADR-0006, CI runbook, gitea-actions-gotchas §5.
ADR added — ADR-0006.
[N/A] Demo note — not user-visible (test/infra slice).
Notes for reviewers
External dependency: the integration job needs egress to selectielijst.openzaak.nl (from the OpenZaak + seed containers) — the one network touchpoint, overridable via OZ_SELECTIELIJST. Documented in ADR-0006.
The chunked-body fix changes OpenZaakGateway production behaviour (buffers before send) — the actual fix the new test drove; worth a careful look.
infra/run-integration.sh uses only plain docker primitives (run/create/cp/build) so it works identically under docker compose (CI) and podman-compose (local).
## What & why
**S-04a** — the deferred S-04 acceptance criterion: an **integration test that drives the real `OpenZaakGateway` against a real OpenZaak**, exercising the live ZGW JWT auth and `POST /zaken/api/v1/zaken` contract (CRS headers, default-fill, the returned zaak URL) that the stubbed-`HttpMessageHandler` unit tests cannot.
Closes #46
Closes #53 (adr-proposal — recorded as ADR-0006)
Closes #55 (CI execution — folded into this PR)
## Approach (ADR-0006)
The test **targets the compose stack**, not a Testcontainers graph: Testcontainers .NET has no docker-compose support (it would mean hand-rolling the 5-service stack in C#), and the test can't be hermetic anyway — a *published* zaaktype pulls in the external `selectielijst.openzaak.nl` reference API. No new NuGet dependency is added.
- **Gated lane.** `Acl.IntegrationTests` (`[Trait("Category","Integration")]`) is excluded from the fast lanes: `make unit` filters `Category!=Integration` and Stryker is pinned to `Acl.Tests`. `make integration` (→ `infra/run-integration.sh`) owns the stack lifecycle: up → seed → test → always tear down.
- **Publishing is opt-in in the seed.** `seed_catalogus.py` gains an `OZ_PUBLISH=1` path that adds the relations OpenZaak's publish requires (two statustypen, a roltype, and a resultaattype with its Selectielijst procestype matched onto the zaaktype). The default seed (ADR-0002) still leaves the zaaktype a concept.
## It caught a real bug
Against real OpenZaak the gateway POST returned `400`: `JsonContent` streams the body without a `Content-Length`, so .NET frames it `Transfer-Encoding: chunked`, which OpenZaak's uwsgi rejects. A stub handler accepts either framing, so only the integration test surfaced it. Fixed by buffering the body; guarded in the fast lane by a unit test asserting a `Content-Length` is sent (which also keeps the Stryker score above the ratchet).
## Running it on CI
A process *on* the hosted runner can't reach the stack's published ports (sibling containers via the host daemon — gitea-actions-gotchas.md §5), so `infra/run-integration.sh` runs **both the seed and the test as containers joined to the OpenZaak network**, reaching it by **container IP** (a single-label host like `openzaak` isn't URL-valid for OpenZaak's own `URLValidator`; an IPv4 literal is). Code is delivered by image build (`services/acl/Dockerfile.integration`) and `docker cp` — never bind-mounts. dotnet runs inside the test image, so the `integration` CI job needs only Docker.
## Definition of Done
- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation (red `test(acl)` → green `fix(acl)`).
- [x] Implementation makes the test pass.
- [x] Conventional Commits referencing the issue (`refs #46`).
- [x] CI green — all six jobs (lint, build, unit, mutation, **integration**, compose-smoke).
- [x] `docker compose up` smoke unaffected (full stack untouched).
- [x] Docs updated — ADR-0006, CI runbook, gitea-actions-gotchas §5.
- [x] ADR added — ADR-0006.
- [N/A] Demo note — not user-visible (test/infra slice).
## Notes for reviewers
- **External dependency:** the `integration` job needs egress to `selectielijst.openzaak.nl` (from the OpenZaak + seed containers) — the one network touchpoint, overridable via `OZ_SELECTIELIJST`. Documented in ADR-0006.
- The chunked-body fix changes `OpenZaakGateway` production behaviour (buffers before send) — the actual fix the new test drove; worth a careful look.
- `infra/run-integration.sh` uses only plain docker primitives (run/create/cp/build) so it works identically under docker compose (CI) and podman-compose (local).
not
added this to the Iteration 1 — Walking Skeleton milestone 2026-06-29 09:44:22 +00:00
OpenZaak rejects a zaak against a concept zaaktype (not-published). Add an
opt-in OZ_PUBLISH path that creates the relations publish requires — two
statustypen, a roltype, and a resultaattype whose Selectielijst procestype is
matched onto the zaaktype — then publishes. Default stays concept (ADR-0002);
only the ACL integration test flips it on.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
S-04a: the deferred S-04 acceptance criterion. A gated Acl.IntegrationTests
project (Category=Integration) drives the real OpenZaakGateway against the
running compose stack — real ZGW JWT auth and the real POST /zaken contract a
stubbed HttpMessageHandler cannot exercise. The lane is kept out of the fast
checks: make unit filters Category!=Integration, Stryker is pinned to Acl.Tests,
and a new make integration target brings the stack up, seeds a published zaaktype
and tears down.
Red: against real OpenZaak the gateway POST fails 400 — JsonContent streams the
body chunked and OpenZaak's uwsgi rejects it. Fixed in the next commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
OpenZaak runs behind uwsgi, which rejects a chunked request body with 400.
JsonContent streams without a Content-Length (Transfer-Encoding: chunked), so
buffer it first. Only a real OpenZaak surfaces this — the integration test from
the previous commit now passes. A unit test asserts a Content-Length is sent
(captured before the stub reads/buffers the body), guarding the fix in the fast
lane and killing the Stryker mutant that would otherwise survive.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records why the integration test targets the running compose stack rather than a
Testcontainers graph (no .NET compose support; not hermetic anyway due to the
Selectielijst dependency), the opt-in publish seed, and the chunked-body bug the
test caught. Proposed in #53.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New integration job: setup-dotnet + make integration (stack up, OZ_PUBLISH=1 seed,
Integration-category tests, tear down), with on-failure log dump + teardown like
compose-smoke. Documents the job and the new make target in the CI runbook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hosted Gitea runner starts the OpenZaak stack as sibling containers via the
host daemon, so a process on the runner can't reach the published ports — the seed
and dotnet test get Connection refused on localhost:8000. Drop the (non-working)
integration CI job; make integration stays the local / host-runner gate. Document
the limitation in gitea-actions-gotchas.md §5 and the CI runbook, and track running
it inside the compose network in #55. ADR-0006 updated accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hosted runner can't reach the stack's published ports (sibling containers),
so run the seed and the test as containers joined to the OpenZaak network,
reaching it by container IP — a single-label host like 'openzaak' isn't URL-valid
for OpenZaak's own URLValidator, but an IPv4 literal is. Code is delivered via
image build / docker cp (bind mounts don't reach the daemon either).
- infra/run-integration.sh: up -> wait healthy (docker inspect) -> seed published
zaaktype (python container on the net) -> build + run the test image on the net
-> always tear down. Plain docker primitives only (portable docker/podman).
- services/acl/Dockerfile.integration: builds + runs Acl.IntegrationTests; dotnet
lives in the image, so the CI job needs only Docker (no setup-dotnet).
- make integration now delegates to the script; re-added the Gitea Actions job.
Supersedes the local-only gap documented earlier; #55 is no longer needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not
merged commit 86cc65f4d9 into main2026-06-29 10:48:01 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
S-04a — the deferred S-04 acceptance criterion: an integration test that drives the real
OpenZaakGatewayagainst a real OpenZaak, exercising the live ZGW JWT auth andPOST /zaken/api/v1/zakencontract (CRS headers, default-fill, the returned zaak URL) that the stubbed-HttpMessageHandlerunit tests cannot.Closes #46
Closes #53 (adr-proposal — recorded as ADR-0006)
Closes #55 (CI execution — folded into this PR)
Approach (ADR-0006)
The test targets the compose stack, not a Testcontainers graph: Testcontainers .NET has no docker-compose support (it would mean hand-rolling the 5-service stack in C#), and the test can't be hermetic anyway — a published zaaktype pulls in the external
selectielijst.openzaak.nlreference API. No new NuGet dependency is added.Acl.IntegrationTests([Trait("Category","Integration")]) is excluded from the fast lanes:make unitfiltersCategory!=Integrationand Stryker is pinned toAcl.Tests.make integration(→infra/run-integration.sh) owns the stack lifecycle: up → seed → test → always tear down.seed_catalogus.pygains anOZ_PUBLISH=1path that adds the relations OpenZaak's publish requires (two statustypen, a roltype, and a resultaattype with its Selectielijst procestype matched onto the zaaktype). The default seed (ADR-0002) still leaves the zaaktype a concept.It caught a real bug
Against real OpenZaak the gateway POST returned
400:JsonContentstreams the body without aContent-Length, so .NET frames itTransfer-Encoding: chunked, which OpenZaak's uwsgi rejects. A stub handler accepts either framing, so only the integration test surfaced it. Fixed by buffering the body; guarded in the fast lane by a unit test asserting aContent-Lengthis sent (which also keeps the Stryker score above the ratchet).Running it on CI
A process on the hosted runner can't reach the stack's published ports (sibling containers via the host daemon — gitea-actions-gotchas.md §5), so
infra/run-integration.shruns both the seed and the test as containers joined to the OpenZaak network, reaching it by container IP (a single-label host likeopenzaakisn't URL-valid for OpenZaak's ownURLValidator; an IPv4 literal is). Code is delivered by image build (services/acl/Dockerfile.integration) anddocker cp— never bind-mounts. dotnet runs inside the test image, so theintegrationCI job needs only Docker.Definition of Done
test(acl)→ greenfix(acl)).refs #46).docker compose upsmoke unaffected (full stack untouched).Notes for reviewers
integrationjob needs egress toselectielijst.openzaak.nl(from the OpenZaak + seed containers) — the one network touchpoint, overridable viaOZ_SELECTIELIJST. Documented in ADR-0006.OpenZaakGatewayproduction behaviour (buffers before send) — the actual fix the new test drove; worth a careful look.infra/run-integration.shuses only plain docker primitives (run/create/cp/build) so it works identically under docker compose (CI) and podman-compose (local).