From 9e7b7eed2735a4c0df3d372393dc80aac90e6f67 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Thu, 28 May 2026 23:49:08 -0400 Subject: [PATCH] feat(pack): /pack/get_gacha_point_rewards endpoint --- .../Controllers/PackController.cs | 19 ++- .../PackControllerGachaPointTests.cs | 115 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 SVSim.UnitTests/Controllers/PackControllerGachaPointTests.cs diff --git a/SVSim.EmulatedEntrypoint/Controllers/PackController.cs b/SVSim.EmulatedEntrypoint/Controllers/PackController.cs index 34ea443..70680d4 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/PackController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/PackController.cs @@ -28,6 +28,7 @@ public class PackController : SVSimController private readonly IRandom _rng; private readonly SVSimDbContext _db; private readonly ICardAcquisitionService _acquisition; + private readonly IGachaPointService _gachaPoint; public PackController( IPackRepository packs, @@ -35,7 +36,8 @@ public class PackController : SVSimController ICardPoolProvider pools, IRandom rng, SVSimDbContext db, - ICardAcquisitionService acquisition) + ICardAcquisitionService acquisition, + IGachaPointService gachaPoint) { _packs = packs; _opener = opener; @@ -43,6 +45,7 @@ public class PackController : SVSimController _rng = rng; _db = db; _acquisition = acquisition; + _gachaPoint = gachaPoint; } [HttpPost("info")] @@ -146,6 +149,20 @@ public class PackController : SVSimController }; } + [HttpPost("get_gacha_point_rewards")] + public async Task> GetGachaPointRewards( + GetGachaPointRewardsRequest request) + { + if (!TryGetViewerId(out long viewerId)) return Unauthorized(); + + var rewards = await _gachaPoint.GetRewardsAsync(request.ParentGachaId, viewerId); + + return new GetGachaPointRewardsResponse + { + GachaPointRewards = rewards.ToList(), + }; + } + [HttpPost("open")] [HttpPost("/tutorial/pack_open")] public async Task> Open(PackOpenRequest request) diff --git a/SVSim.UnitTests/Controllers/PackControllerGachaPointTests.cs b/SVSim.UnitTests/Controllers/PackControllerGachaPointTests.cs new file mode 100644 index 0000000..4828fe3 --- /dev/null +++ b/SVSim.UnitTests/Controllers/PackControllerGachaPointTests.cs @@ -0,0 +1,115 @@ +using System.Net; +using System.Text; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using SVSim.Database; +using SVSim.Database.Enums; +using SVSim.Database.Models; +using SVSim.UnitTests.Infrastructure; + +namespace SVSim.UnitTests.Controllers; + +public class PackControllerGachaPointTests +{ + private static StringContent JsonBody(string json) => + new(json, Encoding.UTF8, "application/json"); + + [Test] + public async Task GetGachaPointRewards_returns_catalog_for_active_pack() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + using (var scope = factory.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Classes.Add(new ClassEntry { Id = 0, Name = "Neutral" }); + var set = new ShadowverseCardSetEntry { Id = 10008, IsInRotation = true }; + db.CardSets.Add(set); + set.Cards.Add(new ShadowverseCardEntry + { + Id = 108041010, Name = "leg", Rarity = Rarity.Legendary, + Class = db.Classes.Local.First(), IsFoil = false, + }); + db.CardCosmeticRewards.Add(new CardCosmeticReward + { + CardId = 108041010, Type = CosmeticType.Emblem, CosmeticId = 1080410100, + }); + db.Packs.Add(new PackConfigEntry + { + Id = 10008, BasePackId = 10008, PackCategory = PackCategory.LegendCardPack, + CommenceDate = DateTime.UtcNow.AddDays(-1), CompleteDate = DateTime.UtcNow.AddDays(30), + GachaPointConfig = new PackGachaPointConfig { ExchangeablePoint = 400, IncreaseGachaPoint = 1 }, + }); + await db.SaveChangesAsync(); + } + + using var client = factory.CreateAuthenticatedClient(viewerId); + var body = JsonBody("""{"odds_gacha_id":10008,"parent_gacha_id":10008,"viewer_id":"0","steam_id":0,"steam_session_ticket":""}"""); + var response = await client.PostAsync("/pack/get_gacha_point_rewards", body); + var text = await response.Content.ReadAsStringAsync(); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), text); + + using var doc = JsonDocument.Parse(text); + var rewards = doc.RootElement.GetProperty("gacha_point_rewards"); + Assert.That(rewards.GetArrayLength(), Is.EqualTo(1)); + var entry = rewards[0]; + Assert.That(entry.GetProperty("class_id").GetString(), Is.EqualTo("0"), + "class_id must be wire-typed as a string"); + Assert.That(entry.GetProperty("card_id").GetInt64(), Is.EqualTo(108041010)); + Assert.That(entry.GetProperty("is_received").GetBoolean(), Is.False); + var rewardList = entry.GetProperty("reward_list"); + Assert.That(rewardList.GetArrayLength(), Is.EqualTo(1)); + Assert.That(rewardList[0].GetProperty("reward_type").GetInt32(), Is.EqualTo(7)); + Assert.That(rewardList[0].GetProperty("reward_detail_id").GetInt64(), Is.EqualTo(1080410100)); + Assert.That(rewardList[0].GetProperty("reward_number").GetInt32(), Is.EqualTo(1)); + } + + [Test] + public async Task GetGachaPointRewards_wire_keys_match_prod_capture() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + using (var scope = factory.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Classes.Add(new ClassEntry { Id = 0, Name = "Neutral" }); + var set = new ShadowverseCardSetEntry { Id = 10008, IsInRotation = true }; + db.CardSets.Add(set); + set.Cards.Add(new ShadowverseCardEntry + { + Id = 108041010, Name = "leg", Rarity = Rarity.Legendary, + Class = db.Classes.Local.First(), IsFoil = false, + }); + db.CardCosmeticRewards.Add(new CardCosmeticReward + { + CardId = 108041010, Type = CosmeticType.Emblem, CosmeticId = 1080410100, + }); + db.Packs.Add(new PackConfigEntry + { + Id = 10008, BasePackId = 10008, PackCategory = PackCategory.LegendCardPack, + CommenceDate = DateTime.UtcNow.AddDays(-1), CompleteDate = DateTime.UtcNow.AddDays(30), + GachaPointConfig = new PackGachaPointConfig { ExchangeablePoint = 400, IncreaseGachaPoint = 1 }, + }); + await db.SaveChangesAsync(); + } + + using var client = factory.CreateAuthenticatedClient(viewerId); + var body = JsonBody("""{"odds_gacha_id":10008,"parent_gacha_id":10008,"viewer_id":"0","steam_id":0,"steam_session_ticket":""}"""); + var response = await client.PostAsync("/pack/get_gacha_point_rewards", body); + var text = await response.Content.ReadAsStringAsync(); + + // Literal wire-key checks — verified against + // data_dumps/traffic_prod_tradeables_capture.ndjson pack 10008 response. + Assert.That(text, Does.Contain("\"gacha_point_rewards\"")); + Assert.That(text, Does.Contain("\"class_id\":\"0\""), "class_id MUST be a string"); + Assert.That(text, Does.Contain("\"reward_detail_id\":1080410100"), + "per-card entry uses reward_detail_id (not reward_id)"); + Assert.That(text, Does.Contain("\"reward_number\":1"), + "per-card entry uses reward_number (not reward_num)"); + Assert.That(text, Does.Contain("\"is_received\":false")); + Assert.That(text, Does.Contain("\"is_display_prize\":false")); + } +}