using System.Net; using System.Text; using EventSubscriber.Api; namespace EventSubscriber.Tests; /// /// 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. /// 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( () => 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( () => 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(() => client.GetZaakReferenceAsync(null!)); Assert.Null(capture.Seen); } } /// A test double for that records the last request and /// returns a scripted response — mirrors the ACL/domain gateway tests. internal sealed class StubHandler(Func> onSend) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken ct) => onSend(request); } /// Captures the request a client sent, including the (buffered) body. 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; }); }