diff --git a/SVSim.UnitTests/Controllers/CardControllerTests.cs b/SVSim.UnitTests/Controllers/CardControllerTests.cs index cab8e7f..d988750 100644 --- a/SVSim.UnitTests/Controllers/CardControllerTests.cs +++ b/SVSim.UnitTests/Controllers/CardControllerTests.cs @@ -1,6 +1,9 @@ using System.Net; using System.Text; using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using SVSim.Database; using SVSim.UnitTests.Infrastructure; namespace SVSim.UnitTests.Controllers; @@ -343,4 +346,89 @@ public class CardControllerTests Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), body); Assert.That(body, Does.Contain("insufficient_vials")); } + + private static StringContent ProtectBody(long cardId, bool isProtected) => + new( + $$"""{"card_id":{{cardId}},"is_protected":{{(isProtected ? "true" : "false")}},"viewer_id":"0","steam_id":0,"steam_session_ticket":""}""", + Encoding.UTF8, + "application/json"); + + [Test] + public async Task Protect_toggles_flag_for_owned_card() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + await factory.SeedOwnedCardAsync(viewerId, cardId: 10001001L, count: 2); + using var client = factory.CreateAuthenticatedClient(viewerId); + + var response = await client.PostAsync("/card/protect", ProtectBody(10001001L, isProtected: true)); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), + await response.Content.ReadAsStringAsync()); + + // Verify persisted flag + using var scope = factory.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + var viewer = await db.Viewers.Include(v => v.Cards).ThenInclude(c => c.Card).FirstAsync(v => v.Id == viewerId); + Assert.That(viewer.Cards.First(c => c.Card.Id == 10001001L).IsProtected, Is.True); + } + + [Test] + public async Task Protect_round_trip_unsets_flag() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + await factory.SeedOwnedCardAsync(viewerId, cardId: 10001001L, count: 2, isProtected: true); + using var client = factory.CreateAuthenticatedClient(viewerId); + + var response = await client.PostAsync("/card/protect", ProtectBody(10001001L, isProtected: false)); + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + + using var scope = factory.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + var viewer = await db.Viewers.Include(v => v.Cards).ThenInclude(c => c.Card).FirstAsync(v => v.Id == viewerId); + Assert.That(viewer.Cards.First(c => c.Card.Id == 10001001L).IsProtected, Is.False); + } + + [Test] + public async Task Protect_without_auth_header_returns_401() + { + using var factory = new SVSimTestFactory(); + using var client = factory.CreateClient(); + + var response = await client.PostAsync("/card/protect", ProtectBody(10001001L, isProtected: true)); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); + } + + [Test] + public async Task Protect_unknown_card_returns_400_unknown_card() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + using var client = factory.CreateAuthenticatedClient(viewerId); + + var response = await client.PostAsync("/card/protect", ProtectBody(99_999_999L, isProtected: true)); + var body = await response.Content.ReadAsStringAsync(); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), body); + Assert.That(body, Does.Contain("unknown_card")); + } + + [Test] + public async Task Protect_returns_empty_data_object() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + await factory.SeedOwnedCardAsync(viewerId, cardId: 10001001L, count: 1); + using var client = factory.CreateAuthenticatedClient(viewerId); + + var response = await client.PostAsync("/card/protect", ProtectBody(10001001L, isProtected: true)); + var body = await response.Content.ReadAsStringAsync(); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body); + // The translation middleware only wraps for UnityPlayer UA; test clients see the raw + // controller payload, which for CardProtectResponse is an empty object. + Assert.That(body.Trim(), Is.EqualTo("{}")); + } }