diff --git a/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs b/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs index e6f98a8..c628139 100644 --- a/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs +++ b/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs @@ -189,6 +189,99 @@ public class BattleSessionDispatchTests Assert.That(a.Phase, Is.EqualTo(BattleSessionPhase.AwaitingInitNetwork)); } + [Test] + public void Pvp_InitBattle_from_A_pushes_Matched_with_B_oppoInfo_to_A_only() + { + var (s, a, b) = NewPvpSession(); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitNetwork)); + var routes = s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitBattle)); + + Assert.That(routes.Count, Is.EqualTo(1)); + Assert.That(routes[0].Target, Is.SameAs(a)); + Assert.That(routes[0].Frame.Uri, Is.EqualTo(NetworkBattleUri.Matched)); + + var body = (MatchedBody)routes[0].Frame.Body; + Assert.That(body.SelfInfo.UserName, Is.EqualTo("PlayerA"), + "Matched.selfInfo must reflect the sender (A)."); + Assert.That(body.SelfInfo.OppoId, Is.EqualTo(b.ViewerId)); + Assert.That(body.OppoInfo.UserName, Is.EqualTo("PlayerB"), + "Matched.oppoInfo must reflect the OTHER participant (B)."); + Assert.That(body.OppoInfo.OppoId, Is.EqualTo(a.ViewerId)); + Assert.That(body.SelfInfo.Seed, Is.EqualTo(body.OppoInfo.Seed), + "Both sides must see the same seed."); + } + + [Test] + public void Pvp_InitBattle_from_B_pushes_Matched_with_A_oppoInfo_to_B_only() + { + var (s, a, b) = NewPvpSession(); + s.ComputeFrames(b, NewEnvelope(NetworkBattleUri.InitNetwork)); + var routes = s.ComputeFrames(b, NewEnvelope(NetworkBattleUri.InitBattle)); + + Assert.That(routes.Count, Is.EqualTo(1)); + Assert.That(routes[0].Target, Is.SameAs(b)); + var body = (MatchedBody)routes[0].Frame.Body; + Assert.That(body.SelfInfo.UserName, Is.EqualTo("PlayerB")); + Assert.That(body.OppoInfo.UserName, Is.EqualTo("PlayerA")); + } + + [Test] + public void Pvp_Loaded_from_A_pushes_BattleStart_with_B_oppoInfo_plus_Deal_to_A() + { + var (s, a, b) = NewPvpSession(); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitNetwork)); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitBattle)); + var routes = s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.Loaded)); + + Assert.That(routes.Count, Is.EqualTo(2)); + Assert.That(routes[0].Frame.Uri, Is.EqualTo(NetworkBattleUri.BattleStart)); + Assert.That(routes[1].Frame.Uri, Is.EqualTo(NetworkBattleUri.Deal)); + Assert.That(routes.All(r => ReferenceEquals(r.Target, a)), Is.True); + + var bs = (BattleStartBody)routes[0].Frame.Body; + Assert.That(bs.SelfInfo.ClassId, Is.EqualTo("3"), "A is class 3 per fixture."); + Assert.That(bs.OppoInfo.ClassId, Is.EqualTo("5"), "B is class 5 per fixture."); + } + + [Test] + public void Pvp_Swap_from_A_pushes_SwapResponse_plus_Ready_to_A_only() + { + var (s, a, b) = NewPvpSession(); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitNetwork)); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitBattle)); + s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.Loaded)); + var routes = s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.Swap)); + + Assert.That(routes.Select(r => r.Frame.Uri), + Is.EqualTo(new[] { NetworkBattleUri.Swap, NetworkBattleUri.Ready })); + Assert.That(routes.All(r => ReferenceEquals(r.Target, a)), Is.True); + Assert.That(a.Phase, Is.EqualTo(BattleSessionPhase.AfterReady)); + Assert.That(b.Phase, Is.EqualTo(BattleSessionPhase.AwaitingInitNetwork), + "Swap from A doesn't advance B's phase."); + } + + private static (BattleSession, FakeRealParticipant, FakeRealParticipant) NewPvpSession() + { + var a = new FakeRealParticipant(viewerId: 1001, PlayerACtx()); + var b = new FakeRealParticipant(viewerId: 2002, PlayerBCtx()); + var s = new BattleSession("bid-pvp-1", BattleType.Pvp, a, b, NullLogger.Instance); + return (s, a, b); + } + + private static MatchContext PlayerACtx() => new( + SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 100_011_010L).ToList(), + ClassId: "3", CharaId: "3", CardMasterName: "card_master_node_10015", + CountryCode: "KOR", UserName: "PlayerA", SleeveId: "3000011", + EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0, + BattleType: 11); + + private static MatchContext PlayerBCtx() => new( + SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 200_011_010L).ToList(), + ClassId: "5", CharaId: "5", CardMasterName: "card_master_node_10015", + CountryCode: "JPN", UserName: "PlayerB", SleeveId: "3000022", + EmblemId: "701441022", DegreeId: "300004", FieldId: 44, IsOfficial: 0, + BattleType: 11); + private static (BattleSession, FakeRealParticipant, FakeParticipant) NewSession() { var a = new FakeRealParticipant(viewerId: 1, FixtureCtx());