using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; [Collection("GatewayIntegration")] public class WardGatewayLocalPathTests : IAsyncLifetime { private readonly GatewayApiFixture _fixture; private readonly HttpClient _client; private Guid _encounterId; public WardGatewayLocalPathTests(GatewayApiFixture fixture) { _fixture = fixture; _client = fixture.CreateClient(); } public async Task InitializeAsync() { (_, _encounterId) = await GatewayTestSeeder.SeedActiveEncounterAsync(_fixture.Services); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task CriticalObservation_CreatesAlertLocally() { var resp = await _client.PostAsJsonAsync( $"/api/v1/encounters/{_encounterId}/observations", new { observationCode = "HEART_RATE", value = 160, unit = "bpm", source = "DEVICE", recordedAt = DateTimeOffset.UtcNow, idempotencyKey = Guid.NewGuid().ToString() }); resp.StatusCode.Should().Be(HttpStatusCode.Created); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); (await db.ClinicalAlerts.CountAsync()).Should().BeGreaterThan(0); (await db.BufferedSyncItems.CountAsync(b => !b.Synced)).Should().BeGreaterThan(0); } [Fact] public async Task DuplicateIdempotencyKey_ReturnsExisting() { var key = Guid.NewGuid().ToString(); var body = new { observationCode = "HEART_RATE", value = 80, unit = "bpm", source = "DEVICE", recordedAt = DateTimeOffset.UtcNow, idempotencyKey = key }; await _client.PostAsJsonAsync($"/api/v1/encounters/{_encounterId}/observations", body); var dup = await _client.PostAsJsonAsync($"/api/v1/encounters/{_encounterId}/observations", body); dup.StatusCode.Should().Be(HttpStatusCode.OK); var json = await dup.Content.ReadFromJsonAsync(); json!.RootElement.GetProperty("data").GetProperty("duplicate").GetBoolean().Should().BeTrue(); } [Fact] public async Task Acknowledge_WritesBufferedAck() { await _client.PostAsJsonAsync( $"/api/v1/encounters/{_encounterId}/observations", new { observationCode = "HEART_RATE", value = 160, unit = "bpm", source = "DEVICE", recordedAt = DateTimeOffset.UtcNow, idempotencyKey = Guid.NewGuid().ToString() }); var alertsResp = await _client.GetAsync($"/api/v1/encounters/{_encounterId}/alerts"); alertsResp.StatusCode.Should().Be(HttpStatusCode.OK); var alertsDoc = await alertsResp.Content.ReadFromJsonAsync(); var alertId = alertsDoc!.RootElement .GetProperty("data").GetProperty("items")[0].GetProperty("id").GetGuid(); var ackResp = await _client.PostAsJsonAsync( $"/api/v1/alerts/{alertId}/acknowledge", new AcknowledgeAlertRequest("Reviewing at bedside.")); ackResp.StatusCode.Should().Be(HttpStatusCode.OK); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); (await db.BufferedSyncItems.CountAsync(b => b.ItemType == BufferedSyncItemType.Ack && !b.Synced)).Should().Be(1); } [Fact] public async Task CentralDown_ReachabilityReportsDegraded() { await Task.Delay(TimeSpan.FromSeconds(2)); using var scope = _fixture.Services.CreateScope(); var reachability = scope.ServiceProvider.GetRequiredService(); reachability.IsCentralReachable.Should().BeFalse(); } [Fact] public async Task WarningObservation_CreatesWarningNoPage() { var resp = await _client.PostAsJsonAsync( $"/api/v1/encounters/{_encounterId}/observations", new { observationCode = "HEART_RATE", value = 105, unit = "bpm", source = "DEVICE", recordedAt = DateTimeOffset.UtcNow, idempotencyKey = Guid.NewGuid().ToString() }); resp.StatusCode.Should().Be(HttpStatusCode.Created); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var alert = await db.ClinicalAlerts.SingleAsync(); alert.Severity.Should().Be(AlertSeverity.Warning); alert.AlertType.Should().Be(AlertType.WarningHeartRate); alert.Status.Should().Be(AlertStatus.Open); } }