Files
vigilcare-records/VigilCareRecordsAPI.Tests/AuthTests.cs
T

152 lines
5.4 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Integration tests for authentication flow: login, refresh, logout, and role-based access.
/// </summary>
[Collection("Database")]
public class AuthTests : IAsyncLifetime
{
private readonly ApiFixture _fixture;
public AuthTests(ApiFixture fixture) => _fixture = fixture;
public async Task InitializeAsync()
{
using var scope = _fixture.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await DbResetHelper.ResetAsync(db);
await DataSeeder.SeedAsync(db);
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task Login_ValidCredentials_ReturnsTokenAndProfile()
{
var client = _fixture.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new { username = "entry1", password = "password" });
response.StatusCode.Should().Be(HttpStatusCode.OK);
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
var data = body.GetProperty("data");
data.GetProperty("token").GetString().Should().NotBeNullOrEmpty();
data.GetProperty("refreshToken").GetString().Should().NotBeNullOrEmpty();
data.GetProperty("username").GetString().Should().Be("entry1");
data.GetProperty("role").GetString().Should().Be("DATA_ENTRY_CLERK");
}
[Fact]
public async Task Login_InvalidPassword_Returns422()
{
var client = _fixture.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new { username = "entry1", password = "wrongpassword" });
response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
}
[Fact]
public async Task Login_NonexistentUser_Returns422()
{
var client = _fixture.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new { username = "nonexistent", password = "password" });
response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
}
[Fact]
public async Task ProtectedEndpoint_NoToken_Returns401()
{
var client = _fixture.CreateClient();
var response = await client.GetAsync("/api/v1/digitization-batches");
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
[Fact]
public async Task RoleRestricted_WrongRole_Returns403()
{
// entry1 is DATA_ENTRY_CLERK — cannot access admin dashboard
var client = await AuthHelper.LoginAsync(_fixture, "entry1");
var response = await client.GetAsync("/api/v1/work-queue/overview");
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
}
[Fact]
public async Task Refresh_ValidToken_ReturnsNewTokens()
{
var client = _fixture.CreateClient();
// Login to get refresh token
var loginResponse = await client.PostAsJsonAsync("/api/v1/auth/login",
new { username = "verifier1", password = "password" });
loginResponse.EnsureSuccessStatusCode();
var loginBody = await loginResponse.Content.ReadFromJsonAsync<JsonElement>();
var refreshToken = loginBody.GetProperty("data").GetProperty("refreshToken").GetString();
// Refresh
var refreshResponse = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new { refreshToken });
refreshResponse.StatusCode.Should().Be(HttpStatusCode.OK);
var refreshBody = await refreshResponse.Content.ReadFromJsonAsync<JsonElement>();
refreshBody.GetProperty("data").GetProperty("token").GetString().Should().NotBeNullOrEmpty();
refreshBody.GetProperty("data").GetProperty("refreshToken").GetString()
.Should().NotBe(refreshToken, "refresh should rotate the token");
}
[Fact]
public async Task Logout_RevokesRefreshToken()
{
var client = _fixture.CreateClient();
// Login
var loginResponse = await client.PostAsJsonAsync("/api/v1/auth/login",
new { username = "intake1", password = "password" });
loginResponse.EnsureSuccessStatusCode();
var loginBody = await loginResponse.Content.ReadFromJsonAsync<JsonElement>();
var refreshToken = loginBody.GetProperty("data").GetProperty("refreshToken").GetString();
// Logout
var logoutResponse = await client.PostAsJsonAsync("/api/v1/auth/logout",
new { refreshToken });
logoutResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
// Attempt to refresh with revoked token
var refreshResponse = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new { refreshToken });
refreshResponse.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
}
[Fact]
public async Task Me_Authenticated_ReturnsProfile()
{
var client = await AuthHelper.LoginAsync(_fixture, "approver1");
var response = await client.GetAsync("/api/v1/auth/me");
response.StatusCode.Should().Be(HttpStatusCode.OK);
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
var data = body.GetProperty("data");
data.GetProperty("username").GetString().Should().Be("approver1");
data.GetProperty("role").GetString().Should().Be("CLINICAL_APPROVER");
}
}