2819786f91
- Extract IWizardDraftRepository interface for testability (NSubstitute cannot mock sealed classes; the codebase uses fake-style doubles instead). - Add step-transition, pool-slot, validation, cancel/back, and render-shape tests using FakeWizardDraftRepository and FakeWizardMessenger. - Fix wizard payload persistence bug: HandleCallbackAsync and HandleTextAsync now call SavePayload after ApplyChoice/ApplyText mutations, so subsequent LoadPayload calls see the user's progress. Previously, local WizardPayload mutations were discarded and the wizard reset on every step. - CommitCurrentPoolSlot now auto-creates a slot via EnsureCurrentPoolSlot when one is missing, so the PoolSlotCapacity → waitlist click is recoverable even if the user lands on the step without a slot.
61 lines
1.6 KiB
C#
61 lines
1.6 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using GmRelay.Shared.Features.Sessions.CreateSession.Wizard;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
|
|
|
|
public sealed class WizardDraftCleanupService : BackgroundService
|
|
{
|
|
private static readonly TimeSpan TickInterval = TimeSpan.FromMinutes(1);
|
|
|
|
private readonly IWizardDraftRepository _drafts;
|
|
private readonly ILogger<WizardDraftCleanupService> _log;
|
|
|
|
public WizardDraftCleanupService(
|
|
IWizardDraftRepository drafts,
|
|
ILogger<WizardDraftCleanupService> log)
|
|
{
|
|
_drafts = drafts;
|
|
_log = log;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
using var timer = new PeriodicTimer(TickInterval);
|
|
try
|
|
{
|
|
while (await timer.WaitForNextTickAsync(stoppingToken))
|
|
{
|
|
await RunOnceAsync(stoppingToken);
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// graceful shutdown
|
|
}
|
|
}
|
|
|
|
internal async Task RunOnceAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
var deleted = await _drafts.DeleteExpiredAsync(ct);
|
|
if (deleted > 0)
|
|
{
|
|
_log.LogInformation("Wizard cleanup deleted {Count} expired drafts", deleted);
|
|
}
|
|
}
|
|
catch (OperationCanceledException) when (ct.IsCancellationRequested)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_log.LogError(ex, "Wizard cleanup tick failed");
|
|
}
|
|
}
|
|
}
|