- 从文件模块的多部分上传处理器中移除未使用的实体引用 - 从即时通讯模块的群组成员处理器中移除未使用的实体引用 - 从RAG模块的AI模型配置中移除未使用的实体引用 - 从监控模块的告警规则配置中移除未使用的实体引用 - 从平台模块的角色权限处理器中移除未使用的实体引用 - 从脚本模块的执行日志实体引用中进行清理 - 从多个基础设施配置类中移除未使用的依赖项 - 优化测试文件中的导入声明,移除不必要的引用
117 lines
4.3 KiB
C#
117 lines
4.3 KiB
C#
using BuildFlow.Application.Document.Handlers;
|
||
using BuildFlow.Application.Document.Queries;
|
||
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 Microsoft.AspNetCore.Http;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Xunit;
|
||
|
||
namespace BuildFlow.Rag.Tests;
|
||
|
||
/// <summary>
|
||
/// 文档切片预览测试。预览只提取并分块,不生成 embedding,也不写入正式分块表。
|
||
/// </summary>
|
||
public class PreviewDocumentChunksQueryHandlerTests
|
||
{
|
||
[Fact]
|
||
public async Task HandleShouldReturnDryRunChunksWithoutPersistingDocumentChunks()
|
||
{
|
||
var options = new DbContextOptionsBuilder<RagDbContext>()
|
||
.UseInMemoryDatabase($"rag-preview-{Guid.NewGuid():N}")
|
||
.Options;
|
||
await using var db = new RagDbContext(options);
|
||
var kbId = Guid.NewGuid();
|
||
var docId = Guid.NewGuid();
|
||
var filePath = Path.Combine(Path.GetTempPath(), $"preview-{Guid.NewGuid():N}.md");
|
||
await File.WriteAllTextAsync(filePath, "# 第一章\n\nBuildFlow 文档切片预览。\n\n# 第二章\n\n上传后先看切片,再手动处理向量。");
|
||
|
||
try
|
||
{
|
||
await db.KnowledgeBases.AddAsync(new KnowledgeBaseEntity
|
||
{
|
||
Id = kbId,
|
||
Name = "预览知识库",
|
||
ChunkSize = 40,
|
||
ChunkOverlap = 5,
|
||
ChunkingMode = ChunkingModeEnum.Heading,
|
||
EmbeddingModel = "text-embedding-embeddinggemma-300m",
|
||
Status = KnowledgeBaseStatusEnum.Active,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
await db.Documents.AddAsync(new DocumentEntity
|
||
{
|
||
Id = docId,
|
||
KnowledgeBaseId = kbId,
|
||
Title = "preview.md",
|
||
FileName = "preview.md",
|
||
FilePath = filePath,
|
||
FileSize = 64,
|
||
ContentType = "text/markdown",
|
||
Status = DocumentStatusEnum.Pending,
|
||
CreatedBy = "test",
|
||
UpdatedBy = "test",
|
||
OperatorIP = "127.0.0.1",
|
||
});
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new PreviewDocumentChunksQueryHandler(
|
||
db,
|
||
new StubDocumentExtractorFactory(),
|
||
new TextChunker(),
|
||
new StubFileStorageClient(),
|
||
new HttpContextAccessor());
|
||
|
||
var result = await handler.Handle(new PreviewDocumentChunksQuery(docId), CancellationToken.None);
|
||
|
||
Assert.Equal(2, result.Count);
|
||
Assert.All(result, chunk => Assert.False(chunk.HasEmbedding));
|
||
Assert.Contains("第一章", result[0].Content);
|
||
Assert.Contains("第二章", result[1].Content);
|
||
Assert.Equal(0, await db.DocumentChunks.CountAsync());
|
||
}
|
||
finally
|
||
{
|
||
File.Delete(filePath);
|
||
}
|
||
}
|
||
|
||
private sealed class StubDocumentExtractorFactory : IDocumentExtractorFactory
|
||
{
|
||
public IDocumentExtractor GetExtractor(string fileNameOrExtension) => new StubDocumentExtractor();
|
||
}
|
||
|
||
private sealed class StubDocumentExtractor : IDocumentExtractor
|
||
{
|
||
public IReadOnlyCollection<string> SupportedExtensions { get; init; } = ["md"];
|
||
|
||
public async Task<ExtractedDocument> ExtractAsync(string filePath, CancellationToken ct = default)
|
||
{
|
||
return new ExtractedDocument
|
||
{
|
||
Content = await File.ReadAllTextAsync(filePath, ct),
|
||
Title = Path.GetFileName(filePath),
|
||
};
|
||
}
|
||
}
|
||
|
||
private sealed class StubFileStorageClient : IFileStorageClient
|
||
{
|
||
public Task UploadAsync(
|
||
string bucket,
|
||
string objectKey,
|
||
Stream content,
|
||
string contentType,
|
||
string? authToken,
|
||
CancellationToken ct = default) => Task.CompletedTask;
|
||
|
||
public Task<Stream> DownloadAsync(string bucket, string objectKey, string? authToken, CancellationToken ct = default)
|
||
=> throw new InvalidOperationException("本用例不应下载文件服务对象");
|
||
}
|
||
}
|