feat(audit): add SourceNode property to AuditEvent record

This commit is contained in:
Joseph Doherty
2026-05-23 15:45:31 -04:00
parent 2e10cbe42d
commit ad625eb36d
2 changed files with 32 additions and 0 deletions

View File

@@ -43,6 +43,15 @@ public sealed record AuditEvent
/// <summary>Site id where the action originated; null for central-direct events.</summary>
public string? SourceSiteId { get; init; }
/// <summary>
/// The cluster node on which the event was emitted — `node-a` / `node-b` for
/// site rows (qualified by <see cref="SourceSiteId"/>), `central-a` / `central-b`
/// for central-originated rows. Stamped by the writing node from
/// <c>INodeIdentityProvider</c>; nullable so reconciled rows from a node that
/// has since been retired don't block ingest.
/// </summary>
public string? SourceNode { get; init; }
/// <summary>Instance id where the action originated, when applicable.</summary>
public string? SourceInstanceId { get; init; }

View File

@@ -116,6 +116,29 @@ public class AuditEventTests
Assert.Null(evt.ForwardState);
}
[Fact]
public void AuditEvent_carries_SourceNode_through_init()
{
// SourceNode identifies the cluster node that emitted the event (site
// node-a/node-b or central-a/central-b). It's an additive nullable
// init-only property — defaults to null when omitted, round-trips its
// value when set, and is preserved through `with` expressions.
var evtDefault = new AuditEvent
{
EventId = Guid.NewGuid(),
OccurredAtUtc = DateTime.UtcNow,
Channel = AuditChannel.ApiOutbound,
Kind = AuditKind.ApiCall,
Status = AuditStatus.Submitted,
PayloadTruncated = false
};
Assert.Null(evtDefault.SourceNode);
var evtStamped = evtDefault with { SourceNode = "node-a" };
Assert.Equal("node-a", evtStamped.SourceNode);
Assert.Null(evtDefault.SourceNode);
}
[Fact]
public void With_ProducesNewInstance_WithSingleFieldChanged()
{