125 lines
4.6 KiB
C#
125 lines
4.6 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using FluentAssertions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
/// <summary>
|
|
/// Degraded-mode tests — central is unreachable; gateway clinical path remains authoritative locally.
|
|
/// </summary>
|
|
[Collection("GatewayIntegration")]
|
|
public class WardGatewayPartitionTests : IAsyncLifetime
|
|
{
|
|
private readonly GatewayApiFixture _fixture;
|
|
private readonly HttpClient _client;
|
|
private Guid _encounterId;
|
|
|
|
public WardGatewayPartitionTests(GatewayApiFixture fixture)
|
|
{
|
|
_fixture = fixture;
|
|
_client = fixture.CreateClient();
|
|
}
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
(_, _encounterId) = await GatewayTestSeeder.SeedActiveEncounterAsync(_fixture.Services);
|
|
}
|
|
|
|
public Task DisposeAsync() => Task.CompletedTask;
|
|
|
|
[Fact]
|
|
public async Task Partition_CriticalIngest_CreatesLocalAlertAndBuffer()
|
|
{
|
|
var resp = await _client.PostAsJsonAsync(
|
|
$"/api/v1/encounters/{_encounterId}/observations",
|
|
new
|
|
{
|
|
observationCode = "HEART_RATE",
|
|
value = 160,
|
|
unit = "bpm",
|
|
source = "DEVICE",
|
|
recordedAt = DateTimeOffset.UtcNow,
|
|
idempotencyKey = $"partition-{Guid.NewGuid()}"
|
|
});
|
|
|
|
resp.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
|
|
using var scope = _fixture.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<GatewayDbContext>();
|
|
var alerts = await db.ClinicalAlerts.Where(a => a.EncounterId == _encounterId).ToListAsync();
|
|
alerts.Should().ContainSingle();
|
|
alerts[0].Severity.Should().Be(AlertSeverity.Critical);
|
|
|
|
(await db.BufferedSyncItems.CountAsync(b => !b.Synced)).Should().BeGreaterThanOrEqualTo(2);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Partition_ListAlertsViaGatewayApi()
|
|
{
|
|
await _client.PostAsJsonAsync(
|
|
$"/api/v1/encounters/{_encounterId}/observations",
|
|
new
|
|
{
|
|
observationCode = "HEART_RATE",
|
|
value = 160,
|
|
unit = "bpm",
|
|
source = "DEVICE",
|
|
recordedAt = DateTimeOffset.UtcNow,
|
|
idempotencyKey = $"partition-list-{Guid.NewGuid()}"
|
|
});
|
|
|
|
var resp = await _client.GetAsync($"/api/v1/encounters/{_encounterId}/alerts");
|
|
resp.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
var doc = await resp.Content.ReadFromJsonAsync<JsonDocument>();
|
|
doc!.RootElement.GetProperty("data").GetProperty("items").GetArrayLength()
|
|
.Should().BeGreaterThan(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Partition_ListActiveEncounters_FromLocalReplica()
|
|
{
|
|
var resp = await _client.GetAsync("/api/v1/encounters?status=ACTIVE&department=ICU");
|
|
resp.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
var doc = await resp.Content.ReadFromJsonAsync<JsonDocument>();
|
|
var items = doc!.RootElement.GetProperty("data").GetProperty("items");
|
|
items.GetArrayLength().Should().BeGreaterThan(0);
|
|
items[0].GetProperty("encounterId").GetGuid().Should().Be(_encounterId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Partition_AcknowledgeAndResolve_BuffersLocally()
|
|
{
|
|
await _client.PostAsJsonAsync(
|
|
$"/api/v1/encounters/{_encounterId}/observations",
|
|
new
|
|
{
|
|
observationCode = "HEART_RATE",
|
|
value = 160,
|
|
unit = "bpm",
|
|
source = "DEVICE",
|
|
recordedAt = DateTimeOffset.UtcNow,
|
|
idempotencyKey = $"partition-lifecycle-{Guid.NewGuid()}"
|
|
});
|
|
|
|
var alertsResp = await _client.GetAsync($"/api/v1/encounters/{_encounterId}/alerts");
|
|
var alertId = (await alertsResp.Content.ReadFromJsonAsync<JsonDocument>())!
|
|
.RootElement.GetProperty("data").GetProperty("items")[0].GetProperty("id").GetGuid();
|
|
|
|
(await _client.PostAsJsonAsync(
|
|
$"/api/v1/alerts/{alertId}/acknowledge",
|
|
new AcknowledgeAlertRequest("Partition ack"))).StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
(await _client.PostAsync($"/api/v1/alerts/{alertId}/resolve", null))
|
|
.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
using var scope = _fixture.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<GatewayDbContext>();
|
|
var alert = await db.ClinicalAlerts.FindAsync(alertId);
|
|
alert!.Status.Should().Be(AlertStatus.Resolved);
|
|
(await db.BufferedSyncItems.CountAsync(b => !b.Synced)).Should().BeGreaterThanOrEqualTo(4);
|
|
}
|
|
}
|