100 lines
3.7 KiB
C#
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 = " " });
|
|
}
|
|
}
|