feat(matching): MatchContextBuilder.BuildForRankBattleAsync for rank battles

Sibling to BuildForTwoPickAsync. Routes through IDeckRepository.GetDeck
to pull the viewer's deck #1 for the requested format (avoiding the
viewer-graph nav-ref auto-load pitfall — DeckCard.Card silently ships
card_id=0 via the default include path). Throws if the viewer has no
deck for the format. Cosmetics fall back to DefaultLoadoutConfig
defaults when unequipped, same shape as TK2.

Used by RankBattleController in a later task to build self-context for
/ai_<fmt>_rank_battle/start and to pair-up under /<fmt>_rank_battle/do_matching.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-02 01:13:19 -04:00
parent b65cf81977
commit 7eaf13893e
3 changed files with 95 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SVSim.BattleNode.Bridge;
using SVSim.Database;
using SVSim.Database.Enums;
using SVSim.Database.Models;
using SVSim.EmulatedEntrypoint.Services;
using SVSim.UnitTests.Infrastructure;
@@ -116,6 +117,41 @@ public class MatchContextBuilderTests
Assert.That(ex!.ErrorCode, Is.EqualTo("arena_two_pick_draft_incomplete"));
}
[Test]
public async Task BuildForRankBattle_returns_MatchContext_with_format_specific_deck()
{
await using var factory = new SVSimTestFactory();
var viewerId = await factory.SeedViewerAsync(displayName: "Ranker");
await factory.SeedGlobalsAsync();
await factory.SeedDeckAsync(viewerId, Format.Rotation, 1, "Rank Rotation Deck");
using var scope = factory.Services.CreateScope();
var builder = scope.ServiceProvider.GetRequiredService<IMatchContextBuilder>();
var ctx = await builder.BuildForRankBattleAsync(viewerId, Format.Rotation);
Assert.That(ctx.UserName, Is.EqualTo("Ranker"));
Assert.That(ctx.BattleType, Is.EqualTo(11), "BattleType=11 matches the prod rank-battle wire value (same as TK2).");
Assert.That(ctx.ClassId, Is.Not.Null.And.Not.Empty, "ClassId from the selected deck's class.");
Assert.That(ctx.CardMasterName, Is.EqualTo("card_master_node_10015"));
Assert.That(ctx.FieldId, Is.EqualTo(43));
}
[Test]
public async Task BuildForRankBattle_throws_when_no_deck_for_format()
{
await using var factory = new SVSimTestFactory();
var viewerId = await factory.SeedViewerAsync();
await factory.SeedGlobalsAsync();
// Intentionally no SeedDeckAsync for Rotation.
using var scope = factory.Services.CreateScope();
var builder = scope.ServiceProvider.GetRequiredService<IMatchContextBuilder>();
Assert.That(async () => await builder.BuildForRankBattleAsync(viewerId, Format.Rotation),
Throws.Exception);
}
[Test]
public async Task BuildForTwoPick_falls_back_to_default_loadout_when_unequipped()
{