From 742058403ccbd42ab60e847c066b511249924e1b Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 9 Jun 2026 20:52:47 -0400 Subject: [PATCH] refactor(campaign): delegate gift-reward-type check to GiftRewardTypes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delete local IsSupportedGiftRewardType and replace its single call site with GiftRewardTypes.IsSupported — Card (5) and Sleeve (6) are now accepted. Update unsupported-type test sentinel from 5 (Card) to 11 (SpotCard). Add Card and Sleeve success-path tests; full suite 1152/1152. --- .../Controllers/CampaignController.cs | 11 +--- .../Controllers/CampaignControllerTests.cs | 58 ++++++++++++++++++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/SVSim.EmulatedEntrypoint/Controllers/CampaignController.cs b/SVSim.EmulatedEntrypoint/Controllers/CampaignController.cs index bd19353..44c9126 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/CampaignController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/CampaignController.cs @@ -4,6 +4,7 @@ using SVSim.Database; using SVSim.Database.Enums; using SVSim.Database.Models; using SVSim.EmulatedEntrypoint.Models.Dtos.Campaign; +using SVSim.EmulatedEntrypoint.Services; namespace SVSim.EmulatedEntrypoint.Controllers; @@ -41,7 +42,7 @@ public sealed class CampaignController : SVSimController .AnyAsync(r => r.ViewerId == viewerId && r.SerialCodeId == code.Id, ct); if (alreadyRedeemed) return Fail(); - if (code.Rewards.Any(r => !IsSupportedGiftRewardType(r.RewardType))) return Fail(); + if (code.Rewards.Any(r => !GiftRewardTypes.IsSupported(r.RewardType))) return Fail(); try { @@ -82,12 +83,4 @@ public sealed class CampaignController : SVSimController private IActionResult Fail() => Ok(new { result_code = FailureResultCode }); - /// - /// Gift wire types per GiftController.WireRewardTypeToUserGoodsType: - /// 1=Crystal, 4=Item, 9=Rupy. Codes with unsupported types fail-fast at redemption. - /// Note: wire "1" means Crystal (not RedEther), following the gift wire convention - /// rather than the enum order. - /// - private static bool IsSupportedGiftRewardType(int rewardType) => - rewardType is 1 or 4 or 9; } diff --git a/SVSim.UnitTests/Controllers/CampaignControllerTests.cs b/SVSim.UnitTests/Controllers/CampaignControllerTests.cs index 3d2cbf0..0f797cb 100644 --- a/SVSim.UnitTests/Controllers/CampaignControllerTests.cs +++ b/SVSim.UnitTests/Controllers/CampaignControllerTests.cs @@ -182,9 +182,10 @@ public class CampaignControllerTests { using var factory = new SVSimTestFactory(); long viewerId = await factory.SeedViewerAsync(); - // RewardType 5 = Card; gift mapper supports only 1=Crystal, 4=Item, 9=Rupy. + // RewardType 11 = SpotCard; InventoryTransaction throws NotSupportedException, so the + // gift mapper rejects it. var code = await SeedCodeAsync(factory, "BADTYPE", - rewards: new[] { (5, 100L, 1) }); + rewards: new[] { (11, 100L, 1) }); using var client = factory.CreateAuthenticatedClient(viewerId); var response = await client.PostAsync("/campaign/regist_serial_code", @@ -227,4 +228,57 @@ public class CampaignControllerTests using var doc = JsonDocument.Parse(raw); Assert.That(doc.RootElement.GetProperty("result_code").GetInt32(), Is.EqualTo(4202)); } + + [Test] + public async Task Register_with_card_reward_succeeds_and_creates_present() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + var code = await SeedCodeAsync(factory, "CARDCODE", "Free card", + rewards: new[] { (5, 12345L, 1) }); + + using var client = factory.CreateAuthenticatedClient(viewerId); + var response = await client.PostAsync("/campaign/regist_serial_code", + JsonBody("""{"serial_code":"CARDCODE"}""")); + var raw = await response.Content.ReadAsStringAsync(); + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), raw); + + using var doc = JsonDocument.Parse(raw); + Assert.That(doc.RootElement.GetProperty("is_complete").GetBoolean(), Is.True); + + using var verifyScope = factory.Services.CreateScope(); + var ctx = verifyScope.ServiceProvider.GetRequiredService(); + var presents = await ctx.ViewerPresents.AsNoTracking() + .Where(p => p.ViewerId == viewerId).ToListAsync(); + Assert.That(presents, Has.Count.EqualTo(1)); + Assert.That(presents[0].RewardType, Is.EqualTo(5)); + Assert.That(presents[0].RewardDetailId, Is.EqualTo(12345L)); + Assert.That(presents[0].Source, Is.EqualTo($"serial_code:{code.Id}")); + } + + [Test] + public async Task Register_with_sleeve_reward_succeeds_and_creates_present() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + var code = await SeedCodeAsync(factory, "SLEEVECODE", "Free sleeve", + rewards: new[] { (6, 700100L, 1) }); + + using var client = factory.CreateAuthenticatedClient(viewerId); + var response = await client.PostAsync("/campaign/regist_serial_code", + JsonBody("""{"serial_code":"SLEEVECODE"}""")); + var raw = await response.Content.ReadAsStringAsync(); + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), raw); + + using var doc = JsonDocument.Parse(raw); + Assert.That(doc.RootElement.GetProperty("is_complete").GetBoolean(), Is.True); + + using var verifyScope = factory.Services.CreateScope(); + var ctx = verifyScope.ServiceProvider.GetRequiredService(); + var presents = await ctx.ViewerPresents.AsNoTracking() + .Where(p => p.ViewerId == viewerId).ToListAsync(); + Assert.That(presents, Has.Count.EqualTo(1)); + Assert.That(presents[0].RewardType, Is.EqualTo(6)); + Assert.That(presents[0].RewardDetailId, Is.EqualTo(700100L)); + } }