using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; [Collection("Integration")] public class AlertLifecycleTests : IAsyncLifetime { private readonly ApiFixture _fixture; private readonly HttpClient _client; private Guid _alertId; public AlertLifecycleTests(ApiFixture fixture) { _fixture = fixture; _client = fixture.CreateClient(); _client.AsNurse(); } public async Task InitializeAsync() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); var patient = new Patient { Id = Guid.NewGuid(), Mrn = "MRN-T002", FirstName = "Alert", LastName = "Test", DateOfBirth = new DateOnly(1980, 6, 15), Gender = "M", CreatedAt = DateTimeOffset.UtcNow }; var encounter = new Encounter { Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient, Status = EncounterStatus.Active, Department = Department.GeneralMedicine, AttendingPhysician = "Dr. Test", AdmittedAt = DateTimeOffset.UtcNow, CreatedAt = DateTimeOffset.UtcNow }; var alert = new ClinicalAlert { Id = Guid.NewGuid(), EncounterId = encounter.Id, PatientId = patient.Id, AlertType = AlertType.CriticalPotassiumMeqL, Severity = AlertSeverity.Critical, Details = "Potassium 2.1 mEq/L is below critical low.", Status = AlertStatus.Open, TriggeredAt = DateTimeOffset.UtcNow }; db.Patients.Add(patient); db.Encounters.Add(encounter); db.ClinicalAlerts.Add(alert); await db.SaveChangesAsync(); _alertId = alert.Id; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task AcknowledgeAlert_TransitionsToAcknowledged() { var resp = await _client.PostAsJsonAsync( $"/api/v1/alerts/{_alertId}/acknowledge", new AcknowledgeAlertRequest("Reviewing now, ordering repeat labs.")); resp.StatusCode.Should().Be(HttpStatusCode.OK); var body = await resp.Content.ReadFromJsonAsync(); body!.RootElement.GetProperty("data").GetProperty("status").GetString() .Should().Be("Acknowledged"); body.RootElement.GetProperty("data").GetProperty("acknowledgedBy").GetString() .Should().Be("Test NURSE (NURSE)"); } [Fact] public async Task ResolveWithoutAcknowledge_Returns409() { var resp = await _client.PostAsync($"/api/v1/alerts/{_alertId}/resolve", null); resp.StatusCode.Should().Be(HttpStatusCode.Conflict); var body = await resp.Content.ReadFromJsonAsync(); body!.RootElement.GetProperty("error").GetProperty("code").GetString() .Should().Be("ALERT_NOT_ACKNOWLEDGED"); } [Fact] public async Task AcknowledgeThenResolve_FullLifecycle() { await _client.PostAsJsonAsync( $"/api/v1/alerts/{_alertId}/acknowledge", new AcknowledgeAlertRequest("Treated.")); var resolveResp = await _client.PostAsync( $"/api/v1/alerts/{_alertId}/resolve", null); resolveResp.StatusCode.Should().Be(HttpStatusCode.OK); var body = await resolveResp.Content.ReadFromJsonAsync(); body!.RootElement.GetProperty("data").GetProperty("status").GetString() .Should().Be("Resolved"); } }