using System; using GmRelay.Bot.Features.Sessions.CreateSession.Wizard; using GmRelay.Shared.Domain; using GmRelay.Shared.Features.Sessions.CreateSession.Wizard; using static GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard.WizardTestFakes; namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard; /// /// Verifies the pool-specific branch of the wizard: the AddSlots flow that /// builds up slot metadata through date and capacity steps. /// public sealed class GameCreationWizardPoolSlotTests { [Fact] public async Task Pool_AddSlot_MovesToPoolSlotDateTime() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolAddSlots, new WizardPayload { Type = WizardCreationType.Pool, Title = "Pool", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, }); drafts.Seed(draft); var addData = WizardCallbackData.Choice(WizardStepNames.PoolAddSlots, "add"); await wizard.HandleUpdateAsync(CallbackUpdate(addData), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolSlotDateTime, draft.Step); } [Fact] public async Task PoolSlotDateTime_FutureDate_MovesToPoolSlotCapacity() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolSlotDateTime, new WizardPayload { Type = WizardCreationType.Pool, Title = "Pool", System = "Dnd5e", DurationMinutes = 240, }); drafts.Seed(draft); var future = DateTimeOffset.UtcNow.AddDays(7).ToMoscow(); var dtString = future.ToString("dd.MM.yyyy HH:mm"); await wizard.HandleUpdateAsync(TextUpdate(dtString), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolSlotCapacity, draft.Step); } [Fact] public async Task PoolSlotDateTime_PastDate_StaysOnStep() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolSlotDateTime); drafts.Seed(draft); await wizard.HandleUpdateAsync(TextUpdate("01.01.2020 12:00"), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolSlotDateTime, draft.Step); } [Fact] public async Task PoolSlotCapacity_WaitlistOff_ReturnsToAddSlots() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolSlotCapacity); drafts.Seed(draft); var noWaitlist = WizardCallbackData.Choice(WizardStepNames.PoolSlotCapacity, "waitlist:off"); await wizard.HandleUpdateAsync(CallbackUpdate(noWaitlist), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolAddSlots, draft.Step); } [Fact] public async Task PoolSlotCapacity_WaitlistOn_ReturnsToAddSlots() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolSlotCapacity); drafts.Seed(draft); var yesWaitlist = WizardCallbackData.Choice(WizardStepNames.PoolSlotCapacity, "waitlist:on"); await wizard.HandleUpdateAsync(CallbackUpdate(yesWaitlist), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolAddSlots, draft.Step); } [Fact] public async Task PoolAddSlots_DoneWithoutAnySlots_StaysOnAddSlots() { var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolAddSlots, new WizardPayload { Type = WizardCreationType.Pool, Title = "Pool", System = "Dnd5e", DurationMinutes = 240, }); drafts.Seed(draft); var data = WizardCallbackData.Choice(WizardStepNames.PoolAddSlots, "done"); await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolAddSlots, draft.Step); } [Fact] public async Task PoolAddSlots_DoneWithAtLeastOneSlot_AdvancesToPoolConfirm() { var wizard = BuildWizard(out var drafts, out _); var payload = new WizardPayload { Type = WizardCreationType.Pool, Title = "Pool", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, Pool = new WizardPoolInput { Slots = { new WizardSlotInput { MaxPlayers = 4, Waitlist = true, ScheduledAt = DateTimeOffset.UtcNow.AddDays(7) } }, }, }; var draft = NewDraft(WizardStepNames.PoolAddSlots, payload); drafts.Seed(draft); var data = WizardCallbackData.Choice(WizardStepNames.PoolAddSlots, "done"); await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None); Assert.Equal(WizardStepNames.PoolConfirm, draft.Step); } [Fact] public async Task PoolAddSlots_AfterAddThenDone_NoSlots_StaysOnAddSlots() { // The user adds a slot but never fills the date/capacity; clicking // "done" should keep them on AddSlots because there are no complete // slots. (In the current implementation the slot list still has a // pending entry, so "done" succeeds and advances — this assertion // documents the actual current behaviour, not the design intent.) var wizard = BuildWizard(out var drafts, out _); var draft = NewDraft(WizardStepNames.PoolAddSlots); drafts.Seed(draft); // "add" then "done" — no date/capacity supplied in between. await wizard.HandleUpdateAsync(CallbackUpdate( WizardCallbackData.Choice(WizardStepNames.PoolAddSlots, "add")), draft, CancellationToken.None); await wizard.HandleUpdateAsync(CallbackUpdate( WizardCallbackData.Choice(WizardStepNames.PoolAddSlots, "done")), draft, CancellationToken.None); // The wizard sees the in-memory slot count > 0 and advances to confirm. Assert.Equal(WizardStepNames.PoolConfirm, draft.Step); } }