xiangning 51eea796e2 refactor(rag): 重构AI模型相关代码结构和数据库迁移逻辑
- 将AI模型相关端点、命令、处理器移动到模块内部统一管理
- 优化数据库迁移脚本,添加对不同环境表状态的兼容处理逻辑
- 为异常处理添加错误码枚举参数,统一错误处理方式
- 重构多处业务逻辑中的异常抛出方式,增强代码一致性
- 移除独立的组件清单分组和项目定义,简化前端物料面板数据结构
- 修复测试代码中实体引用路径错误问题
- 添加新的监控模块测试项目到解决方案文件中
2026-07-20 08:25:18 +08:00

109 lines
3.8 KiB
C#

using System.Net;
using System.Text;
using BuildFlow.Shared.Domain.Exceptions;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Enums;
using Monitoring.Domain.Values;
using Monitoring.Infrastructure.Connectors;
namespace BuildFlow.Monitoring.Tests.Connectors;
/// <summary>u号租订单同步日志安全测试。</summary>
public class UHaoZuConnectorLoggingTests
{
[Fact]
public async Task FetchAsync_ShouldNotLogOrderResponseBody()
{
const string sensitiveOrderId = "secret-order-123";
var handler = new SequenceHttpMessageHandler(
"""{"responseCode":"0000","object":{}}""",
$$"""{"responseCode":"0000","object":[{"id":"{{sensitiveOrderId}}","createTime":1721300000000}]}""");
var resolver = new ConnectorCredentialResolver(new StubHttpClientFactory(handler));
var logger = new CapturingLogger<UHaoZuConnector>();
var connector = new UHaoZuConnector(resolver, logger);
var system = new ExternalSystemEntity
{
Data = new SystemData
{
Name = "u号租",
SystemType = ExternalSystemTypeEnum.UHaoZu,
BaseUrl = "https://mapi.uhaozu.com",
Parameters = new Dictionary<string, string?> { ["maxPages"] = "1" },
},
};
await connector.FetchAsync(system, default);
logger.Messages.Should().NotContain(message => message.Contains(sensitiveOrderId));
}
[Fact]
public async Task FetchAsync_ShouldFailWhenOrderApiRejectsSession()
{
var handler = new SequenceHttpMessageHandler(
"""{"responseCode":"0000","object":{}}""",
"""{"responseCode":"9996","responseMsg":"not logged in"}""");
var resolver = new ConnectorCredentialResolver(new StubHttpClientFactory(handler));
var connector = new UHaoZuConnector(resolver, new CapturingLogger<UHaoZuConnector>());
var system = CreateSystem();
var action = () => connector.FetchAsync(system, default);
await action.Should().ThrowAsync<BusinessException>()
.WithMessage("*not logged in*");
}
private static ExternalSystemEntity CreateSystem() => new()
{
Data = new SystemData
{
Name = "u号租",
SystemType = ExternalSystemTypeEnum.UHaoZu,
BaseUrl = "https://mapi.uhaozu.com",
Parameters = new Dictionary<string, string?> { ["maxPages"] = "1" },
},
};
private sealed class StubHttpClientFactory(HttpMessageHandler handler) : IHttpClientFactory
{
public HttpClient CreateClient(string name) => new(handler, disposeHandler: false);
}
private sealed class SequenceHttpMessageHandler(params string[] responses) : HttpMessageHandler
{
private readonly Queue<string> _responses = new(responses);
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var body = _responses.Dequeue();
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(body, Encoding.UTF8, "application/json"),
});
}
}
private sealed class CapturingLogger<T> : ILogger<T>
{
public List<string> Messages { get; } = [];
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
Messages.Add(formatter(state, exception));
}
}
}