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:
+23
-23
@@ -1,14 +1,14 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Entities;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Schema-level tests for <see cref="AuditLogEntityTypeConfiguration"/> (#23 M1 Bundle B).
|
||||
/// Verifies that <see cref="AuditEvent"/> maps to the AuditLog table with the
|
||||
/// Verifies that <see cref="AuditLogRow"/> maps to the AuditLog table with the
|
||||
/// PK, property set, column types/lengths, and five named indexes specified in alog.md §4.
|
||||
/// Inspects EF model metadata via the existing in-memory SQLite test context — no
|
||||
/// database round-trips required.
|
||||
@@ -34,7 +34,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
// Composite PK {EventId, OccurredAtUtc} is required by the partitioned
|
||||
// AuditLog table — the clustered key must include the partition column
|
||||
// (OccurredAtUtc) so each row can be located in its partition (#23 Bundle C).
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
|
||||
Assert.NotNull(entity);
|
||||
Assert.Equal("AuditLog", entity!.GetTableName());
|
||||
@@ -43,7 +43,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
Assert.NotNull(pk);
|
||||
|
||||
var pkPropertyNames = pk!.Properties.Select(p => p.Name).ToArray();
|
||||
Assert.Equal(new[] { nameof(AuditEvent.EventId), nameof(AuditEvent.OccurredAtUtc) }, pkPropertyNames);
|
||||
Assert.Equal(new[] { nameof(AuditLogRow.EventId), nameof(AuditLogRow.OccurredAtUtc) }, pkPropertyNames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -52,7 +52,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
// EventId remains globally unique (the idempotency key for
|
||||
// InsertIfNotExistsAsync, per M1-T8) via a dedicated unique index that
|
||||
// is independent of the composite PK.
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var eventIdIndex = entity!.GetIndexes()
|
||||
@@ -62,20 +62,20 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
Assert.True(eventIdIndex!.IsUnique);
|
||||
|
||||
var indexedProperty = Assert.Single(eventIdIndex.Properties);
|
||||
Assert.Equal(nameof(AuditEvent.EventId), indexedProperty.Name);
|
||||
Assert.Equal(nameof(AuditLogRow.EventId), indexedProperty.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_HasExpectedPropertyCount()
|
||||
{
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var properties = entity!.GetProperties()
|
||||
.Where(p => !p.IsShadowProperty())
|
||||
.ToList();
|
||||
|
||||
// AuditEvent record exposes 24 init-only properties (alog.md §4 plus the
|
||||
// AuditLogRow record exposes 24 init-only properties (alog.md §4 plus the
|
||||
// additive ExecutionId universal correlation column, its ParentExecutionId
|
||||
// sibling, and the SourceNode-stamping column).
|
||||
Assert.Equal(24, properties.Count);
|
||||
@@ -84,7 +84,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
[Fact]
|
||||
public void Configure_ExpectedIndexes_WithCorrectNames()
|
||||
{
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var indexNames = entity!.GetIndexes()
|
||||
@@ -115,13 +115,13 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(AuditEvent.Channel))]
|
||||
[InlineData(nameof(AuditEvent.Kind))]
|
||||
[InlineData(nameof(AuditEvent.Status))]
|
||||
[InlineData(nameof(AuditEvent.ForwardState))]
|
||||
[InlineData(nameof(AuditLogRow.Channel))]
|
||||
[InlineData(nameof(AuditLogRow.Kind))]
|
||||
[InlineData(nameof(AuditLogRow.Status))]
|
||||
[InlineData(nameof(AuditLogRow.ForwardState))]
|
||||
public void Configure_EnumColumns_StoredAsVarchar32(string propertyName)
|
||||
{
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var property = entity!.FindProperty(propertyName);
|
||||
@@ -136,7 +136,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
[Fact]
|
||||
public async Task Configure_UtcConverter_HydratesOccurredAtUtcAsKindUtc()
|
||||
{
|
||||
// Insert an AuditEvent with an Unspecified-Kind DateTime, then re-read
|
||||
// Insert an AuditLogRow with an Unspecified-Kind DateTime, then re-read
|
||||
// it in a fresh context. The UtcConverter on the OccurredAtUtc /
|
||||
// IngestedAtUtc columns must re-tag the round-tripped value as
|
||||
// DateTimeKind.Utc. Without the converter the SQLite (and on production
|
||||
@@ -147,10 +147,10 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
var eventId = Guid.NewGuid();
|
||||
var siteId = "test-" + Guid.NewGuid().ToString("N").Substring(0, 8);
|
||||
|
||||
var evt = new AuditEvent
|
||||
var evt = new AuditLogRow
|
||||
{
|
||||
EventId = eventId,
|
||||
// The AuditEvent record's init-setter (Commons-019 resolution)
|
||||
// The AuditLogRow record's init-setter (Commons-019 resolution)
|
||||
// re-tags Unspecified values as Utc on assignment, so the value EF
|
||||
// ultimately writes already has Kind=Utc. The converter's job is
|
||||
// to keep the Kind tag on the READ path, which the assertions
|
||||
@@ -163,14 +163,14 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
SourceSiteId = siteId,
|
||||
};
|
||||
|
||||
_context.Set<AuditEvent>().Add(evt);
|
||||
_context.Set<AuditLogRow>().Add(evt);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Detach the tracked entity and re-read in a fresh query so we exercise
|
||||
// the actual hydrate path, not the change-tracker cache.
|
||||
_context.ChangeTracker.Clear();
|
||||
|
||||
var loaded = await _context.Set<AuditEvent>()
|
||||
var loaded = await _context.Set<AuditLogRow>()
|
||||
.AsNoTracking()
|
||||
.Where(e => e.SourceSiteId == siteId)
|
||||
.SingleAsync();
|
||||
@@ -192,14 +192,14 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
// future config refactor accidentally removing the HasConversion calls.
|
||||
// The converter type itself is internal to the configuration, so we
|
||||
// just assert SOME converter is present on each *Utc DateTime column.
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var occurredAt = entity!.FindProperty(nameof(AuditEvent.OccurredAtUtc));
|
||||
var occurredAt = entity!.FindProperty(nameof(AuditLogRow.OccurredAtUtc));
|
||||
Assert.NotNull(occurredAt);
|
||||
Assert.NotNull(occurredAt!.GetValueConverter());
|
||||
|
||||
var ingestedAt = entity.FindProperty(nameof(AuditEvent.IngestedAtUtc));
|
||||
var ingestedAt = entity.FindProperty(nameof(AuditLogRow.IngestedAtUtc));
|
||||
Assert.NotNull(ingestedAt);
|
||||
Assert.NotNull(ingestedAt!.GetValueConverter());
|
||||
}
|
||||
@@ -207,7 +207,7 @@ public class AuditLogEntityTypeConfigurationTests : IDisposable
|
||||
[Fact]
|
||||
public void Configure_FilteredIndexes_HaveExpectedFilters()
|
||||
{
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditEvent));
|
||||
var entity = _context.Model.FindEntityType(typeof(AuditLogRow));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var correlationIdx = entity!.GetIndexes()
|
||||
|
||||
Reference in New Issue
Block a user