feat(audit)!: ScadaBridge C3 — swap to canonical ZB.MOM.WW.Audit.AuditEvent across seams/emitters/DTO/redactor wiring; transitional 24-col storage shim (Task 2.5)

This commit is contained in:
Joseph Doherty
2026-06-02 12:37:50 -04:00
parent 5aaf9e2923
commit db707bb0de
127 changed files with 2240 additions and 3886 deletions
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Options;
using NSubstitute;
using ZB.MOM.WW.ScadaBridge.AuditLog.Site;
using ZB.MOM.WW.ScadaBridge.AuditLog.Site.Telemetry;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
using ZB.MOM.WW.ScadaBridge.Commons.Types;
@@ -105,18 +105,18 @@ public class SiteAuditPushFlowTests : TestKit
=> throw new NotSupportedException();
}
private static AuditEvent NewPendingEvent(Guid id) => new()
{
EventId = id,
OccurredAtUtc = new DateTime(2026, 5, 21, 9, 0, 0, DateTimeKind.Utc),
Channel = AuditChannel.ApiOutbound,
Kind = AuditKind.ApiCall,
Status = AuditStatus.Delivered,
SourceSiteId = "site-1",
Target = "ext-system-1",
PayloadTruncated = false,
ForwardState = AuditForwardState.Pending,
};
// C3 (Task 2.5): canonical record via the shared factory; ForwardState is a
// site-storage-only concern (defaulted to Pending by the SQLite writer), not a
// field on the canonical record.
private static AuditEvent NewPendingEvent(Guid id) =>
ScadaBridgeAuditEventFactory.Create(
channel: AuditChannel.ApiOutbound,
kind: AuditKind.ApiCall,
status: AuditStatus.Delivered,
eventId: id,
occurredAtUtc: new DateTime(2026, 5, 21, 9, 0, 0, DateTimeKind.Utc),
target: "ext-system-1",
sourceSiteId: "site-1");
[Fact]
public async Task SiteAuditEvent_DrainsToCentral_AndFlipsSiteRowToForwarded()
@@ -197,14 +197,16 @@ public class SiteAuditPushFlowTests : TestKit
// The site row reached AuditForwardState.Forwarded specifically —
// not merely "no longer Pending" (a Reconciled row would also leave
// ReadPendingAsync, so we assert the positive Forwarded state).
// C3 (Task 2.5): ForwardState is a site-storage-only column, no longer a
// field on the canonical record. ReadForwardedAsync only returns rows in
// the Forwarded state, so a single match here proves the row reached it.
var forwarded = await writer.ReadForwardedAsync(256, CancellationToken.None);
var row = Assert.Single(forwarded, r => r.EventId == eventId);
Assert.Equal(AuditForwardState.Forwarded, row.ForwardState);
Assert.Single(forwarded, r => r.EventId == eventId);
}, TimeSpan.FromSeconds(10), TimeSpan.FromMilliseconds(250));
// The central-persisted row carries the central-stamped IngestedAtUtc.
var ingested = centralRepo.Rows.Single(r => r.EventId == eventId);
Assert.NotNull(ingested.IngestedAtUtc);
Assert.NotNull(ingested.AsRow().IngestedAtUtc);
// Cleanup the temp SQLite file.
try { File.Delete(dbPath); } catch { /* best-effort */ }