feat(ldap): failover observability — warn per hop, info on sticky change
This commit is contained in:
@@ -234,4 +234,64 @@ public class LdapAuthServiceFailoverTests
|
||||
Assert.True((await svc.AuthenticateAsync("alice", "pw", CancellationToken.None)).Succeeded);
|
||||
Assert.Single(factory.Created);
|
||||
}
|
||||
|
||||
// ---- observability ----
|
||||
|
||||
/// <summary>Captures emitted log entries so failover visibility can be asserted.</summary>
|
||||
private sealed class RecordingLogger : Microsoft.Extensions.Logging.ILogger<LdapAuthService>
|
||||
{
|
||||
public List<(Microsoft.Extensions.Logging.LogLevel Level, string Message)> Entries { get; } = new();
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => true;
|
||||
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId,
|
||||
TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
=> Entries.Add((logLevel, formatter(state, exception)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Failover_LogsWarningWithEndpointAndStage_AndInfoOnStickyChange()
|
||||
{
|
||||
var logger = new RecordingLogger();
|
||||
var factory = new SequenceLdapConnectionFactory(
|
||||
new FakeLdapConnection().ThrowOnConnect(), HealthyWithUser());
|
||||
var svc = new LdapAuthService(Opts("dc2:3894"), factory, logger);
|
||||
|
||||
await svc.AuthenticateAsync("alice", "pw", CancellationToken.None);
|
||||
|
||||
Assert.Contains(logger.Entries, e =>
|
||||
e.Level == Microsoft.Extensions.Logging.LogLevel.Warning
|
||||
&& e.Message.Contains("dc1:636") && e.Message.Contains("connect"));
|
||||
Assert.Contains(logger.Entries, e =>
|
||||
e.Level == Microsoft.Extensions.Logging.LogLevel.Information
|
||||
&& e.Message.Contains("dc2:3894"));
|
||||
// Never log secrets:
|
||||
Assert.DoesNotContain(logger.Entries, e => e.Message.Contains("svcpw") || e.Message.Contains("pw\""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HappyPath_LogsNothing()
|
||||
{
|
||||
var logger = new RecordingLogger();
|
||||
var factory = new SequenceLdapConnectionFactory(HealthyWithUser());
|
||||
var svc = new LdapAuthService(Opts("dc2"), factory, logger);
|
||||
|
||||
await svc.AuthenticateAsync("alice", "pw", CancellationToken.None);
|
||||
|
||||
Assert.Empty(logger.Entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ServiceBindFailure_LogsTheServiceBindStage_NotConnect()
|
||||
{
|
||||
var logger = new RecordingLogger();
|
||||
var factory = new SequenceLdapConnectionFactory(
|
||||
new FakeLdapConnection().ThrowOnServiceBind(), HealthyWithUser());
|
||||
var svc = new LdapAuthService(Opts("dc2"), factory, logger);
|
||||
|
||||
await svc.AuthenticateAsync("alice", "pw", CancellationToken.None);
|
||||
|
||||
Assert.Contains(logger.Entries, e =>
|
||||
e.Level == Microsoft.Extensions.Logging.LogLevel.Warning
|
||||
&& e.Message.Contains("dc1:636") && e.Message.Contains("service-bind"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user