diff --git a/tests/ScadaLink.AuditLog.Tests/Integration/ParentExecutionIdCorrelationTests.cs b/tests/ScadaLink.AuditLog.Tests/Integration/ParentExecutionIdCorrelationTests.cs index 0efc6f1..1207c88 100644 --- a/tests/ScadaLink.AuditLog.Tests/Integration/ParentExecutionIdCorrelationTests.cs +++ b/tests/ScadaLink.AuditLog.Tests/Integration/ParentExecutionIdCorrelationTests.cs @@ -277,6 +277,20 @@ public class ParentExecutionIdCorrelationTests : TestKit, IClassFixture @@ -450,6 +464,48 @@ public class ParentExecutionIdCorrelationTests : TestKit, IClassFixture + /// Polls the site SQLite hot-path until every audit + /// the routed run is expected to emit — sync ApiCall, the cached + /// CachedSubmit/ApiCallCached/CachedResolve lifecycle, + /// and NotifySend — is durably present (Pending or Forwarded). + /// + /// + /// The routed run's sync-ApiCall and NotifySend audit rows are + /// written fire-and-forget (the script call must not block on the audit + /// writer — alog.md §7), so the routed RouteToCallAsync returns + /// before the background writer loop has committed those rows. + /// NotifySend is emitted last and therefore settles last. This wait + /// asserts the specific Kinds are present, not merely a row count: a + /// bare count could be satisfied while the last-emitted NotifySend + /// row was still in flight, letting the SiteAuditTelemetryActor drain + /// only a partial snapshot and leave NotifySend stranded for a later + /// tick — the emit-vs-drain race that failed this test under full-suite load. + /// + private async Task WaitForSiteRowsPersistedAsync(SqliteAuditWriter sqliteWriter) + { + var expectedKinds = new[] + { + AuditKind.ApiCall, AuditKind.CachedSubmit, AuditKind.ApiCallCached, + AuditKind.CachedResolve, AuditKind.NotifySend, + }; + await AwaitAssertAsync( + async () => + { + var pending = await sqliteWriter.ReadPendingAsync(256); + var forwarded = await sqliteWriter.ReadForwardedAsync(256); + var kinds = pending.Concat(forwarded).Select(r => r.Kind).ToHashSet(); + var missing = expectedKinds.Where(k => !kinds.Contains(k)).ToList(); + Assert.True( + missing.Count == 0, + "Expected every routed-run audit Kind durably in SQLite; missing: " + + string.Join(", ", missing) + + $" (saw {pending.Count} Pending + {forwarded.Count} Forwarded)."); + }, + TimeSpan.FromSeconds(30), + TimeSpan.FromMilliseconds(50)); + } + /// /// Stub that always reports a /// successful delivery — a single dispatch sweep then yields one