Files
FlyShotHost/tests/Flyshot.Server.IntegrationTests/RequestResponseLoggingMiddlewareTests.cs
yunxiao.zhu a6579f1e5b feat(*): 添加 ConfigRoot 运行时配置目录隔离
* 新增 ControllerClientCompatOptions.ConfigRoot 及解析方法
* 兼容层默认从运行目录 Config 加载模型、轨迹和配置
* 移除隐式父工作区根目录推断,旧路径仅在显式配置时生效
* Host 项目编译时将 Config 目录复制到输出目录
* 请求响应日志中间件忽略 /api/status/snapshot 高频轮询
* 补充 ConfigRoot 和日志过滤相关单元测试
2026-04-29 18:27:03 +08:00

81 lines
2.5 KiB
C#

using Flyshot.Server.Host.Middleware;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Flyshot.Server.IntegrationTests;
/// <summary>
/// HTTP 请求响应日志中间件测试。
/// </summary>
public sealed class RequestResponseLoggingMiddlewareTests
{
/// <summary>
/// 高频状态快照路径命中忽略前缀时,不应写入请求和响应日志。
/// </summary>
[Fact]
public async Task InvokeAsync_WhenPathMatchesIgnoredPrefix_DoesNotWriteRequestResponseLogs()
{
var logger = new CapturingLogger<RequestResponseLoggingMiddleware>();
var nextWasCalled = false;
var middleware = new RequestResponseLoggingMiddleware(
async context =>
{
nextWasCalled = true;
context.Response.StatusCode = StatusCodes.Status200OK;
await context.Response.WriteAsync("ok");
},
logger);
var context = new DefaultHttpContext();
context.Request.Method = HttpMethods.Get;
context.Request.Path = "/api/status/snapshot/current";
context.Response.Body = new MemoryStream();
await middleware.InvokeAsync(context);
Assert.True(nextWasCalled);
Assert.Empty(logger.Entries);
}
/// <summary>
/// 捕获中间件写出的日志条目,避免测试依赖真实 NLog 目标。
/// </summary>
private sealed class CapturingLogger<T> : ILogger<T>
{
/// <summary>
/// 已捕获的日志条目。
/// </summary>
public List<LogEntry> Entries { get; } = new();
/// <inheritdoc />
public IDisposable? BeginScope<TState>(TState state)
where TState : notnull
{
return null;
}
/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
/// <inheritdoc />
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
Entries.Add(new LogEntry(logLevel, formatter(state, exception)));
}
}
/// <summary>
/// 测试用日志条目。
/// </summary>
/// <param name="Level">日志级别。</param>
/// <param name="Message">格式化后的日志消息。</param>
private sealed record LogEntry(LogLevel Level, string Message);
}