From 155ccf0a48a49adc23142e4cd270761414942ceb Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 3 Jun 2026 23:41:59 -0400 Subject: [PATCH] test(battle-node): lock token-reveal knownList shape vs prod capture line 96 Co-Authored-By: Claude Opus 4.8 --- .../Integration/CaptureConformanceTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/SVSim.UnitTests/BattleNode/Integration/CaptureConformanceTests.cs b/SVSim.UnitTests/BattleNode/Integration/CaptureConformanceTests.cs index dc20179..20367ab 100644 --- a/SVSim.UnitTests/BattleNode/Integration/CaptureConformanceTests.cs +++ b/SVSim.UnitTests/BattleNode/Integration/CaptureConformanceTests.cs @@ -303,6 +303,55 @@ public class CaptureConformanceTests } Assert.That(ourEntry.GetProperty("cardId").GetInt64(), Is.EqualTo(128821011L)); } + + [Test] + public void SynthesizedKnownList_for_a_generated_token_matches_prod_recv_shape() + { + // Prod recv PlayActions for a PLAYED token (battle-traffic_tk2_regular.ndjson:96): + // the token's cardId was generated by an earlier add op, not present in any deck. + const string prodEntry = """ + { "idx": 38, "cardId": 900811111, "to": 20, "cost": 1, "clan": 8, "tribe": "0", "spellboost": 0, "attachTarget": "" } + """; + + // Compose the two new pure pieces: mine the token from a generating frame's add op, + // then build the played-card entry from the resulting map. + var generatingOrderList = new List + { + new Dictionary { ["add"] = new Dictionary + { ["idx"] = new List { 38L }, ["isSelf"] = 1L, + ["card"] = new Dictionary { ["cardId"] = 900811111L } } }, + }; + var map = new Dictionary(); + foreach (var (idx, cardId) in SVSim.BattleNode.Sessions.Dispatch.KnownListBuilder.MineAddOps(generatingOrderList)) + map[idx] = cardId; + + var playOrderList = new List + { + new Dictionary { ["move"] = new Dictionary + { ["idx"] = new List { 38L }, ["isSelf"] = 1L, ["from"] = 10L, ["to"] = 20L } }, + }; + var entry = SVSim.BattleNode.Sessions.Dispatch.KnownListBuilder.BuildPlayedCard(map, 38, playOrderList); + Assert.That(entry, Is.Not.Null, "the mined token resolves to a knownList entry"); + + var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( + PlayIdx: 38, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null); + var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, + Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); + + using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); + var ourEntry = ourDoc.RootElement.GetProperty("knownList")[0]; + using var prodDoc = JsonDocument.Parse(prodEntry); + + // We own idx/cardId/to; cost/clan/tribe are deferred (receiver re-derives from cardId). + foreach (var key in new[] { "idx", "cardId", "to" }) + { + Assert.That(ourEntry.TryGetProperty(key, out var ours), Is.True, $"knownList entry missing '{key}'"); + var prodVal = prodDoc.RootElement.GetProperty(key); + Assert.That(ours.ValueKind, Is.EqualTo(prodVal.ValueKind), $"'{key}' type category mismatch"); + } + Assert.That(ourEntry.GetProperty("cardId").GetInt64(), Is.EqualTo(900811111L)); + Assert.That(ourEntry.GetProperty("to").GetInt32(), Is.EqualTo(20)); + } } ///