feat(notification-outbox): route NotificationSubmit to the outbox actor

This commit is contained in:
Joseph Doherty
2026-05-19 02:38:04 -04:00
parent b88c75c116
commit 2ff62a2ceb
2 changed files with 143 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ using ScadaLink.Commons.Messages.Communication;
using ScadaLink.Commons.Messages.Deployment;
using ScadaLink.Commons.Messages.DebugView;
using ScadaLink.Commons.Messages.Health;
using ScadaLink.Commons.Messages.Notification;
using ScadaLink.Communication.Actors;
using ScadaLink.HealthMonitoring;
using Akka.TestKit;
@@ -251,6 +252,75 @@ public class CentralCommunicationActorTests : TestKit
Assert.Equal("dep1", ((DeployInstanceCommand)msg.Message).DeploymentId);
}
private NotificationSubmit CreateSubmit(string id = "notif1") =>
new(id, "ops-list", "Subject", "Body", "site1", "inst1", "script.cs", DateTimeOffset.UtcNow);
[Fact]
public void NotificationSubmit_ForwardedToOutboxProxy_AckRoutesBackToSite()
{
var (actor, _, _) = CreateActorWithMockRepo();
var outboxProbe = CreateTestProbe();
actor.Tell(new RegisterNotificationOutbox(outboxProbe.Ref));
// A second probe stands in for the site's ClusterClient (the original Sender).
var siteProbe = CreateTestProbe();
var submit = CreateSubmit();
actor.Tell(submit, siteProbe.Ref);
// The outbox proxy receives the NotificationSubmit with the site as the sender,
// so an ack it sends routes straight back to the site, not the central actor.
outboxProbe.ExpectMsg<NotificationSubmit>(m => m.NotificationId == "notif1");
outboxProbe.Reply(new NotificationSubmitAck("notif1", Accepted: true, Error: null));
siteProbe.ExpectMsg<NotificationSubmitAck>(a => a.NotificationId == "notif1" && a.Accepted);
}
[Fact]
public void NotificationStatusQuery_ForwardedToOutboxProxy_ResponseRoutesBackToSite()
{
var (actor, _, _) = CreateActorWithMockRepo();
var outboxProbe = CreateTestProbe();
actor.Tell(new RegisterNotificationOutbox(outboxProbe.Ref));
var siteProbe = CreateTestProbe();
var query = new NotificationStatusQuery("corr1", "notif1");
actor.Tell(query, siteProbe.Ref);
outboxProbe.ExpectMsg<NotificationStatusQuery>(m => m.CorrelationId == "corr1");
outboxProbe.Reply(new NotificationStatusResponse(
"corr1", Found: true, Status: "Delivered", RetryCount: 0,
LastError: null, DeliveredAt: DateTimeOffset.UtcNow));
siteProbe.ExpectMsg<NotificationStatusResponse>(r => r.CorrelationId == "corr1" && r.Found);
}
[Fact]
public void NotificationSubmit_NoOutboxConfigured_RepliesNonAccepted()
{
var (actor, _, _) = CreateActorWithMockRepo();
// No RegisterNotificationOutbox sent — the proxy is null.
var submit = CreateSubmit();
actor.Tell(submit);
var ack = ExpectMsg<NotificationSubmitAck>();
Assert.Equal("notif1", ack.NotificationId);
Assert.False(ack.Accepted);
Assert.NotNull(ack.Error);
}
[Fact]
public void NotificationStatusQuery_NoOutboxConfigured_RepliesNotFound()
{
var (actor, _, _) = CreateActorWithMockRepo();
// No RegisterNotificationOutbox sent — the proxy is null.
var query = new NotificationStatusQuery("corr1", "notif1");
actor.Tell(query);
var response = ExpectMsg<NotificationStatusResponse>();
Assert.Equal("corr1", response.CorrelationId);
Assert.False(response.Found);
}
[Fact]
public void BothContactPoints_UsedInSingleClient()
{