feat(practice): serve default/trial/leader-skin lists on practice/deck_list

practice/deck_list returns the same wire shape as /deck/info (the client parses
both via DeckGroupListData), but only ever sent user decks — so a fresh account
saw no default decks and couldn't start a practice match.

Extract the /deck/info hydration into a shared IDeckListBuilder used by
/deck/info, /deck/my_list, and /practice/deck_list. Practice passes
padEmptySlots:false (deck *select*, not builder) — matches the prod practice
capture, which returns real decks unpadded plus the 8 per-class default decks
and per-class leader-skin settings. Retire the near-duplicate
PracticeDeckListResponse DTO in favor of the shared DeckListResponse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-29 12:01:36 -04:00
parent 1e53748ae3
commit 2d675aa35d
6 changed files with 203 additions and 158 deletions

View File

@@ -138,6 +138,33 @@ public class PracticeControllerTests
}
}
[Test]
public async Task DeckList_exposes_the_eight_default_decks()
{
// Prod's practice/deck_list returns the same shape as /deck/info, including the 8 per-class
// starter decks under default_deck_list (keyed by deck_no "91".."98"). Without them, a fresh
// account has no decks to pick and can't start a practice match.
using var factory = new SVSimTestFactory();
await factory.SeedGlobalsAsync(); // imports the 8 default decks
long viewerId = await factory.SeedViewerAsync();
using var client = factory.CreateAuthenticatedClient(viewerId);
var response = await client.PostAsync("/practice/deck_list",
new StringContent(DeckFormatRequestJson(Format.All), Encoding.UTF8, "application/json"));
var body = await response.Content.ReadAsStringAsync();
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
using var doc = JsonDocument.Parse(body);
var defaults = doc.RootElement.GetProperty("default_deck_list");
Assert.That(defaults.ValueKind, Is.EqualTo(JsonValueKind.Object));
foreach (var key in new[] { "91", "92", "93", "94", "95", "96", "97", "98" })
{
Assert.That(defaults.TryGetProperty(key, out _), Is.True, $"missing default deck {key}");
}
Assert.That(defaults.GetProperty("91").GetProperty("class_id").GetInt32(), Is.GreaterThan(0));
}
[Test]
public async Task DeckList_empty_when_viewer_has_none()
{