One FixedWindowBudget (injectable clock) owned by ConceptVoteRateLimits; the vote page 429s with Retry-After before any row or cookie. Prefix lengths and budgets are validated in every environment. Pins the empty Origin branch of CastRequestGuard. Unverified; not yet built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
45 lines
1.1 KiB
C#
45 lines
1.1 KiB
C#
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Bifrost.Web.Tests.Support;
|
|
|
|
public sealed class CapturingLoggerProvider : ILoggerProvider
|
|
{
|
|
private readonly List<string> _lines = [];
|
|
|
|
public IReadOnlyList<string> Lines
|
|
{
|
|
get
|
|
{
|
|
lock (_lines)
|
|
{
|
|
return _lines.ToArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
public ILogger CreateLogger(string categoryName) => new Capture(this);
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
private sealed class Capture(CapturingLoggerProvider owner) : ILogger
|
|
{
|
|
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)
|
|
{
|
|
lock (owner._lines)
|
|
{
|
|
owner._lines.Add(formatter(state, exception) + (exception is null ? string.Empty : "\n" + exception));
|
|
}
|
|
}
|
|
}
|
|
}
|