refactor(wizard): move core to Shared, add IWizardMessenger contract (issue #112)
Moves the game-creation wizard state machine, view builder, and platform-neutral contracts (callback data, step names, storage exception, club option, step limits) from GmRelay.Bot to GmRelay.Shared. Telegram continues to work through a new TelegramWizardMessenger implementing IWizardMessenger and a WizardInteractionMapper that converts Update → WizardInteraction. Wires the new platform column on wizard_drafts (V032 migration) and switches chat/owner/thread/message ids to TEXT so the same table can hold Discord snowflakes later. - GameCreationWizard: now in Shared, takes IWizardMessenger + IWizardDraftRepository, dispatches on WizardInteraction. - New IWizardMessenger contract with Edit/Send/Answer/GetOwnerClubs (returns string ids so Telegram longs and Discord snowflakes both fit). - New WizardStepViewBuilder in Shared returns (text, IReadOnlyList<WizardAction>); TelegramWizardMessenger renders actions into InlineKeyboardMarkup via a new Bot-side ToInlineKeyboard helper. - New WizardInteractionMapper in Bot (5-case test) converts Telegram Update to WizardInteraction. - WizardDraft gains a Platform column; ChatId/MessageThreadId/OwnerId/ DraftMessageId switched to string. V032 migrates existing rows and rebuilds the owner lookup index on (platform, owner_id). - All existing wizard / create-session tests updated to the new contract (HandleInteractionAsync + WizardInteraction). Wizard callback-data format preserved. - dotnet build clean, dotnet format --verify-no-changes clean, all 101 wizard tests pass.
This commit is contained in:
+7
-8
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
|
||||
using GmRelay.Shared.Features.Sessions.CreateSession.Wizard;
|
||||
using static GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard.WizardTestFakes;
|
||||
|
||||
@@ -20,7 +19,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Cancel();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Contains(draft.Id, drafts.DeletedIds);
|
||||
Assert.Single(messenger.Edits);
|
||||
@@ -36,7 +35,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Back();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
// Title is the first step, so Back is a no-op.
|
||||
Assert.Equal(WizardStepNames.Title, draft.Step);
|
||||
@@ -51,7 +50,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Back();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Equal(WizardStepNames.Title, draft.Step);
|
||||
}
|
||||
@@ -65,7 +64,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Back();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Equal(WizardStepNames.Description, draft.Step);
|
||||
}
|
||||
@@ -79,7 +78,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Back();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Equal(WizardStepNames.Cover, draft.Step);
|
||||
}
|
||||
@@ -93,7 +92,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Back();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Equal(WizardStepNames.PoolSystemDuration, draft.Step);
|
||||
}
|
||||
@@ -108,7 +107,7 @@ public sealed class GameCreationWizardCancelBackTests
|
||||
drafts.Seed(draft);
|
||||
|
||||
var data = WizardCallbackData.Create();
|
||||
await wizard.HandleUpdateAsync(CallbackUpdate(data), draft, CancellationToken.None);
|
||||
await wizard.HandleInteractionAsync(CallbackInteraction(data, ownerId: draft.OwnerId), draft, CancellationToken.None);
|
||||
|
||||
Assert.Equal(WizardStepNames.Confirm, draft.Step);
|
||||
Assert.Contains("cb-1", messenger.AnsweredCallbacks);
|
||||
|
||||
Reference in New Issue
Block a user