using System.Net; using System.Text; using System.Text.Json; using NUnit.Framework; using SVSim.UnitTests.Infrastructure; namespace SVSim.UnitTests.Controllers; public class GiftControllerTests { private const string BaseAuthBlock = @"""viewer_id"":""0"",""steam_id"":0,""steam_session_ticket"":"""""; [Test] public async Task GiftTop_returns_five_tutorial_gifts_for_unclaimed_viewer() { using var factory = new SVSimTestFactory(); long viewerId = await factory.SeedViewerAsync(tutorialState: 31); using var client = factory.CreateAuthenticatedClient(viewerId); var response = await client.PostAsync("/tutorial/gift_top", new StringContent($$"""{"page":1,{{BaseAuthBlock}}}""", Encoding.UTF8, "application/json")); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var root = doc.RootElement; var presents = root.GetProperty("present_list"); Assert.That(presents.GetArrayLength(), Is.EqualTo(5)); // Expect the legendary pack entry (present_id 71478630) to be present. bool foundLegendaryGift = false; foreach (var p in presents.EnumerateArray()) { if (p.GetProperty("present_id").GetString() == "71478630") { foundLegendaryGift = true; Assert.That(p.GetProperty("reward_type").GetString(), Is.EqualTo("4")); Assert.That(p.GetProperty("reward_detail_id").GetString(), Is.EqualTo("90001")); Assert.That(p.GetProperty("reward_count").GetString(), Is.EqualTo("1")); Assert.That(p.GetProperty("item_type").GetInt32(), Is.EqualTo(2)); Assert.That(p.GetProperty("message").GetString(), Is.EqualTo("For completing the tutorial")); } } Assert.That(foundLegendaryGift, Is.True, "Legendary starter pack gift (71478630) must be in present_list."); Assert.That(root.GetProperty("present_history_list").GetArrayLength(), Is.EqualTo(0)); Assert.That(root.GetProperty("limit_over_present_list").GetArrayLength(), Is.EqualTo(0)); } [Test] public async Task GiftReceive_grants_currency_and_items_then_history_is_populated() { using var factory = new SVSimTestFactory(); await factory.SeedGlobalsAsync(); long viewerId = await factory.SeedViewerAsync(tutorialState: 31); using var client = factory.CreateAuthenticatedClient(viewerId); var pre = await factory.GetViewerCurrencyAsync(viewerId); var requestJson = $$""" {"present_id_array":["71478626","71478627","71478628","71478629","71478630"],"state":1,{{BaseAuthBlock}}} """; var response = await client.PostAsync("/tutorial/gift_receive", new StringContent(requestJson, Encoding.UTF8, "application/json")); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var root = doc.RootElement; // Five received ids echoed. var ids = root.GetProperty("received_ids").EnumerateArray() .Select(e => e.GetString()).ToHashSet(); Assert.That(ids, Is.EquivalentTo(new[] { "71478626", "71478627", "71478628", "71478629", "71478630" })); // present_list emptied, history populated. Assert.That(root.GetProperty("present_list").GetArrayLength(), Is.EqualTo(0)); Assert.That(root.GetProperty("present_history_list").GetArrayLength(), Is.EqualTo(5)); // Currency credited: +400 crystals, +100 rupees. var post = await factory.GetViewerCurrencyAsync(viewerId); Assert.That(post.Crystals - pre.Crystals, Is.EqualTo(400UL)); Assert.That(post.Rupees - pre.Rupees, Is.EqualTo(100UL)); } [Test] public async Task GiftReceive_advances_tutorial_state_from_31_to_41() { using var factory = new SVSimTestFactory(); await factory.SeedGlobalsAsync(); long viewerId = await factory.SeedViewerAsync(tutorialState: 31); using var client = factory.CreateAuthenticatedClient(viewerId); var json = $$"""{"present_id_array":["71478626"],"state":1,{{BaseAuthBlock}}}"""; var response = await client.PostAsync("/tutorial/gift_receive", new StringContent(json, Encoding.UTF8, "application/json")); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var root = doc.RootElement; // Response carries the new step inline. Assert.That(root.GetProperty("tutorial_step").GetInt32(), Is.EqualTo(41)); Assert.That(root.GetProperty("is_unreceived_present").GetBoolean(), Is.False); Assert.That(root.GetProperty("reward_list").GetArrayLength(), Is.EqualTo(1)); // Side effect: viewer state advanced to 41. Assert.That(await factory.GetViewerTutorialStateAsync(viewerId), Is.EqualTo(41)); } [Test] public async Task GiftReceive_second_call_with_same_ids_does_not_double_grant() { using var factory = new SVSimTestFactory(); await factory.SeedGlobalsAsync(); long viewerId = await factory.SeedViewerAsync(tutorialState: 31); using var client = factory.CreateAuthenticatedClient(viewerId); var preFirst = await factory.GetViewerCurrencyAsync(viewerId); var json = $$"""{"present_id_array":["71478626","71478627"],"state":1,{{BaseAuthBlock}}}"""; await client.PostAsync("/tutorial/gift_receive", new StringContent(json, Encoding.UTF8, "application/json")); var midPost = await factory.GetViewerCurrencyAsync(viewerId); Assert.That(midPost.Crystals - preFirst.Crystals, Is.EqualTo(400UL)); Assert.That(midPost.Rupees - preFirst.Rupees, Is.EqualTo(100UL)); var second = await client.PostAsync("/tutorial/gift_receive", new StringContent(json, Encoding.UTF8, "application/json")); Assert.That(second.StatusCode, Is.EqualTo(HttpStatusCode.OK)); var finalPost = await factory.GetViewerCurrencyAsync(viewerId); Assert.That(finalPost.Crystals, Is.EqualTo(midPost.Crystals), "Second claim of same present_ids must not re-grant."); Assert.That(finalPost.Rupees, Is.EqualTo(midPost.Rupees)); } }