feat(auditlog): site script-side emitters stamp ExecutionId
Move the per-script-execution Guid on ScriptRuntimeContext from _auditCorrelationId to _executionId, and stamp it into the dedicated AuditEvent.ExecutionId column on every script-side audit row: - Sync ApiCall / DbWrite: ExecutionId set; CorrelationId reverts to null (a sync one-shot call has no operation lifecycle). - Cached-call script-side rows (CachedSubmit, immediate-completion ApiCallCached + CachedResolve) and NotifySend: ExecutionId set; CorrelationId unchanged (per-operation TrackedOperationId / NotificationId). Renames the threaded ctor param/field across ExternalSystemHelper, DatabaseHelper, AuditingDbConnection and AuditingDbCommand, and threads the id through NotifyHelper/NotifyTarget. The S&F retry-loop cached rows (CachedCallLifecycleBridge) are out of scope here.
This commit is contained in:
@@ -46,27 +46,27 @@ public class ExternalSystemCallAuditEmissionTests
|
||||
private const string SourceScript = "ScriptActor:CheckPressure";
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log #23: a fixed execution-wide correlation id used by the
|
||||
/// default <see cref="CreateHelper(IExternalSystemClient, IAuditWriter?)"/>
|
||||
/// Audit Log #23: a fixed per-execution id used by the default
|
||||
/// <see cref="CreateHelper(IExternalSystemClient, IAuditWriter?)"/>
|
||||
/// overload so assertions can compare against a known value.
|
||||
/// </summary>
|
||||
private static readonly Guid TestCorrelationId = Guid.NewGuid();
|
||||
private static readonly Guid TestExecutionId = Guid.NewGuid();
|
||||
|
||||
private static ScriptRuntimeContext.ExternalSystemHelper CreateHelper(
|
||||
IExternalSystemClient client,
|
||||
IAuditWriter? auditWriter)
|
||||
=> CreateHelper(client, auditWriter, TestCorrelationId);
|
||||
=> CreateHelper(client, auditWriter, TestExecutionId);
|
||||
|
||||
private static ScriptRuntimeContext.ExternalSystemHelper CreateHelper(
|
||||
IExternalSystemClient client,
|
||||
IAuditWriter? auditWriter,
|
||||
Guid correlationId)
|
||||
Guid executionId)
|
||||
{
|
||||
return new ScriptRuntimeContext.ExternalSystemHelper(
|
||||
client,
|
||||
InstanceName,
|
||||
NullLogger.Instance,
|
||||
correlationId,
|
||||
executionId,
|
||||
auditWriter,
|
||||
SiteId,
|
||||
SourceScript);
|
||||
@@ -225,47 +225,54 @@ public class ExternalSystemCallAuditEmissionTests
|
||||
Assert.Equal(SourceScript, evt.SourceScript);
|
||||
// Outbound channel: Actor carries the calling script identity.
|
||||
Assert.Equal(SourceScript, evt.Actor);
|
||||
// Audit Log #23: the sync ApiCall row now carries the execution-wide
|
||||
// correlation id the helper was constructed with.
|
||||
Assert.Equal(TestCorrelationId, evt.CorrelationId);
|
||||
// Audit Log #23: the sync ApiCall row carries the per-execution id the
|
||||
// helper was constructed with in ExecutionId. CorrelationId is null —
|
||||
// a sync one-shot call has no operation lifecycle.
|
||||
Assert.Equal(TestExecutionId, evt.ExecutionId);
|
||||
Assert.Null(evt.CorrelationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Call_SyncApiCall_StampsExecutionCorrelationId()
|
||||
public async Task Call_SyncApiCall_StampsExecutionId_AndNullCorrelationId()
|
||||
{
|
||||
var client = new Mock<IExternalSystemClient>();
|
||||
client
|
||||
.Setup(c => c.CallAsync("ERP", "GetOrder", It.IsAny<IReadOnlyDictionary<string, object?>?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ExternalCallResult(true, "{}", null));
|
||||
var writer = new CapturingAuditWriter();
|
||||
var correlationId = Guid.NewGuid();
|
||||
var executionId = Guid.NewGuid();
|
||||
|
||||
var helper = CreateHelper(client.Object, writer, correlationId);
|
||||
var helper = CreateHelper(client.Object, writer, executionId);
|
||||
await helper.Call("ERP", "GetOrder");
|
||||
|
||||
var evt = Assert.Single(writer.Events);
|
||||
Assert.Equal(correlationId, evt.CorrelationId);
|
||||
Assert.Equal(executionId, evt.ExecutionId);
|
||||
// Sync one-shot call: CorrelationId is null (no operation lifecycle).
|
||||
Assert.Null(evt.CorrelationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Call_TwoCallsOnSameHelper_ShareTheSameCorrelationId()
|
||||
public async Task Call_TwoCallsOnSameHelper_ShareTheSameExecutionId()
|
||||
{
|
||||
var client = new Mock<IExternalSystemClient>();
|
||||
client
|
||||
.Setup(c => c.CallAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IReadOnlyDictionary<string, object?>?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ExternalCallResult(true, "{}", null));
|
||||
var writer = new CapturingAuditWriter();
|
||||
var correlationId = Guid.NewGuid();
|
||||
var executionId = Guid.NewGuid();
|
||||
|
||||
var helper = CreateHelper(client.Object, writer, correlationId);
|
||||
var helper = CreateHelper(client.Object, writer, executionId);
|
||||
await helper.Call("ERP", "GetOrder");
|
||||
await helper.Call("ERP", "GetCustomer");
|
||||
|
||||
Assert.Equal(2, writer.Events.Count);
|
||||
// Both sync ApiCall rows from one execution carry the same id.
|
||||
Assert.Equal(correlationId, writer.Events[0].CorrelationId);
|
||||
Assert.Equal(correlationId, writer.Events[1].CorrelationId);
|
||||
Assert.Equal(writer.Events[0].CorrelationId, writer.Events[1].CorrelationId);
|
||||
// Both sync ApiCall rows from one execution carry the same ExecutionId.
|
||||
Assert.Equal(executionId, writer.Events[0].ExecutionId);
|
||||
Assert.Equal(executionId, writer.Events[1].ExecutionId);
|
||||
Assert.Equal(writer.Events[0].ExecutionId, writer.Events[1].ExecutionId);
|
||||
// Neither sync call carries a CorrelationId.
|
||||
Assert.Null(writer.Events[0].CorrelationId);
|
||||
Assert.Null(writer.Events[1].CorrelationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user