88 lines
2.6 KiB
C#
88 lines
2.6 KiB
C#
|
|
using System.Security.Claims;
|
||
|
|
using FluentAssertions;
|
||
|
|
using Microsoft.AspNetCore.Http;
|
||
|
|
using SIGCM2.Api.Middleware;
|
||
|
|
using Xunit;
|
||
|
|
|
||
|
|
namespace SIGCM2.Api.Tests.Audit;
|
||
|
|
|
||
|
|
/// UDT-010 Batch 4 — AuditActorMiddleware unit tests (Strict TDD).
|
||
|
|
/// Reads ActorUserId from the JWT "sub" claim after auth middleware populates HttpContext.User.
|
||
|
|
public sealed class AuditActorMiddlewareTests
|
||
|
|
{
|
||
|
|
[Fact]
|
||
|
|
public async Task Invoke_AuthenticatedUserWithSubClaim_SetsActorUserId()
|
||
|
|
{
|
||
|
|
var ctx = new DefaultHttpContext();
|
||
|
|
ctx.User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||
|
|
{
|
||
|
|
new Claim("sub", "42"),
|
||
|
|
new Claim("rol", "admin"),
|
||
|
|
}, authenticationType: "Bearer"));
|
||
|
|
|
||
|
|
var mw = new AuditActorMiddleware(_ => Task.CompletedTask);
|
||
|
|
await mw.InvokeAsync(ctx);
|
||
|
|
|
||
|
|
ctx.Items["audit:actorUserId"].Should().Be(42);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Invoke_AnonymousRequest_LeavesActorUserIdNull()
|
||
|
|
{
|
||
|
|
var ctx = new DefaultHttpContext();
|
||
|
|
// User is an unauthenticated ClaimsPrincipal by default
|
||
|
|
var mw = new AuditActorMiddleware(_ => Task.CompletedTask);
|
||
|
|
|
||
|
|
await mw.InvokeAsync(ctx);
|
||
|
|
|
||
|
|
ctx.Items.TryGetValue("audit:actorUserId", out var value).Should().BeFalse();
|
||
|
|
value.Should().BeNull();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Invoke_AuthenticatedWithoutSubClaim_LeavesActorUserIdNull()
|
||
|
|
{
|
||
|
|
var ctx = new DefaultHttpContext();
|
||
|
|
ctx.User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||
|
|
{
|
||
|
|
new Claim("name", "admin"),
|
||
|
|
}, authenticationType: "Bearer"));
|
||
|
|
|
||
|
|
var mw = new AuditActorMiddleware(_ => Task.CompletedTask);
|
||
|
|
await mw.InvokeAsync(ctx);
|
||
|
|
|
||
|
|
ctx.Items.TryGetValue("audit:actorUserId", out _).Should().BeFalse();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Invoke_SubClaimIsNonNumeric_LeavesActorUserIdNull()
|
||
|
|
{
|
||
|
|
var ctx = new DefaultHttpContext();
|
||
|
|
ctx.User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||
|
|
{
|
||
|
|
new Claim("sub", "not-an-int"),
|
||
|
|
}, authenticationType: "Bearer"));
|
||
|
|
|
||
|
|
var mw = new AuditActorMiddleware(_ => Task.CompletedTask);
|
||
|
|
await mw.InvokeAsync(ctx);
|
||
|
|
|
||
|
|
ctx.Items.TryGetValue("audit:actorUserId", out _).Should().BeFalse();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Invoke_CallsNextDelegate()
|
||
|
|
{
|
||
|
|
var ctx = new DefaultHttpContext();
|
||
|
|
var nextCalled = false;
|
||
|
|
var mw = new AuditActorMiddleware(_ =>
|
||
|
|
{
|
||
|
|
nextCalled = true;
|
||
|
|
return Task.CompletedTask;
|
||
|
|
});
|
||
|
|
|
||
|
|
await mw.InvokeAsync(ctx);
|
||
|
|
|
||
|
|
nextCalled.Should().BeTrue();
|
||
|
|
}
|
||
|
|
}
|