using GmRelay.Bot.Features.Sessions.CreateSession; using GmRelay.Shared.Features.Sessions.CreateSession.Wizard; using GmRelay.Shared.Platform; using Microsoft.Extensions.Logging.Abstractions; using Npgsql; using static GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard.WizardTestFakes; namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard; [Collection(CreateSessionHandlerPostgresCollection.Name)] public sealed class CreateSessionHandlerSubmitSingleDraftTests(CreateSessionHandlerPostgresFixture fixture) { [Fact] public async Task SubmitDraftAsync_CompleteSinglePayload_PublishesScheduleAndStoresMessageRefs() { var connectionString = await fixture.CreateMigratedDatabaseAsync(); await using var dataSource = NpgsqlDataSource.Create(connectionString); var drafts = new FakeWizardDraftRepository(); var wizardMessenger = new FakeWizardMessenger(); var platformMessenger = new FakePlatformMessenger(); var sut = new CreateSessionHandler( drafts, new GmRelay.Shared.Features.Sessions.CreateSession.CreateSessionHandler(dataSource), wizardMessenger, NullLogger.Instance, platformMessenger, dataSource); var payload = new WizardPayload { Type = WizardCreationType.Single, Title = "Тест публикации", System = "Dnd5e", DurationMinutes = 240, Format = WizardSessionFormat.Online, JoinLink = "https://vtt.example/game", Visibility = WizardVisibility.Public, Single = new WizardSingleInput { ScheduledAt = DateTimeOffset.UtcNow.AddDays(7), MaxPlayers = null, }, }; var draft = NewDraft(WizardStepNames.Confirm, payload, ownerId: 111111111); draft.ChatId = "-1003916537960"; draft.DraftMessageId = "7"; drafts.Seed(draft); await sut.SubmitDraftAsync(draft, CancellationToken.None); Assert.Single(platformMessenger.CreatedThreads); Assert.Equal("Тест публикации", platformMessenger.CreatedThreads[0].Title); Assert.Single(platformMessenger.SentSchedules); Assert.Equal("456", platformMessenger.SentSchedules[0].Group.ExternalThreadId); Assert.Contains(draft.Id, drafts.DeletedIds); Assert.Contains(wizardMessenger.Edits, edit => edit.Text.Contains("✅ Создано: 1 сессия", StringComparison.Ordinal)); await using var connection = await dataSource.OpenConnectionAsync(); await using var command = new NpgsqlCommand( """ SELECT thread_id, batch_message_id, topic_created_by_bot FROM sessions ORDER BY created_at DESC LIMIT 1 """, connection); await using var reader = await command.ExecuteReaderAsync(); Assert.True(await reader.ReadAsync()); Assert.Equal(456, reader.GetInt32(0)); Assert.Equal(789, reader.GetInt32(1)); Assert.True(reader.GetBoolean(2)); } } internal sealed class FakePlatformMessenger : IPlatformMessenger { public List<(PlatformGroup Group, string Title)> CreatedThreads { get; } = new(); public List SentSchedules { get; } = new(); public Task CreateThreadAsync(PlatformGroup group, string title, CancellationToken ct) { CreatedThreads.Add((group, title)); return Task.FromResult(new PlatformMessageRef(group.Platform, group.ExternalGroupId, "456", string.Empty)); } public Task SendScheduleAsync(PlatformScheduleMessage message, CancellationToken ct) { SentSchedules.Add(message); return Task.FromResult(new PlatformMessageRef( message.Group.Platform, message.Group.ExternalGroupId, message.Group.ExternalThreadId, "789")); } public Task UpdateScheduleAsync(PlatformScheduleMessage message, CancellationToken ct) => Task.CompletedTask; public Task SendGroupMessageAsync(PlatformGroup group, string htmlText, CancellationToken ct) => Task.CompletedTask; public Task SendPrivateMessageAsync(PlatformPrivateMessage message, CancellationToken ct) => Task.CompletedTask; public Task AnswerInteractionAsync(PlatformInteractionReply reply, CancellationToken ct) => Task.CompletedTask; public Task SendCalendarFileAsync(PlatformCalendarFile file, CancellationToken ct) => Task.CompletedTask; }