using System.Net; using System.Net.Http.Json; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; [Collection("Integration")] public class GatewayRegistryTests : IAsyncLifetime { private readonly ApiFixture _fixture; private readonly HttpClient _client; private Guid _siteId; private Guid _gatewayId; public GatewayRegistryTests(ApiFixture fixture) { _fixture = fixture; _client = fixture.CreateClient(); } public async Task InitializeAsync() { _client.AsAdmin(); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); await GatewayRegistrySeeder.SeedAsync(db); _siteId = GatewayRegistrySeeder.DemoSiteId; _gatewayId = GatewayRegistrySeeder.DemoGatewayId; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task RegisterGateway_UnderSite_Returns201() { var resp = await _client.PostAsJsonAsync( $"/api/v1/sites/{_siteId}/gateways", new { gatewayCode = "GW-ICU-1A", department = "ICU" }); resp.StatusCode.Should().Be(HttpStatusCode.Created); var body = await resp.Content.ReadFromJsonAsync>(); body!.Data!.GatewayCode.Should().Be("GW-ICU-1A"); } [Fact] public async Task Heartbeat_UpdatesStatusAndBufferDepth() { _client.WithGatewayApiKey(_gatewayId); var resp = await _client.PatchAsJsonAsync( $"/api/v1/gateways/{_gatewayId}/heartbeat", new { status = "ONLINE", bufferDepth = 42, reportedAtUtc = DateTimeOffset.UtcNow }); resp.StatusCode.Should().Be(HttpStatusCode.OK); _client.AsAdmin(); var get = await _client.GetAsync($"/api/v1/gateways/{_gatewayId}"); var body = await get.Content.ReadFromJsonAsync>(); body!.Data!.Status.Should().Be("ONLINE"); body.Data.ReportedBufferDepth.Should().Be(42); body.Data.LastHeartbeatAt.Should().NotBeNull(); } [Fact] public async Task Heartbeat_InvalidApiKey_Returns401() { _client.WithGatewayApiKey(_gatewayId, apiKey: "wrong-key"); var resp = await _client.PatchAsJsonAsync( $"/api/v1/gateways/{_gatewayId}/heartbeat", new { status = "ONLINE", bufferDepth = 0, reportedAtUtc = DateTimeOffset.UtcNow }); resp.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task Heartbeat_DegradedStatus_Persisted() { _client.WithGatewayApiKey(_gatewayId); await _client.PatchAsJsonAsync( $"/api/v1/gateways/{_gatewayId}/heartbeat", new { status = "DEGRADED", bufferDepth = 847, reportedAtUtc = DateTimeOffset.UtcNow }); _client.AsAdmin(); var get = await _client.GetAsync($"/api/v1/gateways/{_gatewayId}"); var body = await get.Content.ReadFromJsonAsync>(); body!.Data!.Status.Should().Be("DEGRADED"); body.Data.ReportedBufferDepth.Should().Be(847); } [Fact] public async Task ListGateways_FilterByDepartment() { await _client.PostAsJsonAsync( $"/api/v1/sites/{_siteId}/gateways", new { gatewayCode = "GW-GM-8B", department = "GENERAL_MEDICINE" }); var resp = await _client.GetAsync($"/api/v1/sites/{_siteId}/gateways?department=ICU"); var body = await resp.Content.ReadFromJsonAsync>>(); body!.Data.Should().OnlyContain(g => g.Department == "ICU"); } }