build-flow.api/tests/BuildFlow.Platform.Tests/Auth/LogoutRevokeAccessCodesHandlerTests.cs
xiangning 7479521546 feat(rag): 实现RAG模块的Hybrid检索和向量索引优化
- 添加HNSW向量索引提升检索性能
- 实现Hybrid检索模式支持向量和关键词双路召回
- 采用RRF算法融合多路召回结果
- 支持ParentChild分块模式的上下文回填
- 优化向量写入采用批量SQL操作提升性能
- 添加RAG PostgreSQL连接字符串配置
- 更新部署脚本增加PGVector集成测试
- 修正表名引用从document_chunks到b_document_chunks
- 重构RagRetrievalService实现完整的检索流程
- 增加重排策略支持关键词和LLM重排
- 修改服务器部署地址配置
2026-08-11 10:59:09 +08:00

151 lines
5.6 KiB
C#

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;
/// <summary>
/// [SOP-PLATFORM] Auth 模块 3 个 Handler 覆盖测试。
/// 覆盖: Logout / RevokeToken / GetAccessCodes。
/// 飞书矩阵: platform/Auth 子域(3 Handler 零覆盖→全补)。
/// </summary>
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<UnauthorizedException>();
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<UnauthorizedException>();
ex.Which.Code.Should().Be(ErrorCode.Unauthorized);
}
#endregion
}