using System.Text.Json; using RpgRoller.Contracts; namespace RpgRoller.Tests; public sealed class PayloadBudgetTests { [Fact] public void CharacterSheetPayload_StaysWithinBudget() { using var harness = ServiceTestSupport.CreateHarness(); var service = harness.Service; service.Register("gm-sheet", "Password123", "GM"); service.Register("owner-sheet", "Password123", "Owner"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm-sheet", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-sheet", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Payload Sheet", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Payload Hero", campaign.Id)); var groupIds = new List { ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, character.Id, "Combat", "2D+1", 1, true)).Id, ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, character.Id, "Social", "2D+1", 1, true)).Id, ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, character.Id, "Knowledge", "2D+1", 1, true)).Id, ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, character.Id, "Survival", "2D+1", 1, true)).Id }; for (var i = 0; i < 18; i++) { Guid? skillGroupId = i < 16 ? groupIds[i % groupIds.Count] : null; _ = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, $"Skill {i:D2}", "2D+1", 1, true, skillGroupId)); } var sheet = ServiceTestSupport.GetValue(service.GetCharacterSheet(ownerSession, character.Id)); AssertPayloadWithinBudget(sheet, 12 * 1024, "initial character sheet"); } [Fact] public void CampaignLogInitialPagePayload_StaysWithinBudget() { using var harness = ServiceTestSupport.CreateHarness(CreateScriptedRolls(220)); var service = harness.Service; service.Register("gm-log-budget", "Password123", "GM"); service.Register("owner-log-budget", "Password123", "Owner"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm-log-budget", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-log-budget", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Payload Log", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Log Hero", campaign.Id)); var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, "Stealth", "2D+1", 1, true)); for (var i = 0; i < 25; i++) _ = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public")); var page = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, limit: 25)); AssertPayloadWithinBudget(page, 8 * 1024, "initial log page"); } [Fact] public void CampaignLogIncrementalPayload_StaysWithinBudget() { using var harness = ServiceTestSupport.CreateHarness(CreateScriptedRolls(240)); var service = harness.Service; service.Register("gm-log-delta", "Password123", "GM"); service.Register("owner-log-delta", "Password123", "Owner"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm-log-delta", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-log-delta", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Payload Delta", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Delta Hero", campaign.Id)); var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, "Stealth", "2D+1", 1, true)); for (var i = 0; i < 25; i++) _ = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public")); var initialPage = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, limit: 25)); _ = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public")); var incrementalPage = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, initialPage.Cursor, 25)); AssertPayloadWithinBudget(incrementalPage, 2 * 1024, "incremental log update"); } [Fact] public void RollResultPayload_StaysWithinJsInteropBudget() { using var harness = ServiceTestSupport.CreateHarness(CreateScriptedRolls(40)); var service = harness.Service; service.Register("gm-roll-budget", "Password123", "GM"); service.Register("owner-roll-budget", "Password123", "Owner"); var gmSession = ServiceTestSupport.GetValue(service.Login("gm-roll-budget", "Password123")).SessionToken; var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-roll-budget", "Password123")).SessionToken; var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Payload Roll", "d6")); var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Roll Hero", campaign.Id)); var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, "Wild Roll", "6D+3", 3, true)); var roll = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public")); AssertPayloadWithinBudget(roll, 16 * 1024, "roll mutation response"); } private static void AssertPayloadWithinBudget(T payload, int maxBytes, string label) { var byteCount = JsonSerializer.SerializeToUtf8Bytes(payload, SerializerOptions).Length; Assert.True(byteCount <= maxBytes, $"{label} payload was {byteCount} bytes, above the {maxBytes}-byte budget."); } private static int[] CreateScriptedRolls(int count) { var values = new[] { 6, 5, 4, 3, 2, 1 }; var scriptedRolls = new int[count]; for (var i = 0; i < count; i++) scriptedRolls[i] = values[i % values.Length]; return scriptedRolls; } private static readonly JsonSerializerOptions SerializerOptions = RpgRollerJson.CreateSerializerOptions(); }