using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; [Collection("Integration")] public class MedicationServiceTests : IAsyncLifetime { private readonly ApiFixture _fixture; private Guid _patientId; private Guid _activeEncounterId; private Guid _dischargedEncounterId; public MedicationServiceTests(ApiFixture fixture) => _fixture = fixture; public async Task InitializeAsync() => await ResetAndSeedAsync(); public Task DisposeAsync() => Task.CompletedTask; private async Task ResetAndSeedAsync() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); var patient = new Patient { Id = Guid.NewGuid(), Mrn = "MRN-MED-001", FirstName = "Med", LastName = "Test", DateOfBirth = new DateOnly(1980, 3, 15), Gender = "F", CreatedAt = DateTimeOffset.UtcNow }; var activeEncounter = new Encounter { Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient, Status = EncounterStatus.Active, Department = Department.Icu, AttendingPhysician = "Dr. Medication", AdmittedAt = DateTimeOffset.UtcNow, CreatedAt = DateTimeOffset.UtcNow }; var dischargedEncounter = new Encounter { Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient, Status = EncounterStatus.Discharged, Department = Department.Icu, AttendingPhysician = "Dr. Medication", AdmittedAt = DateTimeOffset.UtcNow.AddDays(-3), DischargedAt = DateTimeOffset.UtcNow.AddHours(-1), CreatedAt = DateTimeOffset.UtcNow }; db.Patients.Add(patient); db.Encounters.Add(activeEncounter); db.Encounters.Add(dischargedEncounter); await db.SaveChangesAsync(); _patientId = patient.Id; _activeEncounterId = activeEncounter.Id; _dischargedEncounterId = dischargedEncounter.Id; } [Fact] public async Task Create_OnActiveEncounter_Succeeds() { await ResetAndSeedAsync(); using var scope = _fixture.Services.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); var med = await service.CreateAsync(_activeEncounterId, new CreateMedicationAdministrationRequest( "metoprolol", 25m, "mg", "PO", null, "nurse-1")); med.Should().NotBeNull(); med.DrugName.Should().Be("metoprolol"); med.Dose.Should().Be(25m); med.DoseUnit.Should().Be("mg"); med.Route.Should().Be("PO"); med.EncounterId.Should().Be(_activeEncounterId); var db = scope.ServiceProvider.GetRequiredService(); var persisted = await db.MedicationAdministrations.FindAsync(med.Id); persisted.Should().NotBeNull(); } [Fact] public async Task Create_OnDischargedEncounter_ThrowsConflict() { await ResetAndSeedAsync(); using var scope = _fixture.Services.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); var act = () => service.CreateAsync(_dischargedEncounterId, new CreateMedicationAdministrationRequest( "metoprolol", 25m, "mg", "PO", null, "nurse-1")); await act.Should().ThrowAsync(); } [Fact] public async Task ListByEncounter_ReturnsPaginated() { await ResetAndSeedAsync(); using var scope = _fixture.Services.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); await service.CreateAsync(_activeEncounterId, new CreateMedicationAdministrationRequest( "metoprolol", 25m, "mg", "PO", null, "nurse-1")); await service.CreateAsync(_activeEncounterId, new CreateMedicationAdministrationRequest( "morphine", 4m, "mg", "IV", null, "nurse-2")); await service.CreateAsync(_activeEncounterId, new CreateMedicationAdministrationRequest( "insulin", 10m, "units", "SubQ", null, "nurse-1")); var result = await service.ListByEncounterAsync(_activeEncounterId, null, 1, 2); result.TotalCount.Should().Be(3); result.Items.Should().HaveCount(2); result.TotalPages.Should().Be(2); } [Fact] public async Task GetRecentForEncounter_FiltersByWindow() { await ResetAndSeedAsync(); using var scope = _fixture.Services.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); var db = scope.ServiceProvider.GetRequiredService(); // Recent metoprolol — within 90-min window await service.CreateAsync(_activeEncounterId, new CreateMedicationAdministrationRequest( "metoprolol", 25m, "mg", "PO", DateTimeOffset.UtcNow.AddMinutes(-30), "nurse-1")); // Old metoprolol — outside 90-min window db.MedicationAdministrations.Add(new MedicationAdministration { Id = Guid.NewGuid(), EncounterId = _activeEncounterId, DrugName = "metoprolol", Dose = 50m, DoseUnit = "mg", Route = "PO", AdministeredAt = DateTimeOffset.UtcNow.AddMinutes(-120), AdministeredBy = "nurse-2" }); await db.SaveChangesAsync(); var recent = await service.GetRecentForEncounterAsync( _activeEncounterId, "SYSTOLIC_BP", 90); recent.Should().HaveCount(1); recent[0].Dose.Should().Be(25m); } }