// SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs using Microsoft.Extensions.Logging.Abstractions; using NUnit.Framework; using SVSim.BattleNode.Protocol; using SVSim.BattleNode.Sessions; namespace SVSim.UnitTests.BattleNode.Sessions; [TestFixture] public class BattleSessionDispatchTests { private static BattleSession NewSession() { // ws is unused by ComputeResponses; pass null! and rely on the test never invoking the pump. return new BattleSession(ws: null!, battleId: "bid-1", viewerId: 1, log: NullLogger.Instance); } 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 Dictionary()); [Test] public void InitNetwork_PushesAckOnly_TransitionsToAwaitingInitBattle() { var s = NewSession(); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitNetwork)); Assert.That(responses.Select(r => r.Envelope.Uri), Is.EqualTo(new[] { NetworkBattleUri.InitNetwork })); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.AwaitingInitBattle)); } [Test] public void InitBattle_PushesMatched_TransitionsToAwaitingLoaded() { var s = NewSession(); s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitNetwork)); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitBattle)); Assert.That(responses.Single().Envelope.Uri, Is.EqualTo(NetworkBattleUri.Matched)); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.AwaitingLoaded)); } [Test] public void Loaded_PushesBattleStartThenDeal_TransitionsToAwaitingSwap() { var s = NewSession(); s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitNetwork)); s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitBattle)); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.Loaded)); Assert.That(responses.Select(r => r.Envelope.Uri), Is.EqualTo(new[] { NetworkBattleUri.BattleStart, NetworkBattleUri.Deal })); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.AwaitingSwap)); } [Test] public void Swap_PushesSwapResponseThenReady_TransitionsToAfterReady() { var s = NewSession(); s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitNetwork)); s.ComputeResponses(NewEnvelope(NetworkBattleUri.InitBattle)); s.ComputeResponses(NewEnvelope(NetworkBattleUri.Loaded)); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.Swap)); Assert.That(responses.Select(r => r.Envelope.Uri), Is.EqualTo(new[] { NetworkBattleUri.Swap, NetworkBattleUri.Ready })); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.AfterReady)); } [Test] public void Retire_PushesBattleFinishNoContest_TransitionsToTerminal() { var s = NewSession(); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.Retire)); var (env, noStock) = responses.Single(); Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.BattleFinish)); Assert.That(noStock, Is.True); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.Terminal)); } [Test] public void Kill_PushesBattleFinishNoContest_TransitionsToTerminal() { var s = NewSession(); var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.Kill)); var (env, noStock) = responses.Single(); Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.BattleFinish)); Assert.That(noStock, Is.True); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.Terminal)); } [Test] public void Swap_ArrivingBeforeLoaded_ProducesNoResponseAndDoesNotAdvancePhase() { var s = NewSession(); // Skip Loaded — fire Swap straight out of AwaitingInitNetwork. var responses = s.ComputeResponses(NewEnvelope(NetworkBattleUri.Swap)); Assert.That(responses, Is.Empty); Assert.That(s.Phase, Is.EqualTo(BattleSessionPhase.AwaitingInitNetwork)); } }