Files
atomic-design-poc/backend/tests/BigRegister.Tests/BeoordelingIdMismatchTests.cs
T
ehoandClaude Opus 5 194cccfd02 refactor: rename Application → Aanvraag across the wire (Step 1/8)
The wire said Application, the domain said Aanvraag — one aggregate with
two names at every hop. Rename the backend DTOs and the /applications
route to /aanvragen, regenerate the typed client, and rename the frontend
adapter/store to match.

Renamed: ApplicationSummaryDto/DetailDto, CreateApplicationRequest,
SubmitApplicationRequest/Response → Aanvraag* equivalents;
ApplicationsAdapter/Store → AanvragenAdapter/Store;
applications.adapter.ts/applications.store.ts → aanvragen.*.

Left untouched: the admin Case/Zaak vocabulary (/admin/cases,
AdminCasesStore) — a separate read model, not part of this rename; the
internal BigRegister.Domain.Applications namespace and the Applications
EF table (renaming those needs a new EF migration, out of scope here).

Part of the dashboard-readability refactor (see the approved plan).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:33:16 +02:00

89 lines
4.5 KiB
C#

using System.Net.Http.Json;
using BigRegister.Api.Contracts;
using BigRegister.Api.Data;
using BigRegister.Domain.Applications;
using BigRegister.Domain.Authorization;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
namespace BigRegister.Tests;
/// <summary>Wraps <see cref="LocalZaakSource"/> but returns a DIFFERENT case id than the
/// underlying Aanvraag.Id — reproduces exactly what <c>OpenZaakZaakSource</c> does in
/// production (the FE-facing case id from <c>ListCases</c> is the ZGW zaak's own uuid, not
/// <c>ApplicationStore</c>'s primary key) without needing a live OpenZaak, so the besluit
/// endpoint's Referentie-based resolution (the fix below) gets coverage on every push.</summary>
file sealed class IdMismatchZaakSource : IZaakSource
{
private readonly LocalZaakSource inner = new();
private static AanvraagSummaryDto Rekey(AanvraagSummaryDto dto) => dto with { Id = $"zaak-{dto.Id}" };
public IReadOnlyList<AanvraagSummaryDto> ListCases(DateTimeOffset now) =>
inner.ListCases(now).Select(Rekey).ToList();
public IReadOnlyList<AanvraagSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) =>
inner.ListMyCases(caller, now).Select(Rekey).ToList();
public (string Referentie, AanvraagStatusDto Status, string? ZaakUrl) CreateZaak(
Aanvraag.Submitted aanvraag, DateTimeOffset now, CallerIdentity caller) => inner.CreateZaak(aanvraag, now, caller);
public void RecordBesluit(Aanvraag aanvraag, Besluit besluit, string? toelichting, DateTimeOffset now, CallerIdentity caller) =>
inner.RecordBesluit(aanvraag, besluit, toelichting, now, caller);
}
/// <summary>
/// Regression for a real, live-repro'd bug: recording a besluit from the behandelportal always
/// 404'd against a real OpenZaak. Root cause — <c>POST /beoordeling/{id}/besluit</c> looked
/// <c>id</c> up directly in <c>ApplicationStore</c> (its own primary key), but <c>id</c> is
/// whatever <c>IZaakSource.ListCases</c> handed the FE; under <c>OpenZaakZaakSource</c> that's
/// the ZGW zaak's own uuid, a different value entirely. Fixed by resolving the case through
/// the same <c>ListCases</c> seak the GET sibling (<see cref="BeoordelingTests"/>) already uses,
/// then to the local <c>Aanvraag</c> via its Referentie (<c>ApplicationStore.GetByReferentie</c>)
/// — the one identifier stable across both sources. <see cref="IdMismatchZaakSource"/>
/// reproduces the id divergence without a live OpenZaak.
/// </summary>
public class BeoordelingIdMismatchTests
{
private static WebApplicationFactory<Program> Factory()
{
var dbPath = Path.Combine(Path.GetTempPath(), $"bigregister-id-mismatch-{Guid.NewGuid():N}.db");
return new WebApplicationFactory<Program>().WithWebHostBuilder(builder => builder
.UseSetting("ConnectionStrings:AppDb", $"Data Source={dbPath}")
.ConfigureTestServices(services => services.AddSingleton<IZaakSource, IdMismatchZaakSource>()));
}
private static HttpRequestMessage Behandelaar(HttpMethod method, string path, object? body = null)
{
var req = new HttpRequestMessage(method, path);
req.Headers.Add("X-Medewerker", "medewerker-1");
if (body is not null) req.Content = JsonContent.Create(body);
return req;
}
[Fact]
public async Task Besluit_resolves_by_referentie_when_the_case_id_differs_from_the_local_aanvraag_id()
{
using var factory = Factory();
using var client = factory.CreateClient();
var created = await client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
var app = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
var submit = await client.PostAsJsonAsync($"/api/v1/aanvragen/{app.Id}/submit", new { diplomaHerkomst = "handmatig" });
submit.EnsureSuccessStatusCode();
var werkvoorraad = await client.SendAsync(Behandelaar(HttpMethod.Get, "/api/v1/werkvoorraad"));
var items = (await werkvoorraad.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
var caseId = Assert.Single(items).Id;
// Sanity: the id divergence this test exists for is real, not accidentally absent.
Assert.NotEqual(app.Id, caseId);
var res = await client.SendAsync(Behandelaar(HttpMethod.Post, $"/api/v1/beoordeling/{caseId}/besluit",
new { besluit = "Afwijzen", toelichting = "onvolledig" }));
res.EnsureSuccessStatusCode();
var body = (await res.Content.ReadFromJsonAsync<RecordBesluitResponse>())!;
Assert.Equal("Afgewezen", body.Status.Tag);
}
}