From 6ed0a120a091320df3231c625ec508b450f581ac Mon Sep 17 00:00:00 2001 From: Toutsu Date: Tue, 26 May 2026 13:40:59 +0300 Subject: [PATCH] fix(discord): avoid duplicate schedule send after new session --- .../Sessions/DiscordNewSessionHandler.cs | 21 +++++++---------- .../Discord/DiscordNewSessionHandlerTests.cs | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionHandler.cs b/src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionHandler.cs index ffa3c4f..0accdd1 100644 --- a/src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionHandler.cs +++ b/src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionHandler.cs @@ -1,7 +1,6 @@ using Dapper; using GmRelay.DiscordBot.Infrastructure.Discord; using GmRelay.Shared.Domain; -using GmRelay.Shared.Platform; using GmRelay.Shared.Rendering; using Npgsql; @@ -12,7 +11,6 @@ public sealed record TimeParseResult(bool IsSuccess, DateTimeOffset Value, strin public sealed class DiscordNewSessionHandler( NpgsqlDataSource dataSource, DiscordPermissionChecker permissionChecker, - IPlatformMessenger messenger, ILogger logger) { public static TimeParseResult ParseTimeInput(string input) @@ -75,6 +73,7 @@ public sealed class DiscordNewSessionHandler( } await using var transaction = await connection.BeginTransactionAsync(cancellationToken); + var transactionCommitted = false; try { await connection.ExecuteAsync( @@ -125,23 +124,19 @@ public sealed class DiscordNewSessionHandler( transaction); await transaction.CommitAsync(cancellationToken); + transactionCommitted = true; logger.LogInformation("Created session {SessionId} in guild {GuildId}", sessionId, guildId); var sessions = new[] { new SessionBatchDto(sessionId, scheduledAt.UtcDateTime, SessionStatus.Planned, maxPlayers, joinLink ?? string.Empty) }; - var view = SessionBatchViewBuilder.Build(title, sessions, Array.Empty()); - - await messenger.SendScheduleAsync( - new PlatformScheduleMessage( - new PlatformGroup(PlatformKind.Discord, guildId, guildId, channelId), - view, - null), - cancellationToken); - - return view; + return SessionBatchViewBuilder.Build(title, sessions, Array.Empty()); } catch { - await transaction.RollbackAsync(cancellationToken); + if (!transactionCommitted) + { + await transaction.RollbackAsync(cancellationToken); + } + throw; } } diff --git a/tests/GmRelay.Bot.Tests/Discord/DiscordNewSessionHandlerTests.cs b/tests/GmRelay.Bot.Tests/Discord/DiscordNewSessionHandlerTests.cs index 637481c..f0fbb05 100644 --- a/tests/GmRelay.Bot.Tests/Discord/DiscordNewSessionHandlerTests.cs +++ b/tests/GmRelay.Bot.Tests/Discord/DiscordNewSessionHandlerTests.cs @@ -127,6 +127,18 @@ public sealed class DiscordNewSessionHandlerTests Assert.Contains("RollbackAsync", source, StringComparison.Ordinal); } + [Fact] + public void Handler_ShouldNotRollbackCommittedTransactionAfterPostCommitFailure() + { + var repoRoot = GetRepoRoot(); + var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs"); + var source = File.ReadAllText(handlerPath); + + Assert.Contains("transactionCommitted = false", source, StringComparison.Ordinal); + Assert.Contains("transactionCommitted = true", source, StringComparison.Ordinal); + Assert.Contains("if (!transactionCommitted)", source, StringComparison.Ordinal); + } + [Fact] public void Handler_ShouldRespectCancellationToken() { @@ -148,6 +160,17 @@ public sealed class DiscordNewSessionHandlerTests Assert.Contains("message.Embeds = embeds", source, StringComparison.Ordinal); } + [Fact] + public void Handler_ShouldLeaveScheduleMessageCreationToInteractionResponse() + { + var repoRoot = GetRepoRoot(); + var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs"); + var source = File.ReadAllText(handlerPath); + + Assert.DoesNotContain("SendScheduleAsync", source, StringComparison.Ordinal); + Assert.DoesNotContain("PlatformScheduleMessage", source, StringComparison.Ordinal); + } + private static DateTimeOffset FutureDateAt1930() { var future = DateTimeOffset.UtcNow.AddDays(7);