Files
vigilcare-clinical/VigilCareClinicalAPI.Tests/Alerts/AlertQualityScenarioAttributionTests.cs
voltsrage 80b009fd23
CI / backend (push) Successful in 8m52s
CI / frontend (push) Failing after 1m39s
feature: Self-Service Clinical Testing Sessions
2026-08-06 04:03:04 +08:00

166 lines
6.3 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
[Collection("Integration")]
public class AlertQualityScenarioAttributionTests : IAsyncLifetime
{
private readonly ApiFixture _fixture;
private readonly HttpClient _client;
public AlertQualityScenarioAttributionTests(ApiFixture fixture)
{
_fixture = fixture;
_client = fixture.CreateClient();
_client.AsNurse();
}
public async Task InitializeAsync()
{
using var scope = _fixture.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await DbResetHelper.ResetAsync(db);
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task AlertQuality_FiltersByScenarioId()
{
Guid matchFeedbackId;
Guid otherFeedbackId;
using (var scope = _fixture.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var patientA = NewPatient("MRN-SCEN-A");
var patientB = NewPatient("MRN-SCEN-B");
var encounterA = NewEncounter(patientA.Id);
var encounterB = NewEncounter(patientB.Id);
var alertA = NewAlert(encounterA.Id, patientA.Id, AlertType.News2Warning);
var alertB = NewAlert(encounterB.Id, patientB.Id, AlertType.News2Emergency);
db.Patients.AddRange(patientA, patientB);
db.Encounters.AddRange(encounterA, encounterB);
db.ClinicalAlerts.AddRange(alertA, alertB);
db.SimulationRuns.Add(new SimulationRun
{
Id = Guid.NewGuid(),
ScenarioId = "uti-sepsis-elderly-01",
ScenarioName = "UTI Sepsis",
Speed = 60,
Status = SimulationRunStatus.Completed,
PatientId = patientA.Id,
EncounterId = encounterA.Id,
SessionId = "session-b-alert-quality",
StartedByUserId = "tester",
StartedAt = DateTimeOffset.UtcNow.AddHours(-1),
CompletedAt = DateTimeOffset.UtcNow,
});
db.SimulationRuns.Add(new SimulationRun
{
Id = Guid.NewGuid(),
ScenarioId = "medication-false-alarm-01",
ScenarioName = "Med False Alarm",
Speed = 60,
Status = SimulationRunStatus.Completed,
PatientId = patientB.Id,
EncounterId = encounterB.Id,
SessionId = "session-b-alert-quality",
StartedByUserId = "tester",
StartedAt = DateTimeOffset.UtcNow.AddHours(-1),
CompletedAt = DateTimeOffset.UtcNow,
});
var fbA = new AlertFeedback
{
Id = Guid.NewGuid(),
AlertId = alertA.Id,
UserId = Guid.NewGuid(),
FeedbackType = AlertFeedbackType.WouldAct,
CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-10),
};
var fbB = new AlertFeedback
{
Id = Guid.NewGuid(),
AlertId = alertB.Id,
UserId = Guid.NewGuid(),
FeedbackType = AlertFeedbackType.FalsePositive,
CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-5),
};
db.AlertFeedbacks.AddRange(fbA, fbB);
await db.SaveChangesAsync();
matchFeedbackId = fbA.Id;
otherFeedbackId = fbB.Id;
}
var all = await _client.GetAsync("/api/v1/alerts/quality-metrics/feedback");
all.StatusCode.Should().Be(HttpStatusCode.OK);
var allBody = await all.Content.ReadFromJsonAsync<JsonDocument>();
var allItems = allBody!.RootElement.GetProperty("data").GetProperty("items");
allItems.GetArrayLength().Should().Be(2);
var match = allItems.EnumerateArray()
.Single(e => e.GetProperty("id").GetGuid() == matchFeedbackId);
match.GetProperty("scenarioId").GetString().Should().Be("uti-sepsis-elderly-01");
match.GetProperty("sessionId").GetString().Should().Be("session-b-alert-quality");
var filtered = await _client.GetAsync(
"/api/v1/alerts/quality-metrics/feedback?scenarioId=uti-sepsis-elderly-01");
filtered.StatusCode.Should().Be(HttpStatusCode.OK);
var filteredBody = await filtered.Content.ReadFromJsonAsync<JsonDocument>();
var filteredItems = filteredBody!.RootElement.GetProperty("data").GetProperty("items");
filteredItems.GetArrayLength().Should().Be(1);
filteredItems[0].GetProperty("id").GetGuid().Should().Be(matchFeedbackId);
filteredItems[0].GetProperty("scenarioId").GetString().Should().Be("uti-sepsis-elderly-01");
// Other scenario's feedback must not leak into the filter.
filteredItems.EnumerateArray()
.Any(e => e.GetProperty("id").GetGuid() == otherFeedbackId)
.Should().BeFalse();
}
private static Patient NewPatient(string mrn) => new()
{
Id = Guid.NewGuid(),
Mrn = mrn,
FirstName = "Sim",
LastName = "Patient",
DateOfBirth = new DateOnly(1980, 1, 1),
Gender = "F",
CreatedAt = DateTimeOffset.UtcNow,
IsSimulated = true,
};
private static Encounter NewEncounter(Guid patientId) => new()
{
Id = Guid.NewGuid(),
PatientId = patientId,
EncounterType = EncounterType.Inpatient,
Status = EncounterStatus.Active,
Department = Department.Icu,
AttendingPhysician = "Dr. Test",
AdmittedAt = DateTimeOffset.UtcNow,
CreatedAt = DateTimeOffset.UtcNow,
};
private static ClinicalAlert NewAlert(Guid encounterId, Guid patientId, AlertType type) => new()
{
Id = Guid.NewGuid(),
EncounterId = encounterId,
PatientId = patientId,
AlertType = type,
Severity = AlertSeverity.Warning,
Details = "Test alert",
Status = AlertStatus.Acknowledged,
TriggeredAt = DateTimeOffset.UtcNow.AddMinutes(-30),
AcknowledgedAt = DateTimeOffset.UtcNow.AddMinutes(-20),
AcknowledgedBy = "nurse",
};
}