using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using StackExchange.Redis; [Collection("Integration")] public class EncountersListTests : IAsyncLifetime { private readonly ApiFixture _fixture; private readonly HttpClient _client; private Guid _activeIcuEncounterId; private Guid _activeSurgeryEncounterId; private Guid _dischargedEncounterId; public EncountersListTests(ApiFixture fixture) { _fixture = fixture; _client = fixture.CreateClient(); } public async Task InitializeAsync() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); var patientIcu = new Patient { Id = Guid.NewGuid(), Mrn = "MRN-WARD-001", FirstName = "Alice", LastName = "Icu", DateOfBirth = new DateOnly(1970, 3, 1), Gender = "F", CreatedAt = DateTimeOffset.UtcNow }; var patientSurgery = new Patient { Id = Guid.NewGuid(), Mrn = "MRN-WARD-002", FirstName = "Bob", LastName = "Surgery", DateOfBirth = new DateOnly(1965, 7, 12), Gender = "M", CreatedAt = DateTimeOffset.UtcNow }; _activeIcuEncounterId = Guid.NewGuid(); _activeSurgeryEncounterId = Guid.NewGuid(); _dischargedEncounterId = Guid.NewGuid(); db.Patients.AddRange(patientIcu, patientSurgery); db.Encounters.AddRange( new Encounter { Id = _activeIcuEncounterId, PatientId = patientIcu.Id, EncounterType = EncounterType.Inpatient, Status = EncounterStatus.Active, Department = Department.Icu, AttendingPhysician = "Dr. Ward", RoomBed = "ICU-3", AdmittedAt = DateTimeOffset.UtcNow.AddHours(-2), CreatedAt = DateTimeOffset.UtcNow }, new Encounter { Id = _activeSurgeryEncounterId, PatientId = patientSurgery.Id, EncounterType = EncounterType.Inpatient, Status = EncounterStatus.Active, Department = Department.Surgery, AttendingPhysician = "Dr. Ward", RoomBed = "S-12", AdmittedAt = DateTimeOffset.UtcNow.AddHours(-1), CreatedAt = DateTimeOffset.UtcNow }, new Encounter { Id = _dischargedEncounterId, PatientId = patientSurgery.Id, EncounterType = EncounterType.Outpatient, Status = EncounterStatus.Discharged, Department = Department.Surgery, AttendingPhysician = "Dr. Ward", AdmittedAt = DateTimeOffset.UtcNow.AddDays(-3), DischargedAt = DateTimeOffset.UtcNow.AddDays(-2), CreatedAt = DateTimeOffset.UtcNow.AddDays(-3) }); db.News2Scores.Add(new News2Score { Id = Guid.NewGuid(), EncounterId = _activeIcuEncounterId, PatientId = patientIcu.Id, TotalScore = 7, RiskLevel = "HIGH", CalculatedAt = DateTimeOffset.UtcNow, RespRateScore = 1, Spo2Score = 0, SystolicBpScore = 1, HeartRateScore = 1, ConsciousnessScore = 0, TemperatureScore = 0, SupplementalO2Score = 0 }); db.ClinicalAlerts.Add(new ClinicalAlert { Id = Guid.NewGuid(), EncounterId = _activeIcuEncounterId, PatientId = patientIcu.Id, AlertType = AlertType.News2Emergency, Severity = AlertSeverity.Critical, Details = "NEWS2 score 7", Status = AlertStatus.Open, TriggeredAt = DateTimeOffset.UtcNow }); db.SofaScores.Add(new SofaScore { Id = Guid.NewGuid(), EncounterId = _activeIcuEncounterId, PatientId = patientIcu.Id, TotalScore = 8, RespiratoryScore = 2, CoagulationScore = 1, LiverScore = 1, CardiovascularScore = 2, CnsScore = 1, RenalScore = 1, DeltaFromBaseline = 2, CalculatedAt = DateTimeOffset.UtcNow, CreatedAt = DateTimeOffset.UtcNow }); db.GcsScores.Add(new GcsScore { Id = Guid.NewGuid(), EncounterId = _activeIcuEncounterId, PatientId = patientIcu.Id, EyeScore = 3, VerbalScore = 4, MotorScore = 5, TotalScore = 12, Classification = "MODERATE", CalculatedAt = DateTimeOffset.UtcNow, CreatedAt = DateTimeOffset.UtcNow }); db.Observations.Add(new Observation { Id = Guid.NewGuid(), EncounterId = _activeIcuEncounterId, ObservationCode = "HEART_RATE", Value = 110m, Unit = "bpm", Source = ObservationSource.Manual, RecordedAt = DateTimeOffset.UtcNow.AddHours(-3), CreatedAt = DateTimeOffset.UtcNow }); await db.SaveChangesAsync(); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task ListActiveEncounters_ReturnsSummariesWithPatientNames() { var resp = await _client.GetAsync("/api/v1/encounters?status=ACTIVE&page=1&pageSize=20"); resp.StatusCode.Should().Be(HttpStatusCode.OK); var body = await resp.Content.ReadFromJsonAsync(); var items = body!.RootElement.GetProperty("data").GetProperty("items"); items.GetArrayLength().Should().Be(2); var icu = items.EnumerateArray() .First(i => i.GetProperty("encounterId").GetGuid() == _activeIcuEncounterId); icu.GetProperty("firstName").GetString().Should().Be("Alice"); icu.GetProperty("lastName").GetString().Should().Be("Icu"); icu.GetProperty("mrn").GetString().Should().Be("MRN-WARD-001"); icu.GetProperty("roomBed").GetString().Should().Be("ICU-3"); icu.GetProperty("news2Score").GetInt32().Should().Be(7); icu.GetProperty("openAlertCount").GetInt32().Should().Be(1); icu.GetProperty("qsofaScore").GetInt32().Should().Be(0); icu.GetProperty("sofaScore").GetInt32().Should().Be(8); icu.GetProperty("sofaDelta").GetInt32().Should().Be(2); icu.GetProperty("gcsScore").GetInt32().Should().Be(12); icu.GetProperty("gcsClassification").GetString().Should().Be("MODERATE"); icu.GetProperty("attendingPhysician").GetString().Should().Be("Dr. Ward"); icu.GetProperty("lastObservationAt").GetDateTimeOffset().Should().NotBe(default); icu.GetProperty("isSimulated").GetBoolean().Should().BeFalse(); } [Fact] public async Task ListByDepartment_FiltersCorrectly() { var resp = await _client.GetAsync("/api/v1/encounters?status=ACTIVE&department=SURGERY"); resp.StatusCode.Should().Be(HttpStatusCode.OK); var body = await resp.Content.ReadFromJsonAsync(); var items = body!.RootElement.GetProperty("data").GetProperty("items"); items.GetArrayLength().Should().Be(1); items[0].GetProperty("encounterId").GetGuid().Should().Be(_activeSurgeryEncounterId); items[0].GetProperty("firstName").GetString().Should().Be("Bob"); } [Fact] public async Task ListInvalidStatus_Returns400() { var resp = await _client.GetAsync("/api/v1/encounters?status=Active"); resp.StatusCode.Should().Be(HttpStatusCode.BadRequest); } }