diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Events/EventPublisherTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Events/EventPublisherTests.cs index 784e4242..a2c84bc5 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Events/EventPublisherTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Events/EventPublisherTests.cs @@ -11,6 +11,7 @@ using Cleanuparr.Persistence.Providers; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Time.Testing; using NSubstitute; using NSubstitute.ExceptionExtensions; using Shouldly; @@ -1162,6 +1163,18 @@ public class EventPublisherTests : IDisposable "Radarr Main", TimeSpan.FromHours(5.5), 120, 14, 0); } + private EventPublisher CreatePublisher(TimeProvider timeProvider) + { + return new EventPublisher( + _context, + _eventNotifier, + Substitute.For>(), + _notificationPublisher, + _dryRunInterceptor, + new SqliteDatabaseProvider(), + timeProvider); + } + [Fact] public async Task PublishSeekerCycleCompleted_CalledTwiceForSameCycle_PublishesOnce() { @@ -1178,6 +1191,54 @@ public class EventPublisherTests : IDisposable Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } + [Fact] + public async Task PublishSeekerCycleCompleted_WhenCycleAlreadyNotified_PublishesNothing() + { + // Arrange + SeekerCycle cycle = CreateCompletedCycle(); + cycle.NotifiedAt = new DateTimeOffset(2026, 1, 2, 0, 0, 0, TimeSpan.Zero); + + // Act + await _publisher.PublishSeekerCycleCompleted(cycle, "Radarr Main"); + + // Assert + (await _context.Events.CountAsync(e => e.EventType == EventType.SeekerCycleCompleted)).ShouldBe(0); + await _notificationPublisher.DidNotReceive().NotifySeekerCycleCompleted( + Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Fact] + public async Task PublishSeekerCycleCompleted_SetsNotifiedAtToCurrentTime() + { + // Arrange + FakeTimeProvider timeProvider = new(new DateTimeOffset(2026, 3, 1, 12, 0, 0, TimeSpan.Zero)); + EventPublisher publisher = CreatePublisher(timeProvider); + SeekerCycle cycle = CreateCompletedCycle(); + + // Act + await publisher.PublishSeekerCycleCompleted(cycle, "Radarr Main"); + + // Assert + cycle.NotifiedAt.ShouldBe(timeProvider.GetUtcNow()); + } + + [Fact] + public async Task PublishSeekerCycleCompleted_WithoutCompletedAt_UsesCurrentTimeForDuration() + { + // Arrange + SeekerCycle cycle = CreateCompletedCycle(); + cycle.CompletedAt = null; + FakeTimeProvider timeProvider = new(cycle.StartedAt.AddHours(2)); + EventPublisher publisher = CreatePublisher(timeProvider); + + // Act + await publisher.PublishSeekerCycleCompleted(cycle, "Radarr Main"); + + // Assert + await _notificationPublisher.Received(1).NotifySeekerCycleCompleted( + "Radarr Main", TimeSpan.FromHours(2), 120, 14, 0); + } + [Fact] public async Task PublishSeekerCycleCompleted_CountsOnlyGrabbedUpgradeSearchesOfThisCycle() {