feat(auditlog): NotifyDeliver rows carry the originating ParentExecutionId

This commit is contained in:
Joseph Doherty
2026-05-21 18:11:04 -04:00
parent c00603e2a4
commit d35551efc2
16 changed files with 2056 additions and 9 deletions

View File

@@ -61,7 +61,8 @@ public class NotifyHelperTests : TestKit, IAsyncLifetime, IDisposable
private ScriptRuntimeContext.NotifyHelper CreateHelper(
IActorRef siteCommunicationActor,
string? sourceScript = null,
Guid? executionId = null)
Guid? executionId = null,
Guid? parentExecutionId = null)
{
return new ScriptRuntimeContext.NotifyHelper(
_saf,
@@ -71,7 +72,9 @@ public class NotifyHelperTests : TestKit, IAsyncLifetime, IDisposable
sourceScript,
TimeSpan.FromSeconds(3),
NullLogger.Instance,
executionId ?? Guid.NewGuid());
executionId ?? Guid.NewGuid(),
auditWriter: null,
parentExecutionId: parentExecutionId);
}
[Fact]
@@ -156,6 +159,44 @@ public class NotifyHelperTests : TestKit, IAsyncLifetime, IDisposable
Assert.Equal(executionId, payload!.OriginExecutionId);
}
[Fact]
public async Task Send_StampsParentExecutionId_OnTheNotificationSubmitPayload()
{
// Audit Log ParentExecutionId (Task 7): for an inbound-API-routed run,
// Notify.Send must stamp the routed run's parent ExecutionId onto the
// NotificationSubmit so it rides inside the serialized S&F payload to
// central, where the dispatcher echoes it onto the NotifyDeliver rows.
// This is the SAME parent id stamped onto the site-emitted NotifySend row.
var parentExecutionId = Guid.NewGuid();
var commProbe = CreateTestProbe();
var notify = CreateHelper(commProbe.Ref, parentExecutionId: parentExecutionId);
var notificationId = await notify.To("Operators").Send("Pump alarm", "Pump 3 tripped");
var buffered = await _saf.GetMessageByIdAsync(notificationId);
Assert.NotNull(buffered);
var payload = JsonSerializer.Deserialize<NotificationSubmit>(buffered!.PayloadJson);
Assert.NotNull(payload);
Assert.Equal(parentExecutionId, payload!.OriginParentExecutionId);
}
[Fact]
public async Task Send_NonRoutedRun_LeavesOriginParentExecutionIdNull()
{
// Non-routed runs have no parent execution — OriginParentExecutionId
// stays null on the NotificationSubmit payload.
var commProbe = CreateTestProbe();
var notify = CreateHelper(commProbe.Ref, parentExecutionId: null);
var notificationId = await notify.To("Operators").Send("Pump alarm", "Pump 3 tripped");
var buffered = await _saf.GetMessageByIdAsync(notificationId);
Assert.NotNull(buffered);
var payload = JsonSerializer.Deserialize<NotificationSubmit>(buffered!.PayloadJson);
Assert.NotNull(payload);
Assert.Null(payload!.OriginParentExecutionId);
}
[Fact]
public async Task Send_WhenHelperHasNoSourceScript_LeavesSourceScriptNull()
{