- 添加HNSW向量索引提升检索性能 - 实现Hybrid检索模式支持向量和关键词双路召回 - 采用RRF算法融合多路召回结果 - 支持ParentChild分块模式的上下文回填 - 优化向量写入采用批量SQL操作提升性能 - 添加RAG PostgreSQL连接字符串配置 - 更新部署脚本增加PGVector集成测试 - 修正表名引用从document_chunks到b_document_chunks - 重构RagRetrievalService实现完整的检索流程 - 增加重排策略支持关键词和LLM重排 - 修改服务器部署地址配置
123 lines
4.5 KiB
C#
123 lines
4.5 KiB
C#
using BuildFlow.Application.Document.Commands;
|
||
using BuildFlow.Application.Document.DTOs;
|
||
using BuildFlow.Application.Document.Handlers;
|
||
using BuildFlow.Domain.Entities.Document;
|
||
using BuildFlow.Domain.Entities.KnowledgeBase;
|
||
using BuildFlow.Domain.Enums;
|
||
using BuildFlow.Infrastructure.Persistence;
|
||
using BuildFlow.Shared.Domain.Errors;
|
||
using BuildFlow.Shared.Domain.Exceptions;
|
||
using FluentAssertions;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using MediatR;
|
||
using Moq;
|
||
using Xunit;
|
||
|
||
namespace BuildFlow.Rag.Tests;
|
||
|
||
/// <summary>
|
||
/// 处理知识库 Command 处理器测试。
|
||
/// 成功路径用 Mock<IMediator> 模拟逐文档 ProcessDocumentCommand;异常分支覆盖知识库不存在→NotFound(依赖调用之前)。
|
||
/// </summary>
|
||
public class ProcessKnowledgeBaseCommandHandlerTests
|
||
{
|
||
[Fact]
|
||
public async Task Handle_KnowledgeBaseNotFound_ThrowsNotFound()
|
||
{
|
||
await using var db = CreateDb();
|
||
var mediator = new Mock<IMediator>();
|
||
var handler = new ProcessKnowledgeBaseCommandHandler(db, mediator.Object);
|
||
|
||
var act = async () => await handler.Handle(
|
||
new ProcessKnowledgeBaseCommand(Guid.NewGuid()), CancellationToken.None);
|
||
var ex = await act.Should().ThrowAsync<NotFoundException>();
|
||
ex.Which.Code.Should().Be(ErrorCode.NotFound);
|
||
mediator.Verify(m => m.Send(It.IsAny<ProcessDocumentCommand>(), It.IsAny<CancellationToken>()), Times.Never);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Handle_ProcessesPendingAndFailedDocumentsOnly()
|
||
{
|
||
await using var db = CreateDb();
|
||
var kbId = Guid.NewGuid();
|
||
var pendingId = Guid.NewGuid();
|
||
var failedId = Guid.NewGuid();
|
||
var completedId = Guid.NewGuid();
|
||
db.KnowledgeBases.Add(new KnowledgeBaseEntity
|
||
{
|
||
Id = kbId,
|
||
Name = "测试知识库",
|
||
Status = KnowledgeBaseStatusEnum.Active,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
db.Documents.Add(new DocumentEntity
|
||
{
|
||
Id = pendingId,
|
||
KnowledgeBaseId = kbId,
|
||
Title = "pending.md",
|
||
FileName = "pending.md",
|
||
FilePath = "pending.md",
|
||
FileSize = 1,
|
||
ContentType = "text/markdown",
|
||
Status = DocumentStatusEnum.Pending,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
db.Documents.Add(new DocumentEntity
|
||
{
|
||
Id = failedId,
|
||
KnowledgeBaseId = kbId,
|
||
Title = "failed.md",
|
||
FileName = "failed.md",
|
||
FilePath = "failed.md",
|
||
FileSize = 1,
|
||
ContentType = "text/markdown",
|
||
Status = DocumentStatusEnum.Failed,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
db.Documents.Add(new DocumentEntity
|
||
{
|
||
Id = completedId,
|
||
KnowledgeBaseId = kbId,
|
||
Title = "completed.md",
|
||
FileName = "completed.md",
|
||
FilePath = "completed.md",
|
||
FileSize = 1,
|
||
ContentType = "text/markdown",
|
||
Status = DocumentStatusEnum.Completed,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
await db.SaveChangesAsync();
|
||
|
||
var mediator = new Mock<IMediator>();
|
||
mediator.Setup(m => m.Send(It.IsAny<ProcessDocumentCommand>(), It.IsAny<CancellationToken>()))
|
||
.ReturnsAsync(new ProcessDocumentResponse(Guid.NewGuid(), 1, "Processed"));
|
||
var handler = new ProcessKnowledgeBaseCommandHandler(db, mediator.Object);
|
||
|
||
var response = await handler.Handle(new ProcessKnowledgeBaseCommand(kbId), CancellationToken.None);
|
||
|
||
response.KnowledgeBaseId.Should().Be(kbId);
|
||
response.Total.Should().Be(2, "仅 Pending + Failed 被纳入批处理");
|
||
response.Succeeded.Should().Be(2);
|
||
response.Failed.Should().Be(0);
|
||
mediator.Verify(m => m.Send(It.IsAny<ProcessDocumentCommand>(), It.IsAny<CancellationToken>()), Times.Exactly(2));
|
||
}
|
||
|
||
private static RagDbContext CreateDb()
|
||
{
|
||
var options = new DbContextOptionsBuilder<RagDbContext>()
|
||
.UseInMemoryDatabase($"rag-process-kb-{Guid.NewGuid():N}")
|
||
.Options;
|
||
var db = new RagDbContext(options);
|
||
db.SavingChanges += (_, _) => RagTestDb.FillAudit(db);
|
||
return db;
|
||
}
|
||
}
|