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
@@ -12,7 +12,7 @@ using Xunit;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Repositories;
/// <summary>
/// Bundle D (#23 M1) integration tests for <see cref="AuditLogRepository"/>. Uses
/// Integration tests for <see cref="AuditLogRepository"/>. Uses
/// the same <see cref="MsSqlMigrationFixture"/> as the Bundle C migration tests so
/// raw-SQL paths (the IF NOT EXISTS insert, partition switch) execute against a
/// real partitioned schema. Tests scope all queries by a per-test
@@ -23,11 +23,15 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
{
private readonly MsSqlMigrationFixture _fixture;
/// <summary>Creates the test class with the shared migrated SQL Server fixture.</summary>
/// <param name="fixture">The shared migration fixture.</param>
public AuditLogRepositoryTests(MsSqlMigrationFixture fixture)
{
_fixture = fixture;
}
/// <summary>Verifies that inserting a fresh event writes one row.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertIfNotExistsAsync_FreshEvent_WritesOneRow()
{
@@ -51,6 +55,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(evt.EventId, loaded[0].EventId);
}
/// <summary>Verifies that inserting an event persists its source node.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertIfNotExistsAsync_PersistsSourceNode()
{
@@ -75,6 +81,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal("central-a", loaded[0].SourceNode);
}
/// <summary>Verifies that inserting an event with no source node persists a null source node.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertIfNotExistsAsync_PersistsNullSourceNode()
{
@@ -101,6 +109,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Null(loaded[0].SourceNode);
}
/// <summary>Verifies that inserting a duplicate event id is a no-op that neither throws nor duplicates the row.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertIfNotExistsAsync_DuplicateEventId_IsNoOp_NoExceptionNoDuplicate()
{
@@ -115,7 +125,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
await repo.InsertIfNotExistsAsync(first);
// Same EventId, different payload — first-write-wins, the second call is silently a no-op.
// C3 (Task 2.5): ErrorMessage rides in DetailsJson on the canonical record, so rebuild
// ErrorMessage rides in DetailsJson on the canonical record, so rebuild
// a sibling row carrying the same EventId via the factory (rather than a top-level `with`).
var second = ScadaBridgeAuditEventFactory.Create(
channel: AuditChannel.ApiOutbound,
@@ -133,11 +143,13 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
.ToListAsync();
Assert.Single(loaded);
// C5 (Task 2.5): ErrorMessage rides in DetailsJson now — decode it to assert
// ErrorMessage rides in DetailsJson — decode it to assert
// first-write-wins kept the original payload.
Assert.Equal("first", AuditDetailsCodec.Deserialize(loaded[0].DetailsJson).ErrorMessage);
}
/// <summary>Verifies that a query returns rows in descending occurred-time order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_ReturnsRowsInOccurredDescOrder()
{
@@ -161,6 +173,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.True(rows[1].OccurredAtUtc > rows[2].OccurredAtUtc);
}
/// <summary>Verifies that a query can filter by channel.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByChannel()
{
@@ -185,6 +199,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.All(rows, r => Assert.Equal(AuditChannel.Notification, r.AsRow().Channel));
}
/// <summary>Verifies that filtering by several channels returns their union.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByMultipleChannels_ReturnsUnion()
{
@@ -212,6 +228,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.DoesNotContain(rows, r => r.AsRow().Channel == AuditChannel.DbOutbound);
}
/// <summary>Verifies that filtering by several statuses returns their union.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByMultipleStatuses_ReturnsUnion()
{
@@ -238,6 +256,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.DoesNotContain(rows, r => r.AsRow().Status == AuditStatus.Delivered);
}
/// <summary>Verifies that filtering by several source site ids returns their union.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByMultipleSourceSiteIds_ReturnsUnion()
{
@@ -263,6 +283,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.DoesNotContain(rows, r => r.AsRow().SourceSiteId == siteC);
}
/// <summary>Verifies that an empty channel list does not constrain the query.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_EmptyChannelList_DoesNotConstrain()
{
@@ -286,6 +308,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(2, rows.Count);
}
/// <summary>Verifies that a query can filter by source site id.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterBySourceSiteId()
{
@@ -310,12 +334,14 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ──────────────────────────────────────────────────────────────────────
// Task 15: SourceNodes filter + GetDistinctSourceNodesAsync. Pins the new
// SourceNodes filter + GetDistinctSourceNodesAsync. Pins the new
// Node multi-select contract — non-empty list → SQL IN (…); NULL
// SourceNode rows are excluded when the filter is set; the distinct
// enumeration omits nulls and orders ascending.
// ──────────────────────────────────────────────────────────────────────
/// <summary>Verifies that filtering by source node returns the matching rows.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterBySourceNode_ReturnsMatchingRows()
{
@@ -340,6 +366,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal("central-a", rows[0].SourceNode);
}
/// <summary>Verifies that filtering by several source nodes returns their union.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByMultipleSourceNodes_ReturnsUnion()
{
@@ -364,6 +392,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.All(rows, r => Assert.Contains(r.SourceNode, new[] { "central-a", "central-b" }));
}
/// <summary>Verifies that the distinct source nodes are returned without nulls and in ascending order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetDistinctSourceNodesAsync_ReturnsDistinctNonNullValues_Ordered()
{
@@ -394,6 +424,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(nodes.OrderBy(n => n, StringComparer.Ordinal), nodes);
}
/// <summary>Verifies that filtering by execution id returns the matching rows.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByExecutionId_ReturnsMatchingRows()
{
@@ -422,6 +454,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.All(rows, r => Assert.Equal(executionId, r.AsRow().ExecutionId));
}
/// <summary>Verifies that filtering by parent execution id returns the matching rows.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByParentExecutionId_ReturnsMatchingRows()
{
@@ -451,6 +485,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.All(rows, r => Assert.Equal(parentExecutionId, r.AsRow().ParentExecutionId));
}
/// <summary>Verifies that a query can filter by time range.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_FilterByTimeRange()
{
@@ -476,6 +512,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(t0.AddMinutes(30), rows[0].OccurredAtUtc);
}
/// <summary>Verifies that the next keyset page starts after the cursor.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_Keyset_NextPageStartsAfterCursor()
{
@@ -525,6 +563,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(t0.AddMinutes(0), page3[0].OccurredAtUtc);
}
/// <summary>Verifies that concurrent duplicate inserts produce exactly one row.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertIfNotExistsAsync_ConcurrentDuplicateInserts_ProduceExactlyOneRow()
{
@@ -561,9 +601,11 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ------------------------------------------------------------------------
// WP2.2: set-based InsertManyIfNotExistsAsync
// Set-based InsertManyIfNotExistsAsync
// ------------------------------------------------------------------------
/// <summary>Verifies that a batch insert writes every distinct event.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_WritesEveryDistinctEvent()
{
@@ -593,6 +635,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
rows.Select(r => r.EventId).OrderBy(g => g).ToArray());
}
/// <summary>Verifies that duplicate event ids within one batch collapse to a single row.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_DuplicateEventIdsWithinOnePacket_CollapseToOneRow()
{
@@ -638,6 +682,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Null(AuditDetailsCodec.Deserialize(stored.DetailsJson).ErrorMessage);
}
/// <summary>Verifies that duplicate event ids across batches are inserted idempotently.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_DuplicateEventIdsAcrossPackets_AreIdempotent()
{
@@ -675,6 +721,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(6, rows.Count);
}
/// <summary>Verifies that a batch larger than the SQL parameter ceiling is inserted in chunks.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_ChunksBeyondTheParameterCeiling()
{
@@ -718,6 +766,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
/// server raise 2628/8152. Reject-everywhere is the contract; this test pins
/// both halves of it.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_TargetLongerThanColumn_IsRejected_NotTruncated()
{
@@ -736,7 +785,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
var batchRepo = new AuditLogRepository(batchContext);
// 2628 = "String or binary data would be truncated in table …" (SQL 2019+),
// 8152 = its pre-2019 predecessor. Either proves the server saw the full value.
// 8152 = its predecessor on SQL Server versions before 2019. Either proves the server saw the full value.
var batchEx = await Assert.ThrowsAsync<SqlException>(
() => batchRepo.InsertManyIfNotExistsAsync(new[] { evt }));
Assert.Contains(batchEx.Number, new[] { 2628, 8152 });
@@ -761,6 +810,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
/// The companion to the rejection test: a value that FITS must still round-trip
/// through the set-based path byte for byte, at the exact column boundary.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_TargetAtColumnLimit_RoundTripsIntact()
{
@@ -786,6 +836,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(boundaryTarget, loaded[0].Target);
}
/// <summary>Verifies that inserting an empty batch is a no-op.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task InsertManyIfNotExistsAsync_EmptyBatch_IsANoOp()
{
@@ -797,6 +849,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(0, await repo.InsertManyIfNotExistsAsync(Array.Empty<AuditEvent>()));
}
/// <summary>Verifies that keyset paging breaks ties on event id when rows share an occurred time.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task QueryAsync_Keyset_SameOccurredAtUtc_TiebreaksOnEventId()
{
@@ -855,12 +909,12 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ------------------------------------------------------------------------
// M6-T4 Bundle C: SwitchOutPartitionAsync drop-and-rebuild integration tests
// SwitchOutPartitionAsync drop-and-rebuild integration tests
// ------------------------------------------------------------------------
//
// The partition-switch path replaces M1's NotSupportedException stub with
// The partition-switch path replaces the earlier NotSupportedException stub with
// the production CREATE-staging → SWITCH PARTITION → DROP-staging batch
// documented in alog.md §4. WP2.2 removed the index drop/rebuild that used to
// documented in alog.md §4. The design removed the index drop/rebuild that used to
// bracket it: uniqueness rides the partition-ALIGNED clustered PK
// (EventId, OccurredAtUtc), so SWITCH has nothing to object to. These tests
// verify the side effects an outsider can observe:
@@ -871,6 +925,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
// after a switch (the aligned key really does enforce it)
// * a thrown SqlException leaves the table intact with no orphaned staging
/// <summary>Verifies that switching out an old partition removes its rows and keeps newer partitions.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task SwitchOutPartitionAsync_OldPartition_RemovesRows_NewPartitionsKept()
{
@@ -906,6 +962,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Contains(remaining, e => e.EventId == marEvt.EventId);
}
/// <summary>Verifies that a partition switch leaves no non-aligned unique index behind.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task SwitchOutPartitionAsync_LeavesNoNonAlignedUniqueIndex_AfterSwitch()
{
@@ -918,7 +976,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
// the fixture's MSSQL database) don't tread on each other.
await repo.SwitchOutPartitionAsync(new DateTime(2026, 4, 1, 0, 0, 0, DateTimeKind.Utc));
// WP2.2: the switch must NOT recreate UX_AuditLog_EventId. Re-adding it
// The switch must NOT recreate UX_AuditLog_EventId. Re-adding it
// would reinstate the offline drop/rebuild the purge was freed from and
// block the next SWITCH until something dropped it again.
await using var verifyContext = CreateContext();
@@ -937,6 +995,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(1, pkExists);
}
/// <summary>Verifies that first-write-wins insertion is still enforced after a partition switch.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task SwitchOutPartitionAsync_InsertIfNotExistsAsync_StillEnforcesFirstWriteWins_AfterSwitch()
{
@@ -959,7 +1019,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
// column instead of a dedicated unique index; if the switch had disturbed
// that key, this insert would silently produce a duplicate row and the
// count assertion below would catch it.
// C3 (Task 2.5): rebuild a sibling row with the same EventId via the factory
// Rebuild a sibling row with the same EventId via the factory
// (ErrorMessage rides in DetailsJson, so a top-level `with` no longer applies).
var dup = ScadaBridgeAuditEventFactory.Create(
channel: AuditChannel.ApiOutbound,
@@ -978,11 +1038,13 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Single(rows);
Assert.Equal(preExisting.EventId, rows[0].EventId);
// First-write-wins: the original ErrorMessage (null) survives. C5 (Task 2.5):
// First-write-wins: the original ErrorMessage (null) survives.
// ErrorMessage rides in DetailsJson — decode it to assert.
Assert.Null(AuditDetailsCodec.Deserialize(rows[0].DetailsJson).ErrorMessage);
}
/// <summary>Verifies that a partial partition-switch failure raises an exception and leaves no orphaned staging table.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task SwitchOutPartitionAsync_PartialFailure_RaisesException_LeavesNoOrphanedStaging()
{
@@ -1045,8 +1107,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
"SELECT COUNT(*) FROM sys.tables WHERE name LIKE 'AuditLog\\_Staging\\_%' ESCAPE '\\';");
Assert.Equal(0, orphanedStaging);
// And it must NOT have (re)created the non-aligned unique index: WP2.2
// deleted that rebuild entirely, so a failed switch cannot reintroduce
// And it must NOT have (re)created the non-aligned unique index: the
// design deleted that rebuild entirely, so a failed switch cannot reintroduce
// the object that blocks the NEXT switch.
var indexExists = await ScalarAsync<int>(
verifyContext,
@@ -1056,9 +1118,11 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ------------------------------------------------------------------------
// M6-T4 Bundle C: GetPartitionBoundariesOlderThanAsync
// GetPartitionBoundariesOlderThanAsync
// ------------------------------------------------------------------------
/// <summary>Verifies that only partition boundaries whose newest row is older than the threshold are returned.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetPartitionBoundariesOlderThanAsync_ReturnsBoundaries_WithMaxOccurredOlderThanThreshold()
{
@@ -1087,7 +1151,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ------------------------------------------------------------------------
// M7-T13 Bundle E: GetKpiSnapshotAsync — Health-dashboard Audit KPI tiles
// GetKpiSnapshotAsync — Health-dashboard Audit KPI tiles
// ------------------------------------------------------------------------
//
// The dashboard's "Audit volume" tile reads TotalEventsLastHour and the
@@ -1102,6 +1166,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
// narrow band centred on that anchor — no other test in this class seeds
// there, so the global count equals the seeded count for that band.
/// <summary>Verifies that the KPI snapshot reports correct totals and error counts for rows of mixed status.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetKpiSnapshotAsync_WithMixedStatusRows_ReturnsCorrectTotalsAndErrors()
{
@@ -1141,6 +1207,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(nowUtc, snapshot.AsOfUtc);
}
/// <summary>Verifies that the KPI snapshot of an empty window reports zero totals.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetKpiSnapshotAsync_EmptyWindow_ReturnsZeroTotals()
{
@@ -1163,7 +1231,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
// ------------------------------------------------------------------------
// Audit Log ParentExecutionId (Task 8): GetExecutionTreeAsync
// Audit Log ParentExecutionId: GetExecutionTreeAsync
// ------------------------------------------------------------------------
//
// GetExecutionTreeAsync walks UP from any node to the chain root, then walks
@@ -1175,6 +1243,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
// * pathological cyclic data is bounded by the MAXRECURSION guard and
// surfaces a SqlException rather than hanging
/// <summary>Verifies that the execution tree resolves a multi-level parent chain.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetExecutionTree_MultiLevelChain()
{
@@ -1267,6 +1337,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
}
}
/// <summary>Verifies that the execution tree edge scan is bounded to the root window.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task ExecutionTree_Bounds_EdgeScan_To_Root_Window()
{
@@ -1310,6 +1382,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(2, tree.Count);
}
/// <summary>Verifies that the execution tree includes a stub node for a parent with no audit rows.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetExecutionTree_StubParentNode()
{
@@ -1351,6 +1425,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
Assert.Equal(stubParentExec, child.ParentExecutionId);
}
/// <summary>Verifies that the execution tree walk respects the maximum recursion depth.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[SkippableFact]
public async Task GetExecutionTree_RespectsMaxRecursion()
{
@@ -1413,7 +1489,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
private static string NewSiteId() =>
"test-bundle-d-" + Guid.NewGuid().ToString("N").Substring(0, 8);
// C3 (Task 2.5): build the canonical ZB.MOM.WW.Audit.AuditEvent via the shared
// Build the canonical ZB.MOM.WW.Audit.AuditEvent via the shared
// factory; the repository's transitional shim decomposes it into the 24-column
// AuditLogRow on INSERT and recomposes the canonical record on QUERY.
private static AuditEvent NewEvent(