Merge branch 'worktree-agent-aa9b7f4762bd3718d' into fix/flow-debugger-arch-review
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.Data.Common;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NSubstitute;
|
||||
using ZB.MOM.WW.Audit;
|
||||
@@ -58,6 +59,26 @@ public class InboundDatabaseHelperAuditTests
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arch-review F8: a minimal <see cref="ILogger"/> double that records every
|
||||
/// Warning-level message, mirroring the <c>CountingLogger<T></c> pattern in
|
||||
/// <c>InboundScriptExecutorTests</c>.
|
||||
/// </summary>
|
||||
private sealed class RecordingLogger : ILogger
|
||||
{
|
||||
public List<(LogLevel Level, string Message, Exception? Exception)> Entries { get; } = new();
|
||||
|
||||
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)
|
||||
{
|
||||
Entries.Add((logLevel, formatter(state, exception), exception));
|
||||
}
|
||||
}
|
||||
|
||||
private static SqliteGateway SeededGateway() => new();
|
||||
|
||||
private readonly List<AuditEvent> _written = new();
|
||||
@@ -166,6 +187,32 @@ public class InboundDatabaseHelperAuditTests
|
||||
Assert.Equal(1, n2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuditWriterFaulting_LogsWarning_AndCallStillSucceeds()
|
||||
{
|
||||
// Arch-review F8: the best-effort continuation used to be `static _ => { }` — a
|
||||
// faulting write vanished with no trace anywhere. It now logs a Warning (and runs
|
||||
// on TaskScheduler.Default rather than riding whatever context completed the
|
||||
// audit-writer task), while the call itself still succeeds either way.
|
||||
var faultedTask = Substitute.For<ICentralAuditWriter>();
|
||||
faultedTask.WriteAsync(Arg.Any<AuditEvent>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromException(new InvalidOperationException("audit down")));
|
||||
var logger = new RecordingLogger();
|
||||
var helper = new InboundDatabaseHelper(
|
||||
SeededGateway(), CancellationToken.None, TimeSpan.Zero,
|
||||
auditWriter: faultedTask, nodeIdentity: _node,
|
||||
executionId: _exec, parentExecutionId: _parent, methodName: "movein",
|
||||
traceSink: null, logger: logger);
|
||||
|
||||
var n = await helper.ExecuteAsync("MesDb", "UPDATE Machine SET Code=Code", null);
|
||||
|
||||
Assert.Equal(1, n);
|
||||
var entry = Assert.Single(logger.Entries);
|
||||
Assert.Equal(LogLevel.Warning, entry.Level);
|
||||
Assert.NotNull(entry.Exception);
|
||||
Assert.Contains("MesDb", entry.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TraceSink_ReceivesTheSameInstance_BeforeTheWriter()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user