feat(event-subscriber): enrich the projection with the zaak reference via the ACL (refs #78)
On each notification the subscriber reads the zaak's reference (identificatie) through the ACL — the only code allowed to talk to ZGW (§8.1) — and persists it on the register_projection row and in the processed_notifications replay log. Storing it in the log keeps rebuild log-only (ADR-0008): no ACL/ZGW access on rebuild. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using EventSubscriber.Api;
|
||||
|
||||
namespace EventSubscriber.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the subscriber's ACL client, which reads a zaak's reference (identificatie) through
|
||||
/// the ACL — the only code allowed to talk to ZGW (§8.1, #78). Uses a scripted message handler so no
|
||||
/// real ACL is required.
|
||||
/// </summary>
|
||||
public class AclHttpClientTests
|
||||
{
|
||||
private static AclHttpClient Client(StubHandler handler) =>
|
||||
new(new HttpClient(handler) { BaseAddress = new Uri("http://acl/") });
|
||||
|
||||
[Fact]
|
||||
public async Task Reads_a_zaak_reference_by_posting_the_zaak_url_and_returns_it()
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.OK, """{"reference":"REG-42"}"""));
|
||||
|
||||
var reference = await client.GetZaakReferenceAsync(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
|
||||
|
||||
Assert.Equal("REG-42", reference);
|
||||
Assert.Equal(HttpMethod.Post, capture.Seen!.Method);
|
||||
Assert.Equal("http://acl/zaken/reference", capture.Seen.RequestUri!.ToString());
|
||||
Assert.Contains("\"zaakUrl\":\"http://openzaak/zaken/api/v1/zaken/abc\"", capture.Body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Throws_when_the_acl_rejects_the_request()
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.BadGateway));
|
||||
|
||||
await Assert.ThrowsAsync<HttpRequestException>(
|
||||
() => client.GetZaakReferenceAsync(new Uri("http://openzaak/zaken/api/v1/zaken/abc")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Throws_when_the_acl_returns_an_empty_body()
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.OK, "null"));
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => client.GetZaakReferenceAsync(new Uri("http://openzaak/zaken/api/v1/zaken/abc")));
|
||||
Assert.Contains("empty", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rejects_a_null_zaak_url_without_sending_a_request()
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.OK, """{"reference":"REG-1"}"""));
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetZaakReferenceAsync(null!));
|
||||
Assert.Null(capture.Seen);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A test double for <see cref="HttpMessageHandler"/> that records the last request and
|
||||
/// returns a scripted response — mirrors the ACL/domain gateway tests.</summary>
|
||||
internal sealed class StubHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> onSend) : HttpMessageHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
|
||||
=> onSend(request);
|
||||
}
|
||||
|
||||
/// <summary>Captures the request a client sent, including the (buffered) body.</summary>
|
||||
internal sealed class RequestCapture
|
||||
{
|
||||
public HttpRequestMessage? Seen { get; private set; }
|
||||
public string? Body { get; private set; }
|
||||
|
||||
public StubHandler Responds(HttpStatusCode status, string? json = null) => new(async req =>
|
||||
{
|
||||
Seen = req;
|
||||
Body = req.Content is null ? null : await req.Content.ReadAsStringAsync();
|
||||
var response = new HttpResponseMessage(status);
|
||||
if (json is not null)
|
||||
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
return response;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user