using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.DeckBuilder;
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.DeckBuilder;
using SVSim.UnitTests.Infrastructure;
namespace SVSim.UnitTests.Controllers;
///
/// End-to-end coverage for the portal pair (/deck_code, /deck). These tests bypass the
/// translation middleware (non-Unity UA) and hit the controllers via plain JSON, which is fine
/// — both endpoints are anonymous and the action signatures don't care which path serialized
/// the body. The middleware's [NoWireEncryption] branch is exercised in the live smoke test.
///
public class DeckBuilderControllerTests
{
private static readonly JsonSerializerOptions Json = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
[Test]
public async Task Generate_then_resolve_roundtrips_deck_payload()
{
using var factory = new SVSimTestFactory();
using var client = factory.CreateClient();
var generate = await client.PostAsJsonAsync("/deck_code",
new GenerateDeckCodeRequest
{
Clan = 4,
DeckFormat = 1,
CardID = new() { 100414020, 100414020, 104021030 }
}, Json);
Assert.That(generate.StatusCode, Is.EqualTo(HttpStatusCode.OK),
await generate.Content.ReadAsStringAsync());
var generateBody = await generate.Content.ReadFromJsonAsync(Json);
Assert.That(generateBody, Is.Not.Null);
Assert.That(generateBody!.DeckCode, Has.Length.EqualTo(4));
var resolve = await client.PostAsJsonAsync("/deck",
new GetDeckFromCodeRequest { DeckCode = generateBody.DeckCode }, Json);
Assert.That(resolve.StatusCode, Is.EqualTo(HttpStatusCode.OK),
await resolve.Content.ReadAsStringAsync());
var resolveBody = await resolve.Content.ReadFromJsonAsync(Json);
Assert.That(resolveBody, Is.Not.Null);
Assert.That(resolveBody!.Deck.Clan, Is.EqualTo("4"));
Assert.That(resolveBody.Deck.DeckFormat, Is.EqualTo("1"));
Assert.That(resolveBody.Deck.SubClan, Is.EqualTo(0));
Assert.That(resolveBody.Deck.CardID, Is.EqualTo(new List { 100414020, 100414020, 104021030 }));
}
[Test]
public async Task Generate_strips_foil_flag_from_card_ids()
{
using var factory = new SVSimTestFactory();
using var client = factory.CreateClient();
var generate = await client.PostAsJsonAsync("/deck_code",
new GenerateDeckCodeRequest
{
Clan = 4,
DeckFormat = 1,
// 011 ids are foil variants observed in the prod traffic dump.
CardID = new() { 703441011, 701441011, 100414020 }
}, Json);
var generateBody = await generate.Content.ReadFromJsonAsync(Json);
var resolve = await client.PostAsJsonAsync("/deck",
new GetDeckFromCodeRequest { DeckCode = generateBody!.DeckCode }, Json);
var resolveBody = await resolve.Content.ReadFromJsonAsync(Json);
Assert.That(resolveBody!.Deck.CardID,
Is.EqualTo(new List { 703441010, 701441010, 100414020 }),
"Foil bit (last digit) must be normalized to 0 in the stored payload.");
}
[Test]
public async Task Resolve_returns_invalid_code_error_for_unknown_code()
{
using var factory = new SVSimTestFactory();
using var client = factory.CreateClient();
var resolve = await client.PostAsJsonAsync("/deck",
new GetDeckFromCodeRequest { DeckCode = "zzzz" }, Json);
Assert.That(resolve.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var body = await resolve.Content.ReadFromJsonAsync(Json);
Assert.That(body!.Errors.Type, Is.EqualTo("INVALID_DECK_CODE"));
}
[Test]
public async Task Generate_rejects_empty_card_list()
{
using var factory = new SVSimTestFactory();
using var client = factory.CreateClient();
var generate = await client.PostAsJsonAsync("/deck_code",
new GenerateDeckCodeRequest { Clan = 1, DeckFormat = 1, CardID = new() }, Json);
var body = await generate.Content.ReadFromJsonAsync(Json);
Assert.That(body!.Errors.Type, Is.EqualTo("INVALID_DECK"));
Assert.That(body.DeckCode, Is.Empty);
}
}