using Akka.Actor; using Akka.TestKit.Xunit2; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; using ScadaLink.Commons.Entities.Audit; using ScadaLink.Commons.Entities.Notifications; using ScadaLink.Commons.Interfaces.Repositories; using ScadaLink.Commons.Interfaces.Services; namespace ScadaLink.NotificationOutbox.Tests; /// /// M4 Bundle B (B1) — verifies accepts an /// at construction so subsequent bundle tasks /// (B2/B3) can route attempt + terminal lifecycle events through the central /// direct-write audit path. /// public class NotificationOutboxActorAuditInjectionTests : TestKit { private static IServiceProvider BuildEmptyProvider() { var services = new ServiceCollection(); services.AddScoped(_ => Substitute.For()); services.AddScoped(_ => Substitute.For()); return services.BuildServiceProvider(); } /// /// Inline NoOp writer that records calls — used to assert later tasks emit /// events without depending on a concrete CentralAuditWriter. /// private sealed class RecordingCentralAuditWriter : ICentralAuditWriter { public List Events { get; } = new(); public Task WriteAsync(AuditEvent evt, CancellationToken ct = default) { lock (Events) { Events.Add(evt); } return Task.CompletedTask; } } [Fact] public void Actor_ConstructedWith_ICentralAuditWriter_NoException() { var writer = new RecordingCentralAuditWriter(); // Long dispatch interval so PreStart's timer never fires during the test. var options = new NotificationOutboxOptions { DispatchInterval = TimeSpan.FromHours(1) }; var actor = Sys.ActorOf(Props.Create(() => new NotificationOutboxActor( BuildEmptyProvider(), options, writer, NullLogger.Instance))); Assert.NotNull(actor); // No event has been emitted yet — the writer is purely injected at this stage. lock (writer.Events) { Assert.Empty(writer.Events); } } [Fact] public void Actor_NullAuditWriter_Throws() { var options = new NotificationOutboxOptions { DispatchInterval = TimeSpan.FromHours(1) }; Assert.Throws(() => new NotificationOutboxActor( BuildEmptyProvider(), options, auditWriter: null!, NullLogger.Instance)); } }