All checks were successful
## What & why Before this change the self-service confirmation and the openbaar register showed **different** identifiers, so a citizen could not look their registration back up (#78). Now both surface the same **reference**: - **domain → ACL (write):** the domain `registrationId` is set as the zaak's `identificatie` on `POST /zaken`. - **event-subscriber → ACL (read):** the subscriber reads the zaak's `identificatie` back through the ACL (§8.1 — only the ACL talks to ZGW) via a new `POST /zaken/reference`, and stores it on the projection row **and** the `processed_notifications` replay log. - **BFF + openbaar:** the public view exposes `id/status/reference` (never bsn/naam) and searches by id or reference; the register's "Referentie" column shows the reference. Storing the reference in the replay log keeps ADR-0008's **rebuild-is-log-only** invariant intact — `/admin/rebuild` reproduces the reference without re-reading the ACL. Decision recorded in **ADR-0012**. ## Definition of Done - [x] Linked issue: #78 - [x] Tests written first; red → green per layer - [x] Unit + acceptance green (`make unit`): domain 49, acl 27, bff 20, event-subscriber 19, acceptance 7 - [x] Frontend lint + test green (`nx run-many -t lint test`) - [x] Mutation ≥ break(90): acl 100%, event-subscriber 100%, bff 100%, domain 98.41% (pre-existing FlowableWorkflowClient baseline, untouched) - [x] e2e extended: confirmation reference == register reference - [x] openapi.json + api-client regenerated (drift guard green) - [x] ADR-0012 added; demo-script note appended - [x] `Acl__BaseUrl` wired for the subscriber in compose closes #78 Reviewed-on: #79
68 lines
3.6 KiB
TypeScript
68 lines
3.6 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
// Walking-skeleton happy path (S-08d + S-09 + S-09b): a zorgprofessional logs in via mock DigiD and
|
|
// submits through the self-service portal → BFF → domain; the entry appears in the openbaar register
|
|
// as INGEDIEND; a behandelaar approves it via the temporary admin endpoint; the approval flows via the
|
|
// ACL → NRC → event-subscriber → projection, and the openbaar register then shows it as INGESCHREVEN.
|
|
test('DigiD login → submit → public INGEDIEND → approve → public INGESCHREVEN', async ({ page, request }) => {
|
|
// Visiting the guarded page redirects to the Keycloak (mock DigiD) login.
|
|
await page.goto('/');
|
|
|
|
// Keycloak's default login form (stable ids across themes).
|
|
await page.locator('#username').fill('jan-burger');
|
|
await page.locator('#password').fill('test123');
|
|
await page.locator('#kc-login').click();
|
|
|
|
// Back on the portal, authenticated.
|
|
await expect(page.getByRole('heading', { name: /Zelfservice/i })).toBeVisible();
|
|
|
|
await page.getByRole('button', { name: /indienen/i }).click();
|
|
|
|
// The BFF accepted it and the page shows the confirmation with the reference.
|
|
const confirmation = page.getByText(/ontvangen/i);
|
|
await expect(confirmation).toBeVisible();
|
|
const reference = (await confirmation.textContent())?.match(/Referentie:\s*([0-9a-fA-F-]+)/)?.[1];
|
|
expect(reference, 'the confirmation shows a registration reference').toBeTruthy();
|
|
|
|
// The openbaar register (anonymous, its own origin) shows the submitted entry once the projection
|
|
// catches up. The projection updates asynchronously (NRC → event-subscriber), and the register loads
|
|
// on open, so reload until *this* submission's row appears. We poll on the reference cell (not a
|
|
// generic INGEDIEND cell): the shared verify stack already holds INGEDIEND rows from earlier checks,
|
|
// so a status-only poll would short-circuit on a stale row before our row is projected.
|
|
await page.goto('http://openbaar/');
|
|
await expect(page.getByRole('heading', { name: /Openbaar BIG-register/i })).toBeVisible();
|
|
|
|
// #78: the reference shown in the public register must be the exact one the citizen saw on the
|
|
// submit confirmation — no mismatch between the two portals.
|
|
await expect
|
|
.poll(async () => {
|
|
await page.reload();
|
|
return page.getByRole('cell', { name: reference }).count();
|
|
}, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] })
|
|
.toBeGreaterThan(0);
|
|
await expect(page.getByRole('row', { name: reference }).getByRole('cell', { name: 'INGEDIEND' }))
|
|
.toBeVisible();
|
|
|
|
// Approve via the temporary admin endpoint (reached directly on the compose network, as a
|
|
// behandelaar would until the behandel-portal exists — S-12). The zaak is opened off the request
|
|
// path by the worker, so wait for it before approving.
|
|
await expect
|
|
.poll(async () => {
|
|
const res = await request.get(`http://domain:8080/registrations/${reference}`);
|
|
return res.ok() ? (await res.json()).zaakUrl : null;
|
|
}, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] })
|
|
.toBeTruthy();
|
|
|
|
const approve = await request.post(`http://domain:8080/registrations/${reference}/approve`);
|
|
expect(approve.status()).toBe(204);
|
|
|
|
// The approval flows back to the projection; the openbaar register now shows *our* row (matched by
|
|
// its reference) as INGESCHREVEN.
|
|
await expect
|
|
.poll(async () => {
|
|
await page.reload();
|
|
return page.getByRole('row', { name: reference }).getByRole('cell', { name: 'INGESCHREVEN' }).count();
|
|
}, { timeout: 30_000, intervals: [1_000, 2_000, 3_000, 5_000] })
|
|
.toBeGreaterThan(0);
|
|
});
|