feat(audit): stamp SourceNode at CentralAuditWriter + persist via AuditLogRepository
CentralAuditWriter injects INodeIdentityProvider and stamps the event before handing to the repository. AuditLogRepository.InsertIfNotExistsAsync now includes SourceNode in the INSERT column list. Caller-provided value wins (supports any future direct-write callsite that already has its own node id).
This commit is contained in:
@@ -50,6 +50,56 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
Assert.Equal(evt.EventId, loaded[0].EventId);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task InsertIfNotExistsAsync_PersistsSourceNode()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteId = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
var evt = NewEvent(
|
||||
siteId,
|
||||
occurredAtUtc: new DateTime(2026, 5, 20, 10, 0, 0, DateTimeKind.Utc),
|
||||
sourceNode: "central-a");
|
||||
await repo.InsertIfNotExistsAsync(evt);
|
||||
|
||||
await using var readContext = CreateContext();
|
||||
var loaded = await readContext.Set<AuditEvent>()
|
||||
.Where(e => e.SourceSiteId == siteId)
|
||||
.ToListAsync();
|
||||
|
||||
Assert.Single(loaded);
|
||||
Assert.Equal("central-a", loaded[0].SourceNode);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task InsertIfNotExistsAsync_PersistsNullSourceNode()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteId = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
// Caller passes null SourceNode (e.g. an unconfigured node) — the
|
||||
// column should persist as NULL, not as the empty string.
|
||||
var evt = NewEvent(
|
||||
siteId,
|
||||
occurredAtUtc: new DateTime(2026, 5, 20, 10, 0, 0, DateTimeKind.Utc),
|
||||
sourceNode: null);
|
||||
await repo.InsertIfNotExistsAsync(evt);
|
||||
|
||||
await using var readContext = CreateContext();
|
||||
var loaded = await readContext.Set<AuditEvent>()
|
||||
.Where(e => e.SourceSiteId == siteId)
|
||||
.ToListAsync();
|
||||
|
||||
Assert.Single(loaded);
|
||||
Assert.Null(loaded[0].SourceNode);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task InsertIfNotExistsAsync_DuplicateEventId_IsNoOp_NoExceptionNoDuplicate()
|
||||
{
|
||||
@@ -962,7 +1012,8 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
AuditStatus status = AuditStatus.Delivered,
|
||||
string? errorMessage = null,
|
||||
Guid? executionId = null,
|
||||
Guid? parentExecutionId = null) =>
|
||||
Guid? parentExecutionId = null,
|
||||
string? sourceNode = null) =>
|
||||
new()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
@@ -971,6 +1022,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
Kind = kind,
|
||||
Status = status,
|
||||
SourceSiteId = siteId,
|
||||
SourceNode = sourceNode,
|
||||
ErrorMessage = errorMessage,
|
||||
ExecutionId = executionId,
|
||||
ParentExecutionId = parentExecutionId,
|
||||
|
||||
Reference in New Issue
Block a user