feat: implement DiscordSessionBatchRenderer for Embed and Buttons

- Render SessionBatchViewModel into NetCord EmbedProperties + ActionRowProperties
- One embed per session with game title, Moscow date, players, capacity, waitlist, status
- Buttons map AvailableAction to ButtonProperties with platform-neutral custom IDs
- Cancelled sessions get embed but no action row
- Full sessions trigger waitlist button label
- 7 tests covering open/full/waitlist/cancelled/reschedule states

Closes #27

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 18:05:35 +03:00
parent c0147fd310
commit 1c75994722
3 changed files with 134 additions and 22 deletions
@@ -35,17 +35,17 @@ public sealed class DiscordSessionBatchRendererTests
Assert.Equal(2, actionRows.Count); // cancelled skipped
// Embed titles contain game title and Moscow date
Assert.Contains(embeds, e => e.Title.Contains("Campaign") && e.Title.Contains("26"));
Assert.Contains(embeds, e => e.Title.Contains("Campaign") && e.Title.Contains("27"));
Assert.Contains(embeds, e => e.Title.Contains("Campaign") && e.Title.Contains("28"));
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("26"));
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("27"));
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("28"));
// Cancelled session embed description indicates cancellation
var cancelledEmbed = embeds.First(e => e.Description.Contains("отменена") || e.Description.Contains("Отменена"));
var cancelledEmbed = embeds.First(e => e.Description!.Contains("отменена") || e.Description.Contains("Отменена"));
Assert.NotNull(cancelledEmbed);
// Active session embeds contain player names
Assert.Contains(embeds, e => e.Description != null && e.Description.Contains("Alice"));
Assert.Contains(embeds, e => e.Description != null && e.Description.Contains("Charlie"));
Assert.Contains(embeds, e => e.Description!.Contains("Alice"));
Assert.Contains(embeds, e => e.Description!.Contains("Charlie"));
// Buttons for active sessions
var allButtons = actionRows.SelectMany(r => r).OfType<ButtonProperties>().ToList();
@@ -94,7 +94,7 @@ public sealed class DiscordSessionBatchRendererTests
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
Assert.Equal(0xED4245, embeds[0].Color!.Value.RawValue);
Assert.Equal(0xED4245, embeds[0].Color.RawValue);
}
[Fact]
@@ -107,7 +107,7 @@ public sealed class DiscordSessionBatchRendererTests
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
Assert.Equal(0x57F287, embeds[0].Color!.Value.RawValue);
Assert.Equal(0x57F287, embeds[0].Color.RawValue);
}
[Fact]
@@ -120,7 +120,7 @@ public sealed class DiscordSessionBatchRendererTests
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
Assert.Equal(0xFEE75C, embeds[0].Color!.Value.RawValue);
Assert.Equal(0xFEE75C, embeds[0].Color.RawValue);
}
[Fact]