✨ feat(*): 添加 ConfigRoot 运行时配置目录隔离
* 新增 ControllerClientCompatOptions.ConfigRoot 及解析方法 * 兼容层默认从运行目录 Config 加载模型、轨迹和配置 * 移除隐式父工作区根目录推断,旧路径仅在显式配置时生效 * Host 项目编译时将 Config 目录复制到输出目录 * 请求响应日志中间件忽略 /api/status/snapshot 高频轮询 * 补充 ConfigRoot 和日志过滤相关单元测试
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user