- 添加HNSW向量索引提升检索性能 - 实现Hybrid检索模式支持向量和关键词双路召回 - 采用RRF算法融合多路召回结果 - 支持ParentChild分块模式的上下文回填 - 优化向量写入采用批量SQL操作提升性能 - 添加RAG PostgreSQL连接字符串配置 - 更新部署脚本增加PGVector集成测试 - 修正表名引用从document_chunks到b_document_chunks - 重构RagRetrievalService实现完整的检索流程 - 增加重排策略支持关键词和LLM重排 - 修改服务器部署地址配置
151 lines
5.6 KiB
C#
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
|
|
}
|