Files
GmRelayBot/tests/GmRelay.Bot.Tests/Features/Sessions/CreateSession/Wizard/UpdateRouterResetsDraftOnStaleCommandTests.cs
T

97 lines
3.9 KiB
C#

using System.Threading;
using System.Threading.Tasks;
using GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
using GmRelay.Bot.Infrastructure.Telegram;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using Telegram.Bot;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using BotCreateSessionHandler = GmRelay.Bot.Features.Sessions.CreateSession.CreateSessionHandler;
using static GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard.WizardTestFakes;
namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard;
/// <summary>
/// When the user sends <c>/newsession</c> while a non-expired draft already
/// exists, the router delegates the update to the wizard (the wizard owns
/// every update while a draft is active). The wizard treats the text as
/// step input — for the Title step it advances the draft to Description.
/// This is the observable contract that this test pins down.
/// </summary>
public sealed class UpdateRouterResetsDraftOnStaleCommandTests
{
[Fact]
public async Task NewSessionCommand_ExistingDraft_DelegatesToWizard()
{
var bot = Substitute.For<ITelegramBotClient>();
var (sut, drafts, messenger) = BuildRouter(bot);
var draft = NewDraft(WizardStepNames.Title);
drafts.Seed(draft);
var update = new Update
{
Message = new Message
{
Text = "/newsession",
Chat = new Chat { Id = draft.ChatId },
From = new User { Id = draft.OwnerTelegramId, FirstName = "GM" },
},
};
await sut.RouteAsync(update, CancellationToken.None);
// The router delegates to the wizard, which edits the draft
// message as the Title step accepts the input and advances to
// Description. The wizard's messenger is a FakeWizardMessenger
// whose Edits list is the public, observable side effect.
Assert.NotEmpty(messenger.Edits);
// The bot.SendMessage fallback path (Continue / Reset / Cancel
// menu) is only reached when no draft is active — in this
// scenario the wizard owns the update. We assert it was NOT
// taken here.
await bot.DidNotReceiveWithAnyArgs().SendRequest(default(IRequest<Message>)!, default);
}
private static (UpdateRouter sut, FakeWizardDraftRepository drafts, FakeWizardMessenger messenger) BuildRouter(
ITelegramBotClient bot)
{
var drafts = new FakeWizardDraftRepository();
var messenger = new FakeWizardMessenger();
var wizard = new GameCreationWizard(drafts, messenger, NullLogger<GameCreationWizard>.Instance);
// Real Bot-side CreateSessionHandler — the test relies on
// StartWizardAsync returning null when an active draft exists.
// We pass null! for the shared handler since the active-draft
// path never touches it.
var createSessionHandler = new BotCreateSessionHandler(
drafts,
shared: null!,
messenger,
NullLogger<BotCreateSessionHandler>.Instance);
var sut = new UpdateRouter(
rsvpHandler: null!,
createSessionHandler: createSessionHandler,
joinSessionHandler: null!,
leaveSessionHandler: null!,
promoteWaitlistedPlayerHandler: null!,
cancelSessionHandler: null!,
deleteSessionHandler: null!,
listSessionsHandler: null!,
exportCalendarHandler: null!,
initiateRescheduleHandler: null!,
rescheduleTimeInputHandler: null!,
rescheduleVoteHandler: null!,
wizard: wizard,
drafts: drafts,
bot: bot,
configuration: Substitute.For<IConfiguration>(),
logger: NullLogger<UpdateRouter>.Instance);
return (sut, drafts, messenger);
}
}