using System; using System.Threading; using System.Threading.Tasks; using GmRelay.Bot.Features.Sessions.CreateSession.Wizard; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; using NSubstitute.ExceptionExtensions; using SharedDraft = GmRelay.Shared.Features.Sessions.CreateSession.Wizard; namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard; /// /// Verifies the cleanup background service: each tick should call the draft /// repository to delete expired drafts and must not propagate repository /// failures (a transient DB blip should not bring the worker down). /// public sealed class WizardDraftCleanupServiceTests { [Fact] public async Task RunOnceAsync_DeletesExpiredDrafts() { var drafts = Substitute.For(); drafts.DeleteExpiredAsync(Arg.Any()).Returns(7); var sut = new WizardDraftCleanupService(drafts, NullLogger.Instance); await sut.RunOnceAsync(CancellationToken.None); await drafts.Received(1).DeleteExpiredAsync(Arg.Any()); } [Fact] public async Task RunOnceAsync_OnRepositoryError_DoesNotThrow() { var drafts = Substitute.For(); drafts.DeleteExpiredAsync(Arg.Any()) .ThrowsAsync(new InvalidOperationException("boom")); var sut = new WizardDraftCleanupService(drafts, NullLogger.Instance); // Should swallow the exception — cleanup is best-effort. await sut.RunOnceAsync(CancellationToken.None); await drafts.Received(1).DeleteExpiredAsync(Arg.Any()); } }