build-flow.api/tests/BuildFlow.Scripting.Tests/ScriptCommandEdgeHandlerTests.cs
xiangning 7479521546 feat(rag): 实现RAG模块的Hybrid检索和向量索引优化
- 添加HNSW向量索引提升检索性能
- 实现Hybrid检索模式支持向量和关键词双路召回
- 采用RRF算法融合多路召回结果
- 支持ParentChild分块模式的上下文回填
- 优化向量写入采用批量SQL操作提升性能
- 添加RAG PostgreSQL连接字符串配置
- 更新部署脚本增加PGVector集成测试
- 修正表名引用从document_chunks到b_document_chunks
- 重构RagRetrievalService实现完整的检索流程
- 增加重排策略支持关键词和LLM重排
- 修改服务器部署地址配置
2026-08-11 10:59:09 +08:00

115 lines
4.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using BuildFlow.Shared.Domain.Errors;
using BuildFlow.Shared.Domain.Exceptions;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using Scripting.Application.Commands;
using Scripting.Application.DTOs;
using Scripting.Application.Handlers;
using Scripting.Application.Services;
using Scripting.Domain.Entities.ScriptFunction;
using Scripting.Domain.Enums;
using Scripting.Infrastructure.Persistence;
using Xunit;
namespace BuildFlow.Scripting.Tests;
/// <summary>
/// scripting 模块命令处理器测试(架构护栏:每个 Handler 至少 1 个测试)。
/// DisableScriptCommandHandler:真实 InMemory 覆盖成功置 Disabled 与不存在→NotFound。
/// TestScriptCommandHandler / InvokeScriptCommandHandler:仅委托 IScriptExecutionService,
/// 不触发真实 Roslyn 编译执行(依赖编译/沙箱执行,需集成环境),故用 NSubstitute 桩验证委托
/// 把请求正确转发给 ExecuteTestAsync / ExecuteByNameAsync 并返回其结果。
/// </summary>
public class ScriptCommandEdgeHandlerTests
{
// ── DisableScript ──────────────────────────────────────────────────────
[Fact]
public async Task DisableScript_Published_SetsDisabledAndPersists()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedScriptAsync(db, "disable-fn", ScriptStatusEnum.Published);
var handler = new DisableScriptCommandHandler(db);
var dto = await handler.Handle(new DisableScriptCommand(script.Id), CancellationToken.None);
dto.Status.Should().Be(ScriptStatusEnum.Disabled);
var entity = await db.Scripts.FindAsync(script.Id);
entity.Should().NotBeNull();
entity!.Status.Should().Be(ScriptStatusEnum.Disabled);
}
[Fact]
public async Task DisableScript_NotFound_Throws()
{
await using var db = ScriptingTestDbFactory.Create();
var handler = new DisableScriptCommandHandler(db);
var act = async () => await handler.Handle(
new DisableScriptCommand(Guid.NewGuid()), CancellationToken.None);
var ex = await act.Should().ThrowAsync<BusinessException>();
ex.Which.Code.Should().Be(ErrorCode.NotFound);
}
// ── TestScript(委托执行服务,前置分支无校验,仅验证委托) ───────────────
[Fact]
public async Task TestScript_DelegatesToService_ReturnsResult()
{
var svc = Substitute.For<IScriptExecutionService>();
var expected = new ScriptExecutionResult(ScriptExecutionStatusEnum.Success, "42", null, 7);
var req = new TestScriptCommand(Guid.NewGuid(), "{\"x\":1}");
svc.ExecuteTestAsync(req.Id, req.InputJson, Arg.Any<CancellationToken>()).Returns(expected);
var handler = new TestScriptCommandHandler(svc);
var result = await handler.Handle(req, CancellationToken.None);
result.Should().Be(expected);
await svc.Received(1).ExecuteTestAsync(req.Id, req.InputJson, Arg.Any<CancellationToken>());
}
// ── InvokeScript(委托执行服务,仅 Published 可调用由服务守卫) ──────────
[Fact]
public async Task InvokeScript_DelegatesToService_ReturnsResult()
{
var svc = Substitute.For<IScriptExecutionService>();
var expected = new ScriptExecutionResult(ScriptExecutionStatusEnum.Success, "99", null, 3);
var req = new InvokeScriptCommand("my-script", "{}");
svc.ExecuteByNameAsync(req.Name, req.InputJson, Arg.Any<CancellationToken>()).Returns(expected);
var handler = new InvokeScriptCommandHandler(svc);
var result = await handler.Handle(req, CancellationToken.None);
result.Should().Be(expected);
await svc.Received(1).ExecuteByNameAsync(req.Name, req.InputJson, Arg.Any<CancellationToken>());
}
// ── 夹具 ───────────────────────────────────────────────────────────────
private static async Task<ScriptFunctionEntity> SeedScriptAsync(
ScriptingDbContext db,
string name,
ScriptStatusEnum status = ScriptStatusEnum.Draft)
{
var script = new ScriptFunctionEntity
{
Id = Guid.NewGuid(),
Name = name,
DisplayName = name,
Code = "return 1;",
Status = status,
Version = 1,
TimeoutMs = 5000,
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
};
db.Scripts.Add(script);
await db.SaveChangesAsync();
return script;
}
}