- 添加HNSW向量索引提升检索性能 - 实现Hybrid检索模式支持向量和关键词双路召回 - 采用RRF算法融合多路召回结果 - 支持ParentChild分块模式的上下文回填 - 优化向量写入采用批量SQL操作提升性能 - 添加RAG PostgreSQL连接字符串配置 - 更新部署脚本增加PGVector集成测试 - 修正表名引用从document_chunks到b_document_chunks - 重构RagRetrievalService实现完整的检索流程 - 增加重排策略支持关键词和LLM重排 - 修改服务器部署地址配置
179 lines
7.0 KiB
C#
179 lines
7.0 KiB
C#
using BuildFlow.Shared.Domain.Errors;
|
||
using BuildFlow.Shared.Domain.Exceptions;
|
||
using FluentAssertions;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Monitoring.Application.ExternalSystems.Commands;
|
||
using Monitoring.Application.ExternalSystems.DTOs;
|
||
using Monitoring.Application.ExternalSystems.Handlers;
|
||
using Monitoring.Application.ExternalSystems.Queries;
|
||
using Monitoring.Infrastructure.Persistence;
|
||
|
||
namespace BuildFlow.Monitoring.Tests;
|
||
|
||
/// <summary>
|
||
/// [SOP-MONITORING] ExternalSystems 子域 Handler 全覆盖测试。
|
||
/// 覆盖: Create / List / GetDetail / Update(含无效类型、缺失、非法 JSON、凭证覆盖分支)。
|
||
/// 约定: 直接用 new Handler(db) 调 Handle;db 由 MonitoringTestDb.Create 提供(自动补审计字段)。
|
||
/// 用 Create handler 串联构造前置数据,避免直接依赖实体内部构造。
|
||
/// </summary>
|
||
public class ExternalSystemsHandlerTests
|
||
{
|
||
private static MonitoringDbContext Db([System.Runtime.CompilerServices.CallerMemberName] string name = "")
|
||
=> MonitoringTestDb.Create(name);
|
||
|
||
#region Create
|
||
|
||
[Fact]
|
||
public async Task Create_Valid_ReturnsDtoAndPersists()
|
||
{
|
||
await using var db = Db();
|
||
var result = await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("u号租", 1, "https://api.example.com", 300, null),
|
||
CancellationToken.None);
|
||
|
||
result.Should().NotBeNull();
|
||
result.Name.Should().Be("u号租");
|
||
result.SystemType.Should().Be(1);
|
||
result.BaseUrl.Should().Be("https://api.example.com");
|
||
result.IsEnabled.Should().BeTrue();
|
||
result.PollIntervalSeconds.Should().Be(300);
|
||
(await db.ExternalSystems.CountAsync()).Should().Be(1);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Create_InvalidSystemType_ThrowsBusiness()
|
||
{
|
||
await using var db = Db();
|
||
var act = async () => await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("x", 999, "https://x", 0, null),
|
||
CancellationToken.None);
|
||
var ex = await act.Should().ThrowAsync<BusinessException>();
|
||
ex.Which.Code.Should().Be(ErrorCode.InvalidParam);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region List
|
||
|
||
[Fact]
|
||
public async Task List_Empty_ReturnsEmpty()
|
||
{
|
||
await using var db = Db();
|
||
var result = await new ListExternalSystemsQueryHandler(db)
|
||
.Handle(new ListExternalSystemsQuery(), CancellationToken.None);
|
||
result.Should().BeEmpty();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task List_ReturnsAllDescendingByCreatedAt()
|
||
{
|
||
await using var db = Db();
|
||
var create = new CreateExternalSystemCommandHandler(db);
|
||
var a = await create.Handle(new CreateExternalSystemCommand("A", 1, "https://a", 1, null), CancellationToken.None);
|
||
var b = await create.Handle(new CreateExternalSystemCommand("B", 1, "https://b", 1, null), CancellationToken.None);
|
||
|
||
// 强制 A 更早创建,使倒序断言稳定
|
||
var entA = await db.ExternalSystems.FirstAsync(x => x.Id == a.Id);
|
||
entA.CreatedAt = DateTime.UtcNow.AddMinutes(-10);
|
||
await db.SaveChangesAsync();
|
||
|
||
var list = await new ListExternalSystemsQueryHandler(db)
|
||
.Handle(new ListExternalSystemsQuery(), CancellationToken.None);
|
||
list.Should().HaveCount(2);
|
||
list[0].Name.Should().Be("B");
|
||
list[1].Name.Should().Be("A");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region GetDetail
|
||
|
||
[Fact]
|
||
public async Task GetDetail_Existing_ReturnsDetail()
|
||
{
|
||
await using var db = Db();
|
||
var created = await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("u号租", 1, "https://x", 60, null),
|
||
CancellationToken.None);
|
||
|
||
var detail = await new GetExternalSystemDetailQueryHandler(db)
|
||
.Handle(new GetExternalSystemDetailQuery(created.Id), CancellationToken.None);
|
||
detail.Should().NotBeNull();
|
||
detail.Id.Should().Be(created.Id);
|
||
detail.Name.Should().Be("u号租");
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetDetail_NotFound_Throws()
|
||
{
|
||
await using var db = Db();
|
||
var act = async () => await new GetExternalSystemDetailQueryHandler(db)
|
||
.Handle(new GetExternalSystemDetailQuery(Guid.NewGuid()), CancellationToken.None);
|
||
var ex = await act.Should().ThrowAsync<NotFoundException>();
|
||
ex.Which.Code.Should().Be(ErrorCode.NotFound);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Update
|
||
|
||
[Fact]
|
||
public async Task Update_NameAndBaseUrl_Overwrites()
|
||
{
|
||
await using var db = Db();
|
||
var created = await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("old", 1, "https://old", 60, null),
|
||
CancellationToken.None);
|
||
var result = await new UpdateExternalSystemCommandHandler(db).Handle(
|
||
new UpdateExternalSystemCommand(created.Id, "new", "https://new", null, null, null, null),
|
||
CancellationToken.None);
|
||
result.Name.Should().Be("new");
|
||
result.BaseUrl.Should().Be("https://new");
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Update_NonExistent_ThrowsNotFound()
|
||
{
|
||
await using var db = Db();
|
||
var act = async () => await new UpdateExternalSystemCommandHandler(db).Handle(
|
||
new UpdateExternalSystemCommand(Guid.NewGuid(), "x", null, null, null, null, null),
|
||
CancellationToken.None);
|
||
var ex = await act.Should().ThrowAsync<NotFoundException>();
|
||
ex.Which.Code.Should().Be(ErrorCode.NotFound);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Update_InvalidParametersJson_ThrowsBusiness()
|
||
{
|
||
await using var db = Db();
|
||
var created = await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("x", 1, "https://x", 60, null),
|
||
CancellationToken.None);
|
||
var act = async () => await new UpdateExternalSystemCommandHandler(db).Handle(
|
||
new UpdateExternalSystemCommand(created.Id, null, null, null, null, "{bad json", null),
|
||
CancellationToken.None);
|
||
var ex = await act.Should().ThrowAsync<BusinessException>();
|
||
ex.Which.Code.Should().Be(ErrorCode.InvalidParam);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Update_Credentials_Overwrites()
|
||
{
|
||
await using var db = Db();
|
||
var created = await new CreateExternalSystemCommandHandler(db).Handle(
|
||
new CreateExternalSystemCommand("x", 1, "https://x", 60, null),
|
||
CancellationToken.None);
|
||
var creds = new List<CredentialInput> { new("token", "ak", "av", true, null) };
|
||
await new UpdateExternalSystemCommandHandler(db).Handle(
|
||
new UpdateExternalSystemCommand(created.Id, null, null, null, null, null, creds),
|
||
CancellationToken.None);
|
||
|
||
var detail = await new GetExternalSystemDetailQueryHandler(db)
|
||
.Handle(new GetExternalSystemDetailQuery(created.Id), CancellationToken.None);
|
||
detail.Credentials.Should().HaveCount(1);
|
||
detail.Credentials[0].Key.Should().Be("ak");
|
||
}
|
||
|
||
#endregion
|
||
}
|