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;
///
/// 处理知识库 Command 处理器测试。
/// 成功路径用 Mock<IMediator> 模拟逐文档 ProcessDocumentCommand;异常分支覆盖知识库不存在→NotFound(依赖调用之前)。
///
public class ProcessKnowledgeBaseCommandHandlerTests
{
[Fact]
public async Task Handle_KnowledgeBaseNotFound_ThrowsNotFound()
{
await using var db = CreateDb();
var mediator = new Mock();
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();
ex.Which.Code.Should().Be(ErrorCode.NotFound);
mediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), 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();
mediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
.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(), It.IsAny()), Times.Exactly(2));
}
private static RagDbContext CreateDb()
{
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase($"rag-process-kb-{Guid.NewGuid():N}")
.Options;
var db = new RagDbContext(options);
db.SavingChanges += (_, _) => RagTestDb.FillAudit(db);
return db;
}
}