docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)

Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
This commit is contained in:
Joseph Doherty
2026-10-07 15:05:49 -04:00
parent 0ffd4bdbea
commit 513fa1aa16
998 changed files with 17896 additions and 2395 deletions
@@ -17,14 +17,15 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
namespace ZB.MOM.WW.ScadaBridge.InboundAPI.Tests;
/// <summary>
/// WP-3: Tests for script execution on central — timeout, handler dispatch, error handling.
/// WP-5: Safe error messages.
/// Tests for script execution on central — timeout, handler dispatch, error handling.
/// Also covers safe error messages.
/// </summary>
public class InboundScriptExecutorTests
{
private readonly InboundScriptExecutor _executor;
private readonly RouteHelper _route;
/// <summary>Initializes a new instance of the <see cref="InboundScriptExecutorTests"/> class.</summary>
public InboundScriptExecutorTests()
{
_executor = new InboundScriptExecutor(NullLogger<InboundScriptExecutor>.Instance, Substitute.For<IServiceProvider>());
@@ -33,6 +34,8 @@ public class InboundScriptExecutorTests
_route = new RouteHelper(locator, router);
}
/// <summary>Verifies that the trace flag reaches the routed call request.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ExecuteAsync_TraceFlag_ReachesTheRoutedCallRequest()
{
@@ -62,6 +65,8 @@ public class InboundScriptExecutorTests
Assert.False(captured[1].Trace);
}
/// <summary>Verifies that a method with SkipBodyCapture set routes with body capture turned off.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Executor_MethodWithSkipBodyCapture_RoutesWithBodyCaptureOff()
{
@@ -99,6 +104,8 @@ public class InboundScriptExecutorTests
Assert.Null(row.ResponseSummary);
}
/// <summary>Verifies that a method without SkipBodyCapture routes with body capture turned on.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Executor_MethodWithoutSkipBodyCapture_RoutesWithBodyCaptureOn()
{
@@ -133,6 +140,8 @@ public class InboundScriptExecutorTests
Assert.Equal("1", row.ResponseSummary);
}
/// <summary>Verifies that a registered handler executes successfully.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task RegisteredHandler_ExecutesSuccessfully()
{
@@ -154,6 +163,8 @@ public class InboundScriptExecutorTests
Assert.Contains("42", result.ResultJson);
}
/// <summary>Verifies that an unregistered handler with an invalid script returns a compilation failure.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task UnregisteredHandler_InvalidScript_ReturnsCompilationFailure()
{
@@ -170,6 +181,8 @@ public class InboundScriptExecutorTests
Assert.Contains("Script compilation failed", result.ErrorMessage);
}
/// <summary>Verifies that an unregistered handler with a valid script is compiled lazily.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task UnregisteredHandler_ValidScript_LazyCompiles()
{
@@ -185,6 +198,8 @@ public class InboundScriptExecutorTests
Assert.True(result.Success);
}
/// <summary>Verifies that a throwing handler returns a safe error message.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HandlerThrows_ReturnsSafeErrorMessage()
{
@@ -198,10 +213,12 @@ public class InboundScriptExecutorTests
TimeSpan.FromSeconds(10));
Assert.False(result.Success);
// WP-5: Safe error message — should NOT contain "internal detail leak"
// Safe error message — should NOT contain "internal detail leak"
Assert.Equal("Internal script error", result.ErrorMessage);
}
/// <summary>Verifies that the error detail carries the exception while the message stays safe.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HandlerThrows_ErrorDetailCarriesTheException_WhileTheMessageStaysSafe()
{
@@ -229,6 +246,8 @@ public class InboundScriptExecutorTests
Assert.DoesNotContain("real reason", result.ErrorMessage!);
}
/// <summary>Verifies that a handler that times out carries no error detail.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HandlerTimesOut_CarriesNoErrorDetail()
{
@@ -252,6 +271,8 @@ public class InboundScriptExecutorTests
Assert.Null(result.ErrorDetail);
}
/// <summary>Verifies that a timed-out routed ask returns a 500 with the site-unreachable code.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task RoutedAskTimeout_Returns500WithSiteUnreachableCode()
{
@@ -276,6 +297,8 @@ public class InboundScriptExecutorTests
Assert.Equal("SITE_UNREACHABLE", result.ErrorCode);
}
/// <summary>Verifies that a site failure whose message mentions unreachable is reported as a script error rather than site unreachable.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task SiteRespondedFailure_MessageMentioningUnreachable_IsScriptErrorNotSiteUnreachable()
{
@@ -303,6 +326,8 @@ public class InboundScriptExecutorTests
Assert.Equal("SCRIPT_ERROR", result.ErrorCode);
}
/// <summary>Verifies that a handler that times out returns a timeout error.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HandlerTimesOut_ReturnsTimeoutError()
{
@@ -323,6 +348,8 @@ public class InboundScriptExecutorTests
Assert.Contains("timed out", result.ErrorMessage);
}
/// <summary>Verifies that a handler can access the request parameters.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HandlerAccessesParameters()
{
@@ -342,8 +369,10 @@ public class InboundScriptExecutorTests
Assert.Contains("ScadaBridge", result.ResultJson!);
}
// --- InboundAPI-001: concurrent lazy-compile must not corrupt the handler cache ---
// --- Concurrent lazy-compile must not corrupt the handler cache ---
/// <summary>Verifies that concurrent lazy compilation of the same method does not corrupt the handler cache.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ConcurrentLazyCompile_SameMethod_DoesNotCorruptCache()
{
@@ -368,6 +397,8 @@ public class InboundScriptExecutorTests
});
}
/// <summary>Verifies that concurrent registration and execution do not throw.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ConcurrentRegisterAndExecute_DoesNotThrow()
{
@@ -397,8 +428,10 @@ public class InboundScriptExecutorTests
await Task.WhenAll(writers.Concat(readers));
}
// --- InboundAPI-005: compiled scripts must not bypass the script trust model ---
// --- Compiled scripts must not bypass the script trust model ---
/// <summary>Verifies that compiling and registering a script that uses a forbidden API rejects the script.</summary>
/// <param name="script">The script source that uses a forbidden API.</param>
[Theory]
[InlineData("System.IO.File.Delete(\"/tmp/x\"); return null;")]
[InlineData("System.Diagnostics.Process.Start(\"/bin/sh\"); return null;")]
@@ -414,6 +447,8 @@ public class InboundScriptExecutorTests
Assert.False(registered);
}
/// <summary>Verifies that executing a script that uses a forbidden API does not run it and returns a failure.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ExecuteAsync_ForbiddenApiScript_DoesNotRunAndReturnsFailure()
{
@@ -436,6 +471,7 @@ public class InboundScriptExecutorTests
Assert.False(System.IO.File.Exists(marker));
}
/// <summary>Verifies that a permitted script still registers.</summary>
[Fact]
public void CompileAndRegister_PermittedScript_StillRegisters()
{
@@ -451,6 +487,7 @@ public class InboundScriptExecutorTests
// --- Compile-diagnostics overload: surface errors so a non-compiling save is not a silent no-op ---
/// <summary>Verifies that a script that does not compile reports errors.</summary>
[Fact]
public void CompileAndRegister_NonCompilingScript_ReportsErrors()
{
@@ -465,6 +502,7 @@ public class InboundScriptExecutorTests
Assert.NotEmpty(errors);
}
/// <summary>Verifies that a valid script reports no errors.</summary>
[Fact]
public void CompileAndRegister_ValidScript_ReportsNoErrors()
{
@@ -476,6 +514,8 @@ public class InboundScriptExecutorTests
Assert.Empty(errors);
}
/// <summary>Verifies that executing after a non-compiling update surfaces the compile failure.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task CompileAndRegister_NonCompilingUpdate_ExecuteSurfacesCompileFailure()
{
@@ -509,8 +549,10 @@ public class InboundScriptExecutorTests
Assert.Contains("111", stillGood.ResultJson);
}
// --- InboundAPI-002: lazy compile-and-fetch must be atomic, never KeyNotFoundException ---
// --- Lazy compile-and-fetch must be atomic, never KeyNotFoundException ---
/// <summary>Verifies that a lazy compile racing a handler removal never throws a <see cref="KeyNotFoundException"/>.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task LazyCompile_RacingRemoveHandler_NeverThrowsKeyNotFound()
{
@@ -540,8 +582,10 @@ public class InboundScriptExecutorTests
await Task.WhenAll(removers.Concat(callers));
}
// --- InboundAPI-004: a client disconnect must NOT be reported as a script timeout ---
// --- A client disconnect must NOT be reported as a script timeout ---
/// <summary>Verifies that a client disconnect is not reported as a timeout.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ClientDisconnect_IsNotReportedAsTimeout()
{
@@ -570,11 +614,13 @@ public class InboundScriptExecutorTests
Assert.DoesNotContain("timed out", result.ErrorMessage ?? string.Empty);
}
/// <summary>Verifies that a genuine timeout is still reported as a timeout.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GenuineTimeout_StillReportedAsTimeout()
{
// A method that exceeds its timeout with no client abort must still be
// reported as "timed out" (regression guard for the InboundAPI-004 fix).
// reported as "timed out" (regression guard for the client-disconnect fix).
var method = new ApiMethod("genuine", "return 1;") { Id = 1, TimeoutSeconds = 1 };
_executor.RegisterHandler("genuine", async ctx =>
{
@@ -593,9 +639,11 @@ public class InboundScriptExecutorTests
Assert.Contains("timed out", result.ErrorMessage);
}
// --- InboundAPI-009: a script that fails to compile must be compiled at most
// --- A script that fails to compile must be compiled at most
// once — repeated calls must not re-run the expensive Roslyn compilation. ---
/// <summary>Verifies that a failed compilation is not retried on every request.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FailedCompilation_IsNotRetriedOnEveryRequest()
{
@@ -620,6 +668,7 @@ public class InboundScriptExecutorTests
Assert.Equal(1, counter.CompilationFailures);
}
/// <summary>Verifies that a failed compilation is recompiled after compile-and-register is called again.</summary>
[Fact]
public void FailedCompilation_RecompilesAfterCompileAndRegisterCalledAgain()
{
@@ -632,9 +681,10 @@ public class InboundScriptExecutorTests
Assert.True(_executor.CompileAndRegister(good));
}
// --- InboundAPI-024: _knownBadMethods must be bounded so a spam attack of
// --- _knownBadMethods must be bounded so a spam attack of
// unique method names cannot grow the cache without bound. ---
/// <summary>Verifies that the known-bad-methods cache never exceeds its cap under a flood of unique method names.</summary>
[Fact]
public void KnownBadMethodsCache_SizeNeverExceedsCap_UnderUniqueNameFlood()
{
@@ -656,6 +706,8 @@ public class InboundScriptExecutorTests
$"known-bad cache size {_executor.KnownBadMethodCount} must not exceed cap {cap}");
}
/// <summary>Verifies that the lazy-compile path also keeps the known-bad-methods cache capped under a flood of unique method names.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task KnownBadMethodsCache_LazyCompilePath_AlsoCappedUnderUniqueNameFlood()
{
@@ -678,8 +730,10 @@ public class InboundScriptExecutorTests
$"known-bad cache size {_executor.KnownBadMethodCount} must not exceed cap {cap}");
}
// --- InboundAPI-014: the script return value is validated against ReturnDefinition ---
// --- The script return value is validated against ReturnDefinition ---
/// <summary>Verifies that a return value matching the return definition succeeds.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReturnValue_MatchingReturnDefinition_Succeeds()
{
@@ -701,6 +755,8 @@ public class InboundScriptExecutorTests
Assert.True(result.Success, result.ErrorMessage);
}
/// <summary>Verifies that a return value not matching the return definition returns a failure instead of a malformed 200.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReturnValue_NotMatchingReturnDefinition_ReturnsFailureNotMalformed200()
{
@@ -725,6 +781,8 @@ public class InboundScriptExecutorTests
Assert.Null(result.ResultJson);
}
/// <summary>Verifies that a method with no return definition leaves the return value unconstrained.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReturnValue_NoReturnDefinition_IsUnconstrained()
{
@@ -744,9 +802,11 @@ public class InboundScriptExecutorTests
Assert.Contains("whatever", result.ResultJson!);
}
// --- Audit Log #23 (ParentExecutionId, T3): the inbound request's
// --- ParentExecutionId: the inbound request's
// ExecutionId is threaded through ExecuteAsync onto routed calls ---
/// <summary>Verifies that executing with a parent execution id makes the routed call carry it as its parent execution id.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ExecuteAsync_WithParentExecutionId_RoutedCallCarriesItAsParentExecutionId()
{
@@ -780,6 +840,8 @@ public class InboundScriptExecutorTests
Assert.Equal(inboundExecutionId, captured!.ParentExecutionId);
}
/// <summary>Verifies that executing without a parent execution id gives the routed call a null parent execution id.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ExecuteAsync_WithoutParentExecutionId_RoutedCallHasNullParentExecutionId()
{
@@ -812,6 +874,8 @@ public class InboundScriptExecutorTests
// --- IpsenMES MoveIn: the read-only Database helper is exposed on the script
// context and resolves IDatabaseGateway from a DI scope per execution. ---
/// <summary>Verifies that a script that uses Database queries through the gateway.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Script_UsingDatabase_QueriesViaGateway()
{
@@ -844,6 +908,8 @@ public class InboundScriptExecutorTests
Assert.Contains("Z28061A", result.ResultJson!);
}
/// <summary>Verifies that a script that does not use Database runs with a service provider that lacks the gateway.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Script_NotUsingDatabase_RunsWithProviderLackingGateway()
{
@@ -865,6 +931,8 @@ public class InboundScriptExecutorTests
Assert.Contains("99", result.ResultJson!);
}
/// <summary>Verifies that a throwing script still disposes its DI scope.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ScriptThrows_DisposesDiScope()
{
@@ -915,9 +983,13 @@ public class InboundScriptExecutorTests
private sealed class SqliteGateway : IDatabaseGateway
{
private readonly string _cs;
/// <summary>Initializes a new instance of the <see cref="SqliteGateway"/> class.</summary>
/// <param name="cs">The SQLite connection string.</param>
public SqliteGateway(string cs) => _cs = cs;
/// <inheritdoc />
public async Task<DbConnection> GetConnectionAsync(string name, CancellationToken ct = default)
{ var c = new SqliteConnection(_cs); await c.OpenAsync(ct); return c; }
/// <inheritdoc />
public Task<ExternalCallResult> CachedWriteAsync(string c, string s,
IReadOnlyDictionary<string, object?>? p = null, string? o = null, CancellationToken ct = default,
TrackedOperationId? t = null, Guid? e = null, string? src = null, Guid? pe = null)
@@ -934,6 +1006,8 @@ public class InboundScriptExecutorTests
return new SqliteGateway("DataSource=file:movein-exec?mode=memory&cache=shared");
}
/// <summary>Verifies that a timed-out script has its scope disposal deferred so no scope is orphaned.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TimedOutScript_ScopeDisposalDeferred_NoOrphanLeak()
{
@@ -956,6 +1030,8 @@ public class InboundScriptExecutorTests
// --- Flow Debugger step logs (design 2026-09-09 D4/D5/D7): the Log global ---
/// <summary>Verifies that a traced run emits one ScriptLog row on the trace sink.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Log_TracedRun_EmitsOneScriptLogRowOnTheTraceSink()
{
@@ -987,6 +1063,8 @@ public class InboundScriptExecutorTests
Assert.Null(extra.StepEventId);
}
/// <summary>Verifies that a traced run writes the ScriptLog row to the central audit store.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Log_TracedRun_WritesTheScriptLogRowToTheCentralAuditStore()
{
@@ -1018,6 +1096,8 @@ public class InboundScriptExecutorTests
Arg.Any<CancellationToken>());
}
/// <summary>Verifies that an untraced run writes only to the node logger.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Log_UntracedRun_WritesOnlyToTheNodeLogger()
{
@@ -1045,6 +1125,8 @@ public class InboundScriptExecutorTests
Assert.Contains(recorded, m => m.Contains("unwatched", StringComparison.Ordinal));
}
/// <summary>Verifies that a traced log call with an exception carries both the message and the detail.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Log_TracedWithException_CarriesMessageAndDetail()
{
@@ -1068,6 +1150,8 @@ public class InboundScriptExecutorTests
Assert.Equal("warn", ScriptTraceExtra.TryParse(details.Extra)!.LogLevel);
}
/// <summary>Verifies that log lines past the per-run maximum are dropped and a single closing runtime warning is emitted.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Log_PastMaxLogLinesPerRun_DropsAndEmitsOneClosingRuntimeWarning()
{
@@ -1110,9 +1194,23 @@ public class InboundScriptExecutorTests
// logger saw a line even when nothing was audited.
private sealed class RecordingLogger<T>(List<string> messages) : ILogger<T>
{
/// <summary>Returns no scope; the fake logger does not track scopes.</summary>
/// <typeparam name="TState">The scope state type.</typeparam>
/// <param name="state">The scope state.</param>
/// <returns>Always null.</returns>
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
/// <summary>Reports every log level as enabled.</summary>
/// <param name="logLevel">The log level being queried.</param>
/// <returns>Always true.</returns>
public bool IsEnabled(LogLevel logLevel) => true;
/// <summary>Records the log entry for later assertions.</summary>
/// <typeparam name="TState">The log state type.</typeparam>
/// <param name="logLevel">The entry's log level.</param>
/// <param name="eventId">The entry's event id.</param>
/// <param name="state">The entry's state.</param>
/// <param name="exception">The entry's exception, if any.</param>
/// <param name="formatter">Formats the state and exception into the message.</param>
public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func<TState, Exception?, string> formatter)
@@ -1128,9 +1226,24 @@ public class InboundScriptExecutorTests
private sealed class CountingLogger<T>(CompileLogCounter counter) : ILogger<T>
{
/// <summary>Returns no scope; the fake does not track scopes.</summary>
/// <typeparam name="TState">The type of the scope state.</typeparam>
/// <param name="state">The scope state (ignored).</param>
/// <returns>Always null.</returns>
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
/// <summary>Reports every log level as enabled.</summary>
/// <param name="logLevel">The log level to check.</param>
/// <returns>Always true.</returns>
public bool IsEnabled(LogLevel logLevel) => true;
/// <summary>Counts entries whose message reports a script compilation failure.</summary>
/// <typeparam name="TState">The type of the log state.</typeparam>
/// <param name="logLevel">The severity of the entry.</param>
/// <param name="eventId">The event identifier.</param>
/// <param name="state">The state object to format.</param>
/// <param name="exception">The exception associated with the entry, if any.</param>
/// <param name="formatter">Formats the state and exception into the message.</param>
public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func<TState, Exception?, string> formatter)