using GmRelay.Bot.Features.Sessions.CreateSession; using GmRelay.Shared.Features.Sessions.CreateSession.Wizard; using Xunit; namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard; /// /// Regression coverage for : /// when the wizard payload carries no player limit (e.g. user picked /// «♾ Без лимита» on the Capacity step), the resulting /// CreateSessionCommand.MaxPlayers must be null — never 0. /// 0 would violate the DB CHECK constraint /// (ck_sessions_max_players in V006) by inserting 0 instead of /// NULL, which is the wire-level representation of "no limit". /// public sealed class CreateSessionHandlerBuildCommandTests { [Fact] public void BuildCommand_WhenSingleMaxPlayersIsNull_PropagatesNull() { var draft = new WizardDraft { Id = Guid.NewGuid(), ChatId = "42", OwnerId = "100", Step = "confirm", }; var payload = new WizardPayload { Type = WizardCreationType.Single, Title = "T", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, Single = new WizardSingleInput { ScheduledAt = DateTimeOffset.UtcNow.AddDays(1), MaxPlayers = null, }, }; var cmd = CreateSessionHandler.BuildCommand( draft, payload, new[] { payload.Single!.ScheduledAt!.Value }, payload.Single.MaxPlayers, isOneShot: true); Assert.Null(cmd.MaxPlayers); } [Fact] public void BuildCommand_WhenSingleMaxPlayersIsSet_PropagatesValue() { var draft = new WizardDraft { Id = Guid.NewGuid(), ChatId = "42", OwnerId = "100", Step = "confirm", }; var payload = new WizardPayload { Type = WizardCreationType.Single, Title = "T", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, Single = new WizardSingleInput { ScheduledAt = DateTimeOffset.UtcNow.AddDays(1), MaxPlayers = 5, }, }; var cmd = CreateSessionHandler.BuildCommand( draft, payload, new[] { payload.Single!.ScheduledAt!.Value }, payload.Single.MaxPlayers, isOneShot: true); Assert.Equal(5, cmd.MaxPlayers); } [Fact] public void BuildCommand_WhenFormatIsOnline_PropagatesFormatAndJoinLink() { var draft = new WizardDraft { Id = Guid.NewGuid(), ChatId = "42", OwnerId = "100", Step = "confirm", }; var payload = new WizardPayload { Type = WizardCreationType.Single, Title = "T", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, Format = WizardSessionFormat.Online, JoinLink = "https://vtt.example/game", Single = new WizardSingleInput { ScheduledAt = DateTimeOffset.UtcNow.AddDays(1), MaxPlayers = 4, }, }; var cmd = CreateSessionHandler.BuildCommand( draft, payload, new[] { payload.Single!.ScheduledAt!.Value }, payload.Single.MaxPlayers, isOneShot: true); Assert.Equal("Online", cmd.Format); Assert.Equal("https://vtt.example/game", cmd.Link); Assert.Null(cmd.LocationAddress); } [Fact] public void BuildCommand_WhenFormatIsOffline_PropagatesFormatAndAddress() { var draft = new WizardDraft { Id = Guid.NewGuid(), ChatId = "42", OwnerId = "100", Step = "confirm", }; var payload = new WizardPayload { Type = WizardCreationType.Single, Title = "T", System = "Dnd5e", DurationMinutes = 240, Visibility = WizardVisibility.Public, Format = WizardSessionFormat.Offline, LocationAddress = "Москва, ул. Кубиков, 12", Single = new WizardSingleInput { ScheduledAt = DateTimeOffset.UtcNow.AddDays(1), MaxPlayers = 4, }, }; var cmd = CreateSessionHandler.BuildCommand( draft, payload, new[] { payload.Single!.ScheduledAt!.Value }, payload.Single.MaxPlayers, isOneShot: true); Assert.Equal("Offline", cmd.Format); Assert.Equal(string.Empty, cmd.Link); Assert.Equal("Москва, ул. Кубиков, 12", cmd.LocationAddress); } }