using BuildFlow.Application.Document.Commands; using BuildFlow.Application.Document.Handlers; using BuildFlow.Domain.Entities.Document; using BuildFlow.Domain.Entities.KnowledgeBase; using BuildFlow.Domain.Enums; using BuildFlow.Domain.Interfaces; using BuildFlow.Infrastructure.AI; using BuildFlow.Infrastructure.Persistence; using BuildFlow.Rag.Tests.Stub; using BuildFlow.Shared.Domain.Exceptions; using File.Application.Features.Files.DTOs; using File.Application.Features.Files.Queries; using MediatR; using Microsoft.EntityFrameworkCore; using Moq; using Xunit; namespace BuildFlow.Rag.Tests; /// /// 文档处理测试。远端对象必须通过 File Application 下载,并在提取后释放响应流。 /// public class ProcessDocumentCommandHandlerTests { [Fact] public async Task Handle_ShouldDownloadThroughFileQueryAndDisposeStream() { await using var db = CreateDb(); var knowledgeBaseId = Guid.NewGuid(); var documentId = Guid.NewGuid(); SeedRemoteDocument(db, knowledgeBaseId, documentId); await db.SaveChangesAsync(); var stream = new TrackingMemoryStream("empty"u8.ToArray()); var mediator = new Mock(); mediator.Setup(m => m.Send( It.Is(query => query.ObjectKey == "docs/kb/document.md"), It.IsAny())) .ReturnsAsync(new DownloadFileResult(stream, "text/markdown", "document.md")); var handler = new ProcessDocumentCommandHandler( db, new EmptyDocumentExtractorFactory(), new TextChunker(), new StubEmbeddingService(), mediator.Object); await Assert.ThrowsAsync(() => handler.Handle( new ProcessDocumentCommand(documentId), CancellationToken.None)); Assert.True(stream.IsDisposed); mediator.Verify(m => m.Send( It.Is(query => query.ObjectKey == "docs/kb/document.md"), It.IsAny()), Times.Once); } private static RagDbContext CreateDb() => new( new DbContextOptionsBuilder() .UseInMemoryDatabase($"rag-process-{Guid.NewGuid():N}") .Options); private static void SeedRemoteDocument(RagDbContext db, Guid knowledgeBaseId, Guid documentId) { db.KnowledgeBases.Add(new KnowledgeBaseEntity { Id = knowledgeBaseId, Name = "Process", Status = KnowledgeBaseStatusEnum.Active, CreatedBy = "test", UpdatedBy = "test", OperatorIP = "127.0.0.1", }); db.Documents.Add(new DocumentEntity { Id = documentId, KnowledgeBaseId = knowledgeBaseId, Title = "document.md", FileName = "document.md", FilePath = "rag-documents/docs/kb/document.md", FileSize = 5, ContentType = "text/markdown", Status = DocumentStatusEnum.Pending, CreatedBy = "test", UpdatedBy = "test", OperatorIP = "127.0.0.1", }); } private sealed class EmptyDocumentExtractorFactory : IDocumentExtractorFactory { public IDocumentExtractor GetExtractor(string fileNameOrExtension) => new EmptyDocumentExtractor(); } private sealed class EmptyDocumentExtractor : IDocumentExtractor { public IReadOnlyCollection SupportedExtensions { get; init; } = ["md"]; public Task ExtractAsync(string filePath, CancellationToken ct = default) => Task.FromResult(new ExtractedDocument { Content = " " }); } }