build-flow.api/tests/BuildFlow.Rag.Tests/ProcessDocumentCommandHandlerTests.cs
2026-07-24 12:29:06 +08:00

100 lines
3.7 KiB
C#

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;
/// <summary>
/// 文档处理测试。远端对象必须通过 File Application 下载,并在提取后释放响应流。
/// </summary>
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<IMediator>();
mediator.Setup(m => m.Send(
It.Is<DownloadFileQuery>(query => query.ObjectKey == "docs/kb/document.md"),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new DownloadFileResult(stream, "text/markdown", "document.md"));
var handler = new ProcessDocumentCommandHandler(
db,
new EmptyDocumentExtractorFactory(),
new TextChunker(),
new StubEmbeddingService(),
mediator.Object);
await Assert.ThrowsAsync<BusinessException>(() => handler.Handle(
new ProcessDocumentCommand(documentId), CancellationToken.None));
Assert.True(stream.IsDisposed);
mediator.Verify(m => m.Send(
It.Is<DownloadFileQuery>(query => query.ObjectKey == "docs/kb/document.md"),
It.IsAny<CancellationToken>()), Times.Once);
}
private static RagDbContext CreateDb() => new(
new DbContextOptionsBuilder<RagDbContext>()
.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<string> SupportedExtensions { get; init; } = ["md"];
public Task<ExtractedDocument> ExtractAsync(string filePath, CancellationToken ct = default) =>
Task.FromResult(new ExtractedDocument { Content = " " });
}
}