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; } }