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
@@ -20,15 +20,15 @@ namespace ZB.MOM.WW.ScadaBridge.ManagementService.Tests;
/// <summary>
/// Handler tests for the two-person ("secured") write submit/reject/list flow
/// (M7 OPC UA / MxGateway UX, Task T14b). Submit is gated to Operator, reject to
/// Submit is gated to Operator, reject to
/// Verifier; the no-self-approval rule and the MxGateway-protocol precondition
/// are enforced inside the handlers and surface as ManagementError.
/// </summary>
public class SecuredWriteHandlerTests : TestKit, IDisposable
{
/// <summary>
/// The central node the test writer stamps onto every emitted row. #206 routes
/// SecuredWrite audit through <see cref="ICentralAuditWriter"/>, whose
/// The central node the test writer stamps onto every emitted row. SecuredWrite
/// audit is routed through <see cref="ICentralAuditWriter"/>, whose
/// <see cref="INodeIdentityProvider"/> stamps <c>SourceNode</c>; the handler tests
/// assert each lifecycle row carries this value (proving the stamping actually runs).
/// </summary>
@@ -40,6 +40,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
private readonly StubCommunicationService _comms;
private readonly ServiceCollection _services;
/// <summary>Initializes the test kit with substitute repositories, the real central audit writer and a stub communication service.</summary>
public SecuredWriteHandlerTests()
{
_siteRepo = Substitute.For<ISiteRepository>();
@@ -59,7 +60,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_services.AddSingleton<IOptions<ManagementServiceOptions>>(
Options.Create(new ManagementServiceOptions()));
// #206: route SecuredWrite audit through the REAL CentralAuditWriter (the same
// Route SecuredWrite audit through the REAL CentralAuditWriter (the same
// central-direct-write path Notification Outbox dispatch + Inbound API use) so
// these tests exercise SourceNode stamping end-to-end — actor → writer → repo.
// The writer stamps SourceNode from the INodeIdentityProvider (central-a/central-b)
@@ -78,7 +79,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
/// <summary>
/// Captures every <see cref="AuditEvent"/> that lands at the substituted
/// <see cref="IAuditLogRepository.InsertIfNotExistsAsync"/> — i.e. the row AFTER the
/// real <see cref="CentralAuditWriter"/> has stamped <c>SourceNode</c> (#206), so the
/// real <see cref="CentralAuditWriter"/> has stamped <c>SourceNode</c>, so the
/// captured events carry the production node-of-origin value. Audit emission is
/// best-effort and asynchronous off the actor thread, so a short await-condition
/// poll lets the captured list settle before assertions.
@@ -112,20 +113,26 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
/// </summary>
private sealed class StubCommunicationService : CommunicationService
{
/// <summary>Initializes a new stub with default communication options.</summary>
public StubCommunicationService()
: base(Options.Create(new CommunicationOptions()), NullLogger<CommunicationService>.Instance)
{
}
/// <summary>Gets the last write request relayed through the stub.</summary>
public WriteTagRequest? LastRequest { get; private set; }
/// <summary>Gets the number of times the stub was asked to write a tag.</summary>
public int CallCount { get; private set; }
/// <summary>Gets or sets the function that produces the canned response for a write request.</summary>
public Func<WriteTagRequest, WriteTagResponse> Responder { get; set; } =
req => new WriteTagResponse(req.CorrelationId, Success: true, ErrorMessage: null, DateTimeOffset.UtcNow);
/// <summary>Gets or sets an exception to throw when a write is attempted.</summary>
public Exception? ThrowOnWrite { get; set; }
/// <inheritdoc />
public override Task<WriteTagResponse> WriteTagAsync(
string siteId, WriteTagRequest request, CancellationToken ct = default)
{
@@ -154,6 +161,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
private static ManagementEnvelope ScopedEnvelope(object command, string username, string[] permittedSiteIds, params string[] roles) =>
new(new AuthenticatedUser(username, username, roles, permittedSiteIds), command, Guid.NewGuid().ToString("N"));
/// <inheritdoc />
void IDisposable.Dispose() => Shutdown();
/// <summary>Wires a site whose single connection uses the given protocol.</summary>
@@ -172,6 +180,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
// Submit
// ------------------------------------------------------------------------
/// <summary>Verifies that submitting on a non-MxGateway connection returns an error and inserts no row.</summary>
[Fact]
public void Submit_OnNonMxGatewayConnection_ReturnsError_NoRowInserted()
{
@@ -190,6 +199,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.DidNotReceiveWithAnyArgs().AddAsync(default!, default);
}
/// <summary>Verifies that submitting on an unknown connection returns an error and inserts no row.</summary>
[Fact]
public void Submit_OnUnknownConnection_ReturnsError_NoRowInserted()
{
@@ -207,6 +217,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.DidNotReceiveWithAnyArgs().AddAsync(default!, default);
}
/// <summary>Verifies that submitting on an MxGateway connection inserts a pending row stamped with the operator user.</summary>
[Fact]
public void Submit_OnMxGatewayConnection_InsertsPendingRowWithOperatorUser()
{
@@ -274,8 +285,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
/// Arms the reject compare-and-swap to succeed (this verifier wins the
/// Pending-&gt;Rejected transition). NSubstitute's unarmed default for the
/// bool-returning CAS is <c>false</c>, i.e. "lost the race", so every reject test
/// that expects the decision to land must arm it explicitly (arch-review
/// 2026-08-19 P6-1).
/// that expects the decision to land must arm it explicitly.
/// </summary>
private void ArmRejectCasSuccess(long id) =>
_securedWriteRepo.TryMarkRejectedAsync(
@@ -293,6 +303,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Empty(captured);
}
/// <summary>Verifies that rejecting as the submitting user returns an error and leaves the status unchanged.</summary>
[Fact]
public void Reject_BySubmittingUser_ReturnsError_StatusUnchanged()
{
@@ -311,6 +322,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
default, default!, default, default, default);
}
/// <summary>Verifies that rejecting as a different user flips the status to rejected through the compare-and-swap, with no blind update.</summary>
[Fact]
public void Reject_ByDifferentUser_CasFlipsStatusToRejected_NoBlindUpdate()
{
@@ -326,7 +338,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(envelope.CorrelationId, response.CorrelationId);
// The decision goes through the conditional Pending->Rejected CAS, exactly once,
// carrying the verifier identity + comment (arch-review 2026-08-19 P6-1).
// carrying the verifier identity + comment.
_securedWriteRepo.Received(1).TryMarkRejectedAsync(
7, "bob", "not authorized", Arg.Any<DateTime>(), Arg.Any<CancellationToken>());
// The blind full-row overwrite is gone — it could land mid-relay on a row a
@@ -341,6 +353,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Contains("alice", response.JsonData);
}
/// <summary>Verifies that rejecting a non-pending row returns an error.</summary>
[Fact]
public void Reject_NonPendingRow_ReturnsError()
{
@@ -362,10 +375,11 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
AssertNoAuditRows(captured);
}
/// <summary>Verifies that a reject that loses the compare-and-swap race returns a conflict, writes nothing and emits no reject audit row.</summary>
[Fact]
public void Reject_WhenCasLosesRace_ReturnsConflict_WritesNothing_NoRejectAudit()
{
// The P6-1 race, from the reject side: the status read at the top of the handler
// The race, from the reject side: the status read at the top of the handler
// saw Pending, but a concurrent approve won the Pending->Approved CAS and entered
// its multi-second WriteTagAsync relay before this reject reached the store. The
// reject must lose (CAS returns false), record NOTHING — no status write, no
@@ -409,6 +423,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
AssertNoAuditRows(captured);
}
/// <summary>Verifies that approving an already rejected row returns an error with no compare-and-swap, relay or audit.</summary>
[Fact]
public void Approve_OnAlreadyRejectedRow_ReturnsError_NoCas_NoRelay_NoAudit()
{
@@ -431,6 +446,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
AssertNoAuditRows(captured);
}
/// <summary>Verifies that rejecting a missing row returns an error.</summary>
[Fact]
public void Reject_MissingRow_ReturnsError()
{
@@ -449,6 +465,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
// List
// ------------------------------------------------------------------------
/// <summary>Verifies that listing filters by status and site.</summary>
[Fact]
public void List_FiltersByStatusAndSite()
{
@@ -478,6 +495,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.Received(1).QueryAsync("Pending", "SITE1", 0, 200, Arg.Any<IReadOnlyCollection<string>?>(), Arg.Any<CancellationToken>());
}
/// <summary>Verifies that listing with no filters passes nulls to the repository.</summary>
[Fact]
public void List_NoFilters_PassesNullsToRepository()
{
@@ -494,6 +512,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.Received(1).QueryAsync(null, null, 0, 200, Arg.Any<IReadOnlyCollection<string>?>(), Arg.Any<CancellationToken>());
}
/// <summary>Verifies that a site-scoped reader requesting an out-of-scope site is unauthorized.</summary>
[Fact]
public void List_SiteScopedReader_ExplicitOutOfScopeSiteFilter_Unauthorized()
{
@@ -503,6 +522,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
ExpectMsg<ManagementUnauthorized>(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that an unfiltered list for a site-scoped reader queries only the permitted site identifiers.</summary>
[Fact]
public void List_SiteScopedReader_Unfiltered_QueriesOnlyPermittedIdentifiers()
{
@@ -522,6 +542,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Arg.Any<CancellationToken>());
}
/// <summary>Verifies that a system-wide reader passes a null permitted-sites filter.</summary>
[Fact]
public void List_SystemWideReader_PassesNullPermittedFilter()
{
@@ -537,7 +558,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
}
// ------------------------------------------------------------------------
// Approve (execute) — Task C3
// Approve (execute)
// ------------------------------------------------------------------------
/// <summary>Arms the CAS to succeed (a different verifier wins the race).</summary>
@@ -546,6 +567,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
id, Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>())
.Returns(true);
/// <summary>Verifies that approval by a different verifier relays the write and flips the status to executed.</summary>
[Fact]
public void Approve_ByDifferentVerifier_RelaysWrite_FlipsStatusToExecuted()
{
@@ -581,6 +603,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Null(updated.ExecutionError);
}
/// <summary>Verifies that approving as the submitting user returns an error with no compare-and-swap and no relay.</summary>
[Fact]
public void Approve_BySubmittingUser_ReturnsError_CasNotCalled_NoRelay()
{
@@ -599,6 +622,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(0, _comms.CallCount);
}
/// <summary>Verifies that an approval that loses the compare-and-swap race returns an error and does not relay.</summary>
[Fact]
public void Approve_WhenCasLosesRace_ReturnsError_NoRelay()
{
@@ -622,6 +646,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
AssertNoAuditRows(captured);
}
/// <summary>Verifies that a failed relay flips the status to failed and records the error.</summary>
[Fact]
public void Approve_WhenRelayFails_FlipsStatusToFailed_WithError()
{
@@ -649,6 +674,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.NotNull(updated.ExecutedAtUtc);
}
/// <summary>Verifies that a relay that throws flips the status to failed and records the exception message.</summary>
[Fact]
public void Approve_WhenRelayThrows_FlipsStatusToFailed_WithExceptionMessage()
{
@@ -672,6 +698,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Contains("transport down", updated.ExecutionError);
}
/// <summary>Verifies that an unknown value type flips the status to failed without relaying.</summary>
[Fact]
public void Approve_UnknownValueType_FlipsStatusToFailed_NoRelay()
{
@@ -696,6 +723,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal("unknown value type", updated.ExecutionError);
}
/// <summary>Verifies that approving when the connection is no longer MxGateway flips the status to failed without relaying.</summary>
[Fact]
public void Approve_WhenConnectionNoLongerMxGateway_FlipsStatusToFailed_NoRelay()
{
@@ -736,6 +764,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(ZB.MOM.WW.Audit.AuditOutcome.Failure, execute.Outcome);
}
/// <summary>Verifies that approving when the connection was deleted flips the status to failed as not found, without relaying.</summary>
[Fact]
public void Approve_WhenConnectionDeleted_FlipsStatusToFailed_NotFound_NoRelay()
{
@@ -766,9 +795,10 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
}
// ------------------------------------------------------------------------
// List paging — Task 17 (arch-review P3)
// List paging
// ------------------------------------------------------------------------
/// <summary>Verifies that listing with skip and take forwards paging and returns the total count.</summary>
[Fact]
public void List_WithSkipTake_ForwardsPagingAndReturnsTotalCount()
{
@@ -786,9 +816,10 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
}
// ------------------------------------------------------------------------
// Opportunistic expiry sweep on list — Task 16 (arch-review S2)
// Opportunistic expiry sweep on list
// ------------------------------------------------------------------------
/// <summary>Verifies that listing sweeps stale pending rows to expired and calls the expiry compare-and-swap once, for the stale rows only.</summary>
[Fact]
public void List_SweepsStalePendingRows_MarksExpired_CallsTryMarkExpiredOnceForStaleOnly()
{
@@ -822,9 +853,10 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
}
// ------------------------------------------------------------------------
// Server-side TTL expiry — Task 15 (arch-review S2)
// Server-side TTL expiry
// ------------------------------------------------------------------------
/// <summary>Verifies that approving a pending row older than the time-to-live expires it instead of executing.</summary>
[Fact]
public void Approve_PendingRowOlderThanTtl_ExpiresInsteadOfExecuting()
{
@@ -845,6 +877,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.Received(1).TryMarkExpiredAsync(1, Arg.Any<DateTime>(), Arg.Any<CancellationToken>());
}
/// <summary>Verifies that rejecting a pending row older than the time-to-live expires it instead of rejecting.</summary>
[Fact]
public void Reject_PendingRowOlderThanTtl_ExpiresInsteadOfRejecting()
{
@@ -865,6 +898,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.Received(1).TryMarkExpiredAsync(1, Arg.Any<DateTime>(), Arg.Any<CancellationToken>());
}
/// <summary>Verifies that approving a fresh pending row still executes.</summary>
[Fact]
public void Approve_FreshPendingRow_StillExecutes()
{
@@ -904,6 +938,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
return new Guid(bytes);
}
/// <summary>Verifies that submitting emits exactly one submit audit row carrying the operator and correlation id.</summary>
[Fact]
public void Submit_EmitsExactlyOneSubmitAuditRow_WithOperatorAndCorrelation()
{
@@ -927,13 +962,14 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(CorrelationFor(55L), evt.CorrelationId);
Assert.Equal("SecuredWrite", evt.Category);
Assert.Equal("SITE1/Mx1/Tag.Setpoint", evt.Target);
// #206: the Submit row is stamped with the writing central node's identifier
// The Submit row is stamped with the writing central node's identifier
// (it routed through the real CentralAuditWriter), not the legacy null SourceNode.
Assert.Equal(ExpectedSourceNode, evt.SourceNode);
// Exactly one row — no stray duplicates.
Assert.Single(captured);
}
/// <summary>Verifies that rejecting emits one reject audit row carrying the verifier.</summary>
[Fact]
public void Reject_EmitsOneRejectAuditRow_WithVerifier()
{
@@ -952,11 +988,12 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
var evt = SingleOfKind(captured, AuditKind.SecuredWriteReject);
Assert.Equal("bob", evt.Actor);
Assert.Equal(CorrelationFor(7L), evt.CorrelationId);
// #206: Reject row carries the writing central node's identifier.
// The Reject row carries the writing central node's identifier.
Assert.Equal(ExpectedSourceNode, evt.SourceNode);
Assert.Single(captured);
}
/// <summary>Verifies that a successful approval emits an approve row followed by an execute-delivered row.</summary>
[Fact]
public void Approve_Success_EmitsApproveThenExecuteDeliveredAuditRows()
{
@@ -975,7 +1012,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
var approve = SingleOfKind(captured, AuditKind.SecuredWriteApprove);
Assert.Equal("bob", approve.Actor);
Assert.Equal(CorrelationFor(7L), approve.CorrelationId);
// #206: Approve + Execute rows both carry the writing central node's identifier.
// Approve + Execute rows both carry the writing central node's identifier.
Assert.Equal(ExpectedSourceNode, approve.SourceNode);
var execute = SingleOfKind(captured, AuditKind.SecuredWriteExecute);
@@ -987,6 +1024,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(2, captured.Count);
}
/// <summary>Verifies that a failing audit insert does not fail the submit and the row is still persisted.</summary>
[Fact]
public void Submit_AuditInsertThrows_StillSucceeds_RowStillPersisted()
{
@@ -998,7 +1036,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.AddAsync(Arg.Any<PendingSecuredWrite>(), Arg.Any<CancellationToken>())
.Returns(55L);
// Best-effort: a thrown audit insert must NOT abort the secured-write action.
// #206: the throw now happens behind the real CentralAuditWriter (which swallows
// The throw now happens behind the real CentralAuditWriter (which swallows
// it), so this still exercises the standing "audit failure never aborts the
// action" invariant — now through the central-direct-write path.
_auditRepo.InsertIfNotExistsAsync(Arg.Any<AuditEvent>(), Arg.Any<CancellationToken>())
@@ -1019,6 +1057,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal("alice", inserted.OperatorUser);
}
/// <summary>Verifies that a corrupt list value JSON flips the status to failed with a decode error and no relay.</summary>
[Fact]
public void Approve_CorruptListValueJson_FlipsStatusToFailed_DecodeError_NoRelay()
{
@@ -1059,6 +1098,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
// Site scope on submit / approve / reject (arch-review R2 N3)
// ------------------------------------------------------------------------
/// <summary>Verifies that a site-scoped operator submitting for an out-of-scope site is unauthorized and no row is inserted.</summary>
[Fact]
public void Submit_SiteScopedOperator_OutOfScopeSite_Unauthorized_NoRowInserted()
{
@@ -1071,6 +1111,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.DidNotReceiveWithAnyArgs().AddAsync(default!, default);
}
/// <summary>Verifies that a site-scoped operator submitting for an in-scope site succeeds.</summary>
[Fact]
public void Submit_SiteScopedOperator_InScopeSite_Succeeds()
{
@@ -1082,6 +1123,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that a site-scoped verifier approving for an out-of-scope site is unauthorized and never relays.</summary>
[Fact]
public void Approve_SiteScopedVerifier_OutOfScopeSite_Unauthorized_NeverRelays()
{
@@ -1099,6 +1141,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(0, _comms.CallCount); // the device write is NEVER relayed
}
/// <summary>Verifies that a site-scoped verifier rejecting for an out-of-scope site is unauthorized.</summary>
[Fact]
public void Reject_SiteScopedVerifier_OutOfScopeSite_Unauthorized()
{
@@ -1117,6 +1160,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
default, default!, default, default, default);
}
/// <summary>Verifies that a scoped administrator bypasses site scoping on approval.</summary>
[Fact]
public void Approve_ScopedAdministrator_BypassesSiteScope()
{
@@ -1140,7 +1184,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
}
// ------------------------------------------------------------------------
// Resolve-before-scope oracles (arch-review 2026-08-19 P6-5)
// Resolve-before-scope oracles
//
// Submit/approve/reject used to answer "not found" (and, on approve/reject,
// "is 'Executed', not Pending") BEFORE the scope gate ran, so an out-of-scope
@@ -1158,6 +1202,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
OperatorUser = "alice", SubmittedAtUtc = DateTime.UtcNow,
});
/// <summary>Verifies that a site-scoped verifier approving a nonexistent id is unauthorized rather than not found.</summary>
[Fact]
public void Approve_SiteScopedVerifier_NonexistentId_Unauthorized_NotNotFound()
{
@@ -1170,6 +1215,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
ExpectMsg<ManagementUnauthorized>(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that a site-scoped verifier approving an out-of-scope executed row is unauthorized rather than a status error.</summary>
[Fact]
public void Approve_SiteScopedVerifier_OutOfScopeExecutedRow_Unauthorized_NotStatusError()
{
@@ -1185,6 +1231,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Equal(0, _comms.CallCount);
}
/// <summary>Verifies that a system-wide verifier approving a nonexistent id still gets a not-found error.</summary>
[Fact]
public void Approve_SystemWideVerifier_NonexistentId_StillReportsNotFound()
{
@@ -1199,6 +1246,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Contains("not found", response.Error);
}
/// <summary>Verifies that a site-scoped verifier rejecting a nonexistent id is unauthorized rather than not found.</summary>
[Fact]
public void Reject_SiteScopedVerifier_NonexistentId_Unauthorized_NotNotFound()
{
@@ -1211,6 +1259,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
ExpectMsg<ManagementUnauthorized>(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that a site-scoped verifier rejecting an out-of-scope executed row is unauthorized rather than a status error.</summary>
[Fact]
public void Reject_SiteScopedVerifier_OutOfScopeExecutedRow_Unauthorized_NotStatusError()
{
@@ -1225,11 +1274,12 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
default, default!, default, default, default);
}
/// <summary>Verifies that a system-wide verifier rejecting an executed row still gets a status conflict.</summary>
[Fact]
public void Reject_SystemWideVerifier_ExecutedRow_StillReportsStatusConflict()
{
// In-scope/unscoped behavior is unchanged: the status error still names
// the status that blocked the transition (the P6-1 CAS shape is intact).
// the status that blocked the transition (the compare-and-swap shape is intact).
SeedExecutedWrite(42);
var actor = CreateActor();
@@ -1239,6 +1289,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
Assert.Contains("Executed", response.Error);
}
/// <summary>Verifies that a site-scoped operator submitting for a nonexistent site is unauthorized rather than not found.</summary>
[Fact]
public void Submit_SiteScopedOperator_NonexistentSite_Unauthorized_NotNotFound()
{
@@ -1254,6 +1305,7 @@ public class SecuredWriteHandlerTests : TestKit, IDisposable
_securedWriteRepo.DidNotReceiveWithAnyArgs().AddAsync(default!, default);
}
/// <summary>Verifies that a system-wide operator submitting for a nonexistent site still gets a not-found error.</summary>
[Fact]
public void Submit_SystemWideOperator_NonexistentSite_StillReportsNotFound()
{