using BuildFlow.Platform.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Handlers; using BuildFlow.Platform.Application.Auth.Queries; using BuildFlow.Platform.Application.Permissions.Commands; using BuildFlow.Platform.Application.Permissions.Handlers; using BuildFlow.Platform.Application.Roles.Commands; using BuildFlow.Platform.Application.Roles.Handlers; using BuildFlow.Platform.Domain.Entities.RefreshToken; using BuildFlow.Platform.Domain.Entities.RolePermission; using BuildFlow.Platform.Domain.Entities.UserRole; using BuildFlow.Platform.Infrastructure.Persistence; using BuildFlow.Shared.Domain.Errors; using BuildFlow.Shared.Domain.Exceptions; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Xunit; namespace BuildFlow.Platform.Tests.Auth; /// /// [SOP-PLATFORM] Auth 模块 3 个 Handler 覆盖测试。 /// 覆盖: Logout / RevokeToken / GetAccessCodes。 /// 飞书矩阵: platform/Auth 子域(3 Handler 零覆盖→全补)。 /// public class LogoutRevokeAccessCodesHandlerTests { private static PlatformDbContext CreateDb([System.Runtime.CompilerServices.CallerMemberName] string name = "") => PlatformTestDb.CreateDbContext(name); private static async Task SeedRefreshToken(PlatformDbContext db, string token) { var user = await PlatformTestDb.SeedUserAsync(db, Guid.NewGuid(), $"u-{Guid.NewGuid():N}"); var rt = PlatformTestDb.WithAudit(new RefreshTokenEntity { UserId = user.Id, User = user, Token = token, ExpiresAt = DateTime.UtcNow.AddDays(1), }); db.RefreshTokens.Add(rt); await db.SaveChangesAsync(); } #region Logout [Fact] public async Task Logout_WithExistingToken_RevokesIt() { var db = CreateDb(); await SeedRefreshToken(db, "tok-logout"); await new LogoutCommandHandler(db).Handle(new LogoutCommand("tok-logout"), CancellationToken.None); (await db.RefreshTokens.FirstAsync(r => r.Token == "tok-logout")).IsRevoked.Should().BeTrue(); } [Fact] public async Task Logout_WithEmptyToken_NoOp() { var db = CreateDb(); var act = async () => await new LogoutCommandHandler(db).Handle(new LogoutCommand(""), CancellationToken.None); await act.Should().NotThrowAsync(); } [Fact] public async Task Logout_WithUnknownToken_NoOp() { var db = CreateDb(); var act = async () => await new LogoutCommandHandler(db).Handle(new LogoutCommand("ghost"), CancellationToken.None); await act.Should().NotThrowAsync(); } #endregion #region RevokeToken [Fact] public async Task Revoke_ExistingToken_RevokesIt() { var db = CreateDb(); await SeedRefreshToken(db, "tok-revoke"); await new RevokeTokenCommandHandler(db).Handle(new RevokeTokenCommand("tok-revoke"), CancellationToken.None); (await db.RefreshTokens.FirstAsync(r => r.Token == "tok-revoke")).IsRevoked.Should().BeTrue(); } [Fact] public async Task Revoke_UnknownToken_ThrowsUnauthorized() { var db = CreateDb(); var act = async () => await new RevokeTokenCommandHandler(db).Handle(new RevokeTokenCommand("ghost"), CancellationToken.None); var ex = await act.Should().ThrowAsync(); ex.Which.Code.Should().Be(ErrorCode.Unauthorized); } #endregion #region GetAccessCodes [Fact] public async Task GetAccessCodes_ExistingUserWithRole_ReturnsCodes() { var db = CreateDb(); var user = await PlatformTestDb.SeedUserAsync(db, Guid.NewGuid(), $"acc-{Guid.NewGuid():N}"); var perm = await new CreatePermissionCommandHandler(db) .Handle(new CreatePermissionCommand("读", "acc:read", null, null), CancellationToken.None); var role = await new CreateRoleCommandHandler(db) .Handle(new CreateRoleCommand("AccRole", null), CancellationToken.None); var roleEntity = await db.Roles.FirstAsync(r => r.Id == role.Id); var permEntity = await db.Permissions.FirstAsync(p => p.Id == perm.Id); db.Add(PlatformTestDb.WithAudit(new UserRoleEntity { UserId = user.Id, RoleId = role.Id, User = user, Role = roleEntity, })); db.Add(PlatformTestDb.WithAudit(new RolePermissionEntity { RoleId = role.Id, PermissionId = perm.Id, Role = roleEntity, Permission = permEntity, })); await db.SaveChangesAsync(); var result = await new GetAccessCodesQueryHandler(db) .Handle(new GetAccessCodesQuery(user.Id.ToString()), CancellationToken.None); result.Should().Contain("acc:read"); } [Fact] public async Task GetAccessCodes_UserWithoutRoles_ReturnsEmpty() { var db = CreateDb(); var user = await PlatformTestDb.SeedUserAsync(db, Guid.NewGuid(), $"acc2-{Guid.NewGuid():N}"); var result = await new GetAccessCodesQueryHandler(db) .Handle(new GetAccessCodesQuery(user.Id.ToString()), CancellationToken.None); result.Should().BeEmpty(); } [Fact] public async Task GetAccessCodes_UnknownUser_ThrowsUnauthorized() { var db = CreateDb(); var act = async () => await new GetAccessCodesQueryHandler(db) .Handle(new GetAccessCodesQuery(Guid.NewGuid().ToString()), CancellationToken.None); var ex = await act.Should().ThrowAsync(); ex.Which.Code.Should().Be(ErrorCode.Unauthorized); } #endregion }