diff --git a/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs b/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs index 0335651..f2623be 100644 --- a/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs +++ b/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs @@ -166,62 +166,6 @@ public class ScriptedLifecycleTests "single-arg overload (non-interactive opponent) keeps the placeholder hand."); } - [Test] - public void BuildClientSwap_has_empty_idxList_in_RawBody() - { - var env = ScriptedLifecycle.BuildClientSwap(); - Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.Swap)); - Assert.That(env.ViewerId, Is.EqualTo(ScriptedLifecycle.FakeOpponentViewerId)); - - var raw = (RawBody)env.Body; - Assert.That(raw.Entries.ContainsKey("idxList"), Is.True); - var idx = (System.Collections.IEnumerable)raw.Entries["idxList"]!; - Assert.That(idx.Cast().Count(), Is.EqualTo(0), "bot keeps its dealt hand (empty mulligan)."); - } - - [Test] - public void BuildClientHandshakeFrames_carry_expected_uris() - { - Assert.That(ScriptedLifecycle.BuildClientInitNetwork().Uri, Is.EqualTo(NetworkBattleUri.InitNetwork)); - Assert.That(ScriptedLifecycle.BuildClientInitBattle().Uri, Is.EqualTo(NetworkBattleUri.InitBattle)); - Assert.That(ScriptedLifecycle.BuildClientLoaded().Uri, Is.EqualTo(NetworkBattleUri.Loaded)); - } - - [Test] - public void BuildOpponentTurnStart_HasUriTurnStartAndSpin() - { - var env = ScriptedLifecycle.BuildOpponentTurnStart(); - Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.TurnStart)); - var body = (OpponentTurnStartBody)env.Body; - Assert.That(body.Spin, Is.EqualTo(100)); - } - - [Test] - public void BuildOpponentTurnEnd_emits_TurnEnd_uri_with_turn_state_zero() - { - var env = ScriptedLifecycle.BuildOpponentTurnEnd(); - - Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.TurnEnd)); - Assert.That(env.ViewerId, Is.EqualTo(ScriptedLifecycle.FakeOpponentViewerId)); - Assert.That(env.Cat, Is.EqualTo(EmitCategory.Battle)); - var body = (TurnEndBody)env.Body; - Assert.That(body.TurnState, Is.EqualTo(0)); - Assert.That(body.ResultCode, Is.EqualTo(1)); - } - - [Test] - public void BuildOpponentJudge_emits_Judge_uri_with_spin_and_default_result_code() - { - var env = ScriptedLifecycle.BuildOpponentJudge(); - - Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.Judge)); - Assert.That(env.ViewerId, Is.EqualTo(ScriptedLifecycle.FakeOpponentViewerId)); - Assert.That(env.Cat, Is.EqualTo(EmitCategory.Battle)); - var body = (JudgeBody)env.Body; - Assert.That(body.Spin, Is.EqualTo(ScriptedProfiles.OpponentJudgeSpin)); - Assert.That(body.ResultCode, Is.EqualTo(1)); - } - private static MatchContext FixtureCtx(IReadOnlyList? deck = null) => new( SelfDeckCardIds: deck ?? Enumerable.Range(1, 30).Select(i => 100_011_010L).ToList(), ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015", diff --git a/SVSim.UnitTests/BattleNode/Lifecycle/TypedBodyWireShapeTests.cs b/SVSim.UnitTests/BattleNode/Lifecycle/TypedBodyWireShapeTests.cs index 11c7fb1..c796d3a 100644 --- a/SVSim.UnitTests/BattleNode/Lifecycle/TypedBodyWireShapeTests.cs +++ b/SVSim.UnitTests/BattleNode/Lifecycle/TypedBodyWireShapeTests.cs @@ -147,42 +147,6 @@ public class TypedBodyWireShapeTests Assert.That(node["oppo"]!.AsArray().Count, Is.EqualTo(3)); } - [Test] - public void BuildOpponentTurnStart_SerializesSpinAndResultCode() - { - var env = ScriptedLifecycle.BuildOpponentTurnStart(); - var json = MsgEnvelope.ToJson(env); - var node = JsonNode.Parse(json)!.AsObject(); - - Assert.That(node["spin"]!.GetValue(), Is.EqualTo(100)); - Assert.That(node["resultCode"]!.GetValue(), Is.EqualTo(1)); - Assert.That(node["uri"]!.GetValue(), Is.EqualTo("TurnStart")); - } - - [Test] - public void BuildOpponentTurnEnd_SerializesTurnStateAndResultCode() - { - var env = ScriptedLifecycle.BuildOpponentTurnEnd(); - var json = MsgEnvelope.ToJson(env); - var node = JsonNode.Parse(json)!.AsObject(); - - Assert.That(node["turnState"]!.GetValue(), Is.EqualTo(0)); - Assert.That(node["resultCode"]!.GetValue(), Is.EqualTo(1)); - Assert.That(node["uri"]!.GetValue(), Is.EqualTo("TurnEnd")); - } - - [Test] - public void BuildOpponentJudge_SerializesSpinAndResultCode() - { - var env = ScriptedLifecycle.BuildOpponentJudge(); - var json = MsgEnvelope.ToJson(env); - var node = JsonNode.Parse(json)!.AsObject(); - - Assert.That(node["spin"]!.GetValue(), Is.EqualTo(100)); - Assert.That(node["resultCode"]!.GetValue(), Is.EqualTo(1)); - Assert.That(node["uri"]!.GetValue(), Is.EqualTo("Judge")); - } - /// /// Wire-shape fixture: 30 copies of the legacy DummyCardId (100_011_010L) so the /// existing literal assertions on selfDeck[0].cardId (line 81 above) keep working diff --git a/SVSim.UnitTests/BattleNode/Sessions/Participants/ScriptedBotParticipantTests.cs b/SVSim.UnitTests/BattleNode/Sessions/Participants/ScriptedBotParticipantTests.cs deleted file mode 100644 index e6e9e2e..0000000 --- a/SVSim.UnitTests/BattleNode/Sessions/Participants/ScriptedBotParticipantTests.cs +++ /dev/null @@ -1,87 +0,0 @@ -using NUnit.Framework; -using SVSim.BattleNode.Lifecycle; -using SVSim.BattleNode.Protocol; -using SVSim.BattleNode.Protocol.Bodies; -using SVSim.BattleNode.Sessions; -using SVSim.BattleNode.Sessions.Participants; - -namespace SVSim.UnitTests.BattleNode.Sessions.Participants; - -[TestFixture] -public class ScriptedBotParticipantTests -{ - [Test] - public async Task RunAsync_emits_InitNetwork_to_kick_off_handshake() - { - var p = new ScriptedBotParticipant(); - var emitted = new List(); - p.FrameEmitted += (env, _) => { emitted.Add(env.Uri); return Task.CompletedTask; }; - - await p.RunAsync(CancellationToken.None); - - Assert.That(emitted, Is.EqualTo(new[] { NetworkBattleUri.InitNetwork })); - } - - [TestCase(NetworkBattleUri.InitNetwork, NetworkBattleUri.InitBattle)] - [TestCase(NetworkBattleUri.Matched, NetworkBattleUri.Loaded)] - [TestCase(NetworkBattleUri.Deal, NetworkBattleUri.Swap)] - public async Task PushAsync_handshake_push_emits_next_client_frame( - NetworkBattleUri pushed, NetworkBattleUri expectedEmit) - { - var p = new ScriptedBotParticipant(); - var emitted = new List(); - p.FrameEmitted += (env, _) => { emitted.Add(env.Uri); return Task.CompletedTask; }; - - await p.PushAsync(NewEnvelope(pushed), noStock: false, CancellationToken.None); - - Assert.That(emitted, Is.EqualTo(new[] { expectedEmit })); - } - - [Test] - public async Task PushAsync_TurnEnd_fires_three_FrameEmitted_in_order() - { - var p = new ScriptedBotParticipant(); - var emitted = new List(); - p.FrameEmitted += (env, _) => { emitted.Add(env.Uri); return Task.CompletedTask; }; - - await p.PushAsync(NewEnvelope(NetworkBattleUri.TurnEnd), noStock: false, CancellationToken.None); - - Assert.That(emitted, Is.EqualTo(new[] - { - NetworkBattleUri.TurnStart, NetworkBattleUri.TurnEnd, NetworkBattleUri.Judge, - })); - } - - [Test] - public async Task PushAsync_non_reactive_uris_emit_nothing() - { - var p = new ScriptedBotParticipant(); - var fired = 0; - p.FrameEmitted += (_, _) => { fired++; return Task.CompletedTask; }; - - foreach (var uri in new[] - { - NetworkBattleUri.BattleStart, NetworkBattleUri.Swap, NetworkBattleUri.Ready, - NetworkBattleUri.PlayActions, NetworkBattleUri.TurnStart, NetworkBattleUri.Echo, - NetworkBattleUri.Judge, NetworkBattleUri.BattleFinish, NetworkBattleUri.TurnEndFinal, - }) - { - await p.PushAsync(NewEnvelope(uri), noStock: false, CancellationToken.None); - } - - Assert.That(fired, Is.EqualTo(0)); - } - - [Test] - public void Implements_IHasHandshakePhase_starting_at_AwaitingInitNetwork() - { - var p = new ScriptedBotParticipant(); - Assert.That(p, Is.InstanceOf()); - Assert.That(((IHasHandshakePhase)p).Phase, Is.EqualTo(BattleSessionPhase.AwaitingInitNetwork)); - } - - private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) => - new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, - Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, - Body: new ResultCodeOnlyBody()); -} diff --git a/SVSim.UnitTests/BattleNode/Sessions/ScriptedBotHandshakeIntegrationTests.cs b/SVSim.UnitTests/BattleNode/Sessions/ScriptedBotHandshakeIntegrationTests.cs deleted file mode 100644 index 7f45d29..0000000 --- a/SVSim.UnitTests/BattleNode/Sessions/ScriptedBotHandshakeIntegrationTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.Extensions.Logging.Abstractions; -using NUnit.Framework; -using SVSim.BattleNode.Bridge; -using SVSim.BattleNode.Lifecycle; -using SVSim.BattleNode.Protocol; -using SVSim.BattleNode.Protocol.Bodies; -using SVSim.BattleNode.Sessions; -using SVSim.BattleNode.Sessions.Participants; - -namespace SVSim.UnitTests.BattleNode.Sessions; - -[TestFixture] -public class ScriptedBotHandshakeIntegrationTests -{ - [Test] - public async Task RealBot_completes_handshake_and_both_sides_reach_Ready_through_barrier() - { - var bot = new ScriptedBotParticipant(); // B — real participant under test - var player = new RecordingPlayer(viewerId: 1, PlayerCtx()); - - var session = new BattleSession("bid-int-1", BattleType.Scripted, player, bot, - NullLogger.Instance); - - // Wire the bot's emissions into the session the way BattleSession's ctor does for - // real participants (the ctor already subscribed; we just need to start RunAsync, - // which fires the bot's InitNetwork and lets the reactive cascade settle). - await bot.RunAsync(CancellationToken.None); - - // After the bot's cascade, the bot has emitted InitNetwork→InitBattle→Loaded→Swap - // and is parked awaiting the player's Swap (barrier holds its Ready). - Assert.That(((IHasHandshakePhase)bot).Phase, Is.EqualTo(BattleSessionPhase.AfterReady), - "Bot should have driven its own handshake to AfterReady."); - Assert.That(player.Received, Does.Not.Contain(NetworkBattleUri.Ready), - "Player hasn't swapped yet → barrier must withhold the bot-triggered Ready from the player too."); - - // Now drive the player's handshake through Swap; the player's Swap should release - // Ready to BOTH sides. - session.ComputeFrames(player, Env(NetworkBattleUri.InitNetwork)); - session.ComputeFrames(player, Env(NetworkBattleUri.InitBattle)); - var loadedRoutes = session.ComputeFrames(player, Env(NetworkBattleUri.Loaded)); - var bs = (BattleStartBody)loadedRoutes[0].Frame.Body; - Assert.That(bs.TurnState, Is.EqualTo(0), "Player (A) goes first."); - - var swapRoutes = session.ComputeFrames(player, Env(NetworkBattleUri.Swap)); - Assert.That(swapRoutes.Any(r => ReferenceEquals(r.Target, player) && r.Frame.Uri == NetworkBattleUri.Ready), - Is.True, "Player's Swap (the second) releases Ready to the player."); - Assert.That(swapRoutes.Any(r => ReferenceEquals(r.Target, bot) && r.Frame.Uri == NetworkBattleUri.Ready), - Is.True, "...and to the bot."); - } - - private sealed class RecordingPlayer : IBattleParticipant, IHasHandshakePhase - { - public long ViewerId { get; } - public MatchContext Context { get; } - public BattleSessionPhase Phase { get; set; } = BattleSessionPhase.AwaitingInitNetwork; - public List Received { get; } = new(); - public event Func? FrameEmitted; - public RecordingPlayer(long viewerId, MatchContext ctx) { ViewerId = viewerId; Context = ctx; } - public Task PushAsync(MsgEnvelope env, bool noStock, CancellationToken ct) - { Received.Add(env.Uri); return Task.CompletedTask; } - public Task RunAsync(CancellationToken ct) => Task.CompletedTask; - public Task TerminateAsync(BattleFinishReason reason) => Task.CompletedTask; - public ValueTask DisposeAsync() => ValueTask.CompletedTask; - private void Touch() => FrameEmitted?.Invoke(null!, default); - } - - private static MatchContext PlayerCtx() => new( - SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 100_011_010L).ToList(), - ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015", - CountryCode: "KOR", UserName: "Player", SleeveId: "3000011", - EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0, BattleType: 11); - - private static MsgEnvelope Env(NetworkBattleUri uri) => - new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, Cat: EmitCategory.Battle, - PubSeq: null, PlaySeq: null, Body: new RawBody(new Dictionary())); -}