Files
atomic-design-poc/backend/tests/BigRegister.Tests/NotificatieTests.cs
T
ehoandClaude Sonnet 5 bea04549dd
CI / frontend (push) Successful in 2m33s
CI / backend (push) Successful in 1m45s
CI / storybook-a11y (push) Successful in 7m47s
CI / e2e (push) Successful in 4m3s
CI / semgrep (push) Successful in 1m7s
CI / api-client-drift (push) Successful in 2m3s
feat(zgw): finish WP-52 OpenZaak Notificaties (NRC) webhook slice
Endpoint/DTO/options landed already in c4dd846; this closes the loop with
NotificatieTests.cs (accept/reject/missing-header, asserting the AuthzAuditStore
row), missing appsettings.json keys (also backfills DrcBaseUrl/
InformatieobjecttypeUrls, stale since WP-51), and the webhook + abonnement
provisioning docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 08:07:50 +02:00

73 lines
2.6 KiB
C#

using System.Net;
using System.Net.Http.Json;
using BigRegister.Api.Contracts;
using BigRegister.Api.Zgw;
using Microsoft.AspNetCore.Mvc.Testing;
namespace BigRegister.Tests;
/// WP-52: the inbound Notificaties (NRC) webhook — auth accept/reject + the audit trail it
/// writes via AuthzAuditStore (no Principal exists for an NRC caller, so this doesn't go
/// through the Principal-shaped AuditAuthz helper the user-facing endpoints use).
public class NotificatieTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
{
private readonly HttpClient _client = factory.CreateClient();
private static NotificatieDto Sample(string zaakUrl) => new(
Kanaal: "zaken",
HoofdObject: zaakUrl,
Resource: "zaak",
ResourceUrl: zaakUrl,
Actie: "update",
Aanmaakdatum: DateTimeOffset.UtcNow,
Kenmerken: null);
private async Task<HttpResponseMessage> Post(string? authorization, string zaakUrl)
{
var req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/zgw/notificaties")
{
Content = JsonContent.Create(Sample(zaakUrl)),
};
if (authorization is not null) req.Headers.TryAddWithoutValidation("Authorization", authorization);
return await _client.SendAsync(req);
}
private async Task<List<AuthzAuditDto>> AuditLog()
{
var req = new HttpRequestMessage(HttpMethod.Get, "/api/v1/admin/audit");
req.Headers.Add("X-Role", "admin");
var res = await _client.SendAsync(req);
res.EnsureSuccessStatusCode();
return (await res.Content.ReadFromJsonAsync<List<AuthzAuditDto>>())!;
}
[Fact]
public async Task Correct_shared_secret_is_accepted_and_recorded()
{
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
var res = await Post("test-nrc-secret", zaak);
Assert.Equal(HttpStatusCode.NoContent, res.StatusCode);
Assert.Contains(await AuditLog(), e =>
e.Action == "zgw:notificatie" && e.Resource == zaak && e.Decision == "allow" && e.Role == "nrc");
}
[Fact]
public async Task Wrong_secret_is_rejected_and_recorded()
{
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
var res = await Post("not-the-secret", zaak);
Assert.Equal(HttpStatusCode.Unauthorized, res.StatusCode);
Assert.Contains(await AuditLog(), e =>
e.Action == "zgw:notificatie" && e.Resource == zaak && e.Decision == "deny");
}
[Fact]
public async Task Missing_authorization_header_is_rejected()
{
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
Assert.Equal(HttpStatusCode.Unauthorized, (await Post(null, zaak)).StatusCode);
}
}