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 RbacTests : IAsyncLifetime { private readonly ApiFixture _fixture; private readonly HttpClient _client; public RbacTests(ApiFixture fixture) { _fixture = fixture; _client = fixture.CreateClient(); } public async Task InitializeAsync() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var redis = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); await DataSeeder.SeedThresholdsOnlyAsync(db, redis); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task Unauthenticated_PatientsList_Returns401() { _client.ClearAuth(); var resp = await _client.GetAsync("/api/v1/patients"); resp.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task Nurse_CannotUpdateThreshold_Returns403() { _client.ClearAuth(); _client.AsNurse(); var listResp = await _client.GetAsync("/api/v1/alert-thresholds"); listResp.EnsureSuccessStatusCode(); var thresholds = await listResp.Content.ReadFromJsonAsync(); var id = thresholds.GetProperty("data")[0].GetProperty("id").GetGuid(); var resp = await _client.PutAsJsonAsync($"/api/v1/alert-thresholds/{id}", new { observationCode = "HEART_RATE", displayName = "Heart Rate", unit = "/min", criticalLow = 40m, warningLow = 50m, warningHigh = 100m, criticalHigh = 130m }); resp.StatusCode.Should().Be(HttpStatusCode.Forbidden); } [Fact] public async Task Admin_CanUpdateThreshold_AndAuditLogCreated() { _client.ClearAuth(); _client.AsAdmin(); var listResp = await _client.GetAsync("/api/v1/alert-thresholds"); var thresholds = await listResp.Content.ReadFromJsonAsync(); var id = thresholds.GetProperty("data")[0].GetProperty("id").GetGuid(); var resp = await _client.PutAsJsonAsync($"/api/v1/alert-thresholds/{id}", new { observationCode = "HEART_RATE", displayName = "Heart Rate", unit = "/min", criticalLow = 40m, warningLow = 50m, warningHigh = 100m, criticalHigh = 130m }); resp.EnsureSuccessStatusCode(); var auditResp = await _client.GetAsync( $"/api/v1/audit-logs?entityType=AlertThreshold&entityId={id}"); auditResp.EnsureSuccessStatusCode(); var audit = await auditResp.Content.ReadFromJsonAsync(); audit.GetProperty("data").GetProperty("totalCount").GetInt32().Should().BeGreaterThan(0); } [Fact(Skip = "Not yet implemented")] public Task AlertAcknowledge_UsesAuthenticatedUser_NotBodyClinicianId() { _client.ClearAuth(); var nurseId = Guid.Parse("11111111-1111-1111-1111-111111111111"); _client.AsNurse(nurseId); // ... create patient, encounter, critical observation to generate alert ... // ... acknowledge with { "note": "reviewed" } only ... // Assert alert.AcknowledgedBy == "Test NURSE" (from TestingAuthHandler display_name) // Assert clinical_audit_logs row with action ALERT_ACKNOWLEDGED and userId == nurseId return Task.CompletedTask; } }