Practice battles work
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Repositories.Deck;
|
||||
using SVSim.EmulatedEntrypoint.Extensions;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
@@ -25,6 +30,9 @@ public class PracticeControllerTests
|
||||
public async Task Info_returns_non_empty_opponent_array()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
// Practice opponents are bootstrapped from prod-captures/practice-info-*.json into the
|
||||
// PracticeOpponents table — empty by default in tests, so seed first.
|
||||
await factory.SeedGlobalsAsync();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
|
||||
@@ -41,6 +49,26 @@ public class PracticeControllerTests
|
||||
Assert.That(doc.RootElement[0].GetProperty("practice_id").GetInt32(), Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Info_returns_empty_array_when_db_not_bootstrapped()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
// Skip SeedGlobalsAsync — table is empty. /practice/info must still 200, not 500: the
|
||||
// client treats an empty array as "no opponents" and the practice menu just shows nothing.
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
|
||||
var response = await client.PostAsync("/practice/info",
|
||||
new StringContent(BaseRequestJson, 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);
|
||||
Assert.That(doc.RootElement.ValueKind, Is.EqualTo(JsonValueKind.Array));
|
||||
Assert.That(doc.RootElement.GetArrayLength(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeckList_returns_viewer_decks()
|
||||
{
|
||||
@@ -65,6 +93,51 @@ public class PracticeControllerTests
|
||||
Assert.That(unlimited[0].GetProperty("deck_name").GetString(), Is.EqualTo("Unlimited Deck"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeckList_card_id_array_contains_real_card_ids()
|
||||
{
|
||||
// Regression for the deck-include bug: PracticeController used to load the viewer
|
||||
// via GetViewerByShortUdid, which Includes Decks but NOT Decks.Cards.Card. The
|
||||
// DeckCard.Card navigation defaults to `new ShadowverseCardEntry()` (Id=0), so the
|
||||
// wire response shipped 40 zeros — which then NREs the client's SBattleLoad
|
||||
// (CardCreator returns null for id=0 → BattlePlayerBase.AddToDeck(null)). Asserts
|
||||
// a real card id round-trips end-to-end so the same .Include drop can't reappear.
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
|
||||
const long CardId = 900_123_456L;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.Cards.Add(new ShadowverseCardEntry { Id = CardId, Name = "Regression Card" });
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
await factory.SeedDeckAsync(viewerId, Format.Rotation, number: 1);
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var repo = scope.ServiceProvider.GetRequiredService<IDeckRepository>();
|
||||
var card = await db.Cards.FirstAsync(c => c.Id == CardId);
|
||||
await repo.UpsertDeck(viewerId, Format.Rotation, 1,
|
||||
d => d.Cards = new List<DeckCard> { new() { Card = card, Count = 3 } });
|
||||
}
|
||||
|
||||
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 cards = doc.RootElement.GetProperty("user_deck_rotation")[0].GetProperty("card_id_array");
|
||||
Assert.That(cards.GetArrayLength(), Is.EqualTo(3), "DeckCard.Count should expand into Count copies in card_id_array");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
Assert.That(cards[i].GetInt64(), Is.EqualTo(CardId), "card_id must round-trip — Includes are dropping DeckCard.Card");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeckList_empty_when_viewer_has_none()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user