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;
/// u号租订单同步日志安全测试。
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();
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 { ["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());
var system = CreateSystem();
var action = () => connector.FetchAsync(system, default);
await action.Should().ThrowAsync()
.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 { ["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 _responses = new(responses);
protected override Task 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 : ILogger
{
public List Messages { get; } = [];
public IDisposable? BeginScope(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func formatter)
{
Messages.Add(formatter(state, exception));
}
}
}