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

138 lines
4.8 KiB
C#

using BuildFlow.Platform.Application.Roles.Commands;
using BuildFlow.Platform.Application.Roles.Handlers;
using BuildFlow.Platform.Application.Roles.Queries;
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.Roles;
/// <summary>
/// [SOP-PLATFORM] Roles 模块 5 个 Handler 覆盖测试。
/// 覆盖: Create / GetById / GetList / Update / Delete。
/// 飞书矩阵: platform/Roles 子域(5 Handler 零覆盖→全补)。
/// </summary>
public class RolesHandlerTests
{
private static PlatformDbContext CreateDb([System.Runtime.CompilerServices.CallerMemberName] string name = "")
=> PlatformTestDb.CreateDbContext(name);
#region CreateRole
[Fact]
public async Task CreateRole_Valid_ReturnsDto()
{
var db = CreateDb();
var result = await new CreateRoleCommandHandler(db)
.Handle(new CreateRoleCommand("Admin", "管理员"), CancellationToken.None);
result.Name.Should().Be("Admin");
(await db.Roles.CountAsync()).Should().Be(1);
}
[Fact]
public async Task CreateRole_DuplicateName_ThrowsBusiness()
{
var db = CreateDb();
await new CreateRoleCommandHandler(db).Handle(new CreateRoleCommand("Admin", null), CancellationToken.None);
var act = async () => await new CreateRoleCommandHandler(db)
.Handle(new CreateRoleCommand("Admin", null), CancellationToken.None);
var ex = await act.Should().ThrowAsync<BusinessException>();
ex.Which.Code.Should().Be(ErrorCode.AlreadyExists);
}
#endregion
#region GetRoleById
[Fact]
public async Task GetRoleById_Existing_ReturnsDto()
{
var db = CreateDb();
var role = await new CreateRoleCommandHandler(db)
.Handle(new CreateRoleCommand("Manager", "经理"), CancellationToken.None);
var result = await new GetRoleByIdQueryHandler(db)
.Handle(new GetRoleByIdQuery(role.Id), CancellationToken.None);
result.Name.Should().Be("Manager");
}
[Fact]
public async Task GetRoleById_NonExistent_ThrowsNotFound()
{
var db = CreateDb();
var act = async () => await new GetRoleByIdQueryHandler(db)
.Handle(new GetRoleByIdQuery(Guid.NewGuid()), CancellationToken.None);
var ex = await act.Should().ThrowAsync<NotFoundException>();
ex.Which.Code.Should().Be(ErrorCode.NotFound);
}
#endregion
#region GetRoleList
[Fact]
public async Task GetRoleList_ReturnsAll()
{
var db = CreateDb();
await new CreateRoleCommandHandler(db).Handle(new CreateRoleCommand("R1", null), CancellationToken.None);
await new CreateRoleCommandHandler(db).Handle(new CreateRoleCommand("R2", null), CancellationToken.None);
var result = await new GetRoleListQueryHandler(db)
.Handle(new GetRoleListQuery(), CancellationToken.None);
result.Should().HaveCount(2);
}
#endregion
#region UpdateRole
[Fact]
public async Task UpdateRole_Existing_UpdatesFields()
{
var db = CreateDb();
var role = await new CreateRoleCommandHandler(db)
.Handle(new CreateRoleCommand("Old", "旧"), CancellationToken.None);
var result = await new UpdateRoleCommandHandler(db)
.Handle(new UpdateRoleCommand(role.Id, "New", "新"), CancellationToken.None);
result.Name.Should().Be("New");
result.Description.Should().Be("新");
}
[Fact]
public async Task UpdateRole_NonExistent_ThrowsNotFound()
{
var db = CreateDb();
var act = async () => await new UpdateRoleCommandHandler(db)
.Handle(new UpdateRoleCommand(Guid.NewGuid(), "X", null), CancellationToken.None);
var ex = await act.Should().ThrowAsync<NotFoundException>();
ex.Which.Code.Should().Be(ErrorCode.NotFound);
}
#endregion
#region DeleteRole
[Fact]
public async Task DeleteRole_Existing_Removes()
{
var db = CreateDb();
var role = await new CreateRoleCommandHandler(db)
.Handle(new CreateRoleCommand("Tmp", null), CancellationToken.None);
await new DeleteRoleCommandHandler(db).Handle(new DeleteRoleCommand(role.Id), CancellationToken.None);
(await db.Roles.CountAsync()).Should().Be(0);
}
[Fact]
public async Task DeleteRole_NonExistent_ThrowsNotFound()
{
var db = CreateDb();
var act = async () => await new DeleteRoleCommandHandler(db)
.Handle(new DeleteRoleCommand(Guid.NewGuid()), CancellationToken.None);
var ex = await act.Should().ThrowAsync<NotFoundException>();
ex.Which.Code.Should().Be(ErrorCode.NotFound);
}
#endregion
}