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(); } }