Add remain services and fixes
This commit is contained in:
@@ -32,17 +32,20 @@ public class ApiFixture : WebApplicationFactory<Program>, IAsyncLifetime
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Apply migrations before the host starts — background services such as
|
||||
// ThresholdCacheLoader query the database during StartAsync.
|
||||
// Apply migrations and clean stale data before the host starts — background
|
||||
// services such as ThresholdCacheLoader query the database during StartAsync,
|
||||
// so the reset must happen while no hosted service holds a lock.
|
||||
var connectionString = "Host=localhost;Port=5436;Database=vigilcare_test;Username=postgres;Password=password";
|
||||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||||
.UseNpgsql(connectionString)
|
||||
.Options;
|
||||
await using (var migrateDb = new AppDbContext(options))
|
||||
{
|
||||
await migrateDb.Database.MigrateAsync();
|
||||
await DbResetHelper.ResetAsync(migrateDb);
|
||||
}
|
||||
|
||||
using var scope = Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
|
||||
var server = redis.GetServer(redis.GetEndPoints().First());
|
||||
await server.FlushDatabaseAsync(1);
|
||||
|
||||
@@ -11,12 +11,17 @@ public static class DbResetHelper
|
||||
try
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(@"
|
||||
SET lock_timeout = '3s';
|
||||
TRUNCATE TABLE sepsis_bundle_elements, sepsis_bundles,
|
||||
reconciliation_alerts, outbox_events, orders,
|
||||
clinical_alerts, news2_scores, observations, encounters,
|
||||
alert_thresholds, patients
|
||||
RESTART IDENTITY CASCADE;
|
||||
DELETE FROM sepsis_bundle_elements;
|
||||
DELETE FROM sepsis_bundles;
|
||||
DELETE FROM reconciliation_alerts;
|
||||
DELETE FROM outbox_events;
|
||||
DELETE FROM orders;
|
||||
DELETE FROM clinical_alerts;
|
||||
DELETE FROM news2_scores;
|
||||
DELETE FROM observations;
|
||||
DELETE FROM encounters;
|
||||
DELETE FROM alert_thresholds;
|
||||
DELETE FROM patients;
|
||||
");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ public class QsofaDetectorTests : IAsyncLifetime
|
||||
alert.PatientId.Should().Be(_patientId);
|
||||
alert.Details.Should().Contain("qSOFA score 2/3");
|
||||
|
||||
var outbox = await db.OutboxEvents.SingleAsync();
|
||||
outbox.Topic.Should().Be("alert.generated");
|
||||
var outbox = await db.OutboxEvents.SingleAsync(e => e.Topic == "alert.generated");
|
||||
outbox.PartitionKey.Should().Be(_encounterId.ToString());
|
||||
}
|
||||
|
||||
@@ -141,7 +140,7 @@ public class QsofaDetectorTests : IAsyncLifetime
|
||||
var alertCount = await db.ClinicalAlerts.CountAsync();
|
||||
alertCount.Should().Be(1, "WHERE NOT EXISTS prevents duplicate QSOFA_WARNING while one is open");
|
||||
|
||||
var outboxCount = await db.OutboxEvents.CountAsync();
|
||||
var outboxCount = await db.OutboxEvents.CountAsync(e => e.Topic == "alert.generated");
|
||||
outboxCount.Should().Be(1, "outbox event must be written exactly once");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
|
||||
[Collection("Integration")]
|
||||
@@ -206,4 +207,40 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
updated.ComplianceStatus.Should().Be(SepsisBundleComplianceStatus.NonCompliant);
|
||||
updated.CompletedAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeadlinePassed_MonitorMarksNonCompliant()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
|
||||
var bundle = await db.SepsisBundles.SingleAsync();
|
||||
bundle.DeadlineAt = DateTimeOffset.UtcNow.AddHours(-1);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var monitor = new SepsisBundleMonitorService(
|
||||
_fixture.Services,
|
||||
_fixture.Services.GetRequiredService<ClinicalMetrics>(),
|
||||
_fixture.Services.GetRequiredService<ILogger<SepsisBundleMonitorService>>());
|
||||
|
||||
await monitor.ScanOverdueBundlesAsync(CancellationToken.None);
|
||||
|
||||
var updated = await db.SepsisBundles.AsNoTracking().SingleAsync();
|
||||
updated.ComplianceStatus.Should().Be(SepsisBundleComplianceStatus.NonCompliant);
|
||||
updated.CompletedAt.Should().BeNull(
|
||||
"the monitor marks overdue bundles non-compliant without setting CompletedAt — " +
|
||||
"elements are still incomplete");
|
||||
|
||||
var elements = await db.SepsisBundleElements
|
||||
.AsNoTracking()
|
||||
.Where(e => e.BundleId == updated.Id)
|
||||
.ToListAsync();
|
||||
elements.Should().AllSatisfy(e =>
|
||||
e.Status.Should().Be(SepsisBundleElementStatus.Pending,
|
||||
"the monitor does not complete individual elements"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,7 @@ public class SirsDetectorTests : IAsyncLifetime
|
||||
alert.PatientId.Should().Be(_patientId);
|
||||
|
||||
// Verify outbox event was written in the same transaction
|
||||
var outbox = await db.OutboxEvents.SingleAsync();
|
||||
outbox.Topic.Should().Be("alert.generated");
|
||||
var outbox = await db.OutboxEvents.SingleAsync(e => e.Topic == "alert.generated");
|
||||
outbox.PartitionKey.Should().Be(_encounterId.ToString());
|
||||
}
|
||||
|
||||
@@ -189,7 +188,7 @@ public class SirsDetectorTests : IAsyncLifetime
|
||||
var alertCount = await db.ClinicalAlerts.CountAsync();
|
||||
alertCount.Should().Be(1, "WHERE NOT EXISTS prevents duplicate alert on redelivery");
|
||||
|
||||
var outboxCount = await db.OutboxEvents.CountAsync();
|
||||
var outboxCount = await db.OutboxEvents.CountAsync(e => e.Topic == "alert.generated");
|
||||
outboxCount.Should().Be(1, "outbox event must be written exactly once");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user