test(battle-node): drive PvP flow handshakes through the mulligan barrier
The three PvP BattleNodeFlowTests drove each client's handshake to Ready independently; the new barrier withholds Ready until both sides swap, so the single-client helper timed out. Split DriveHandshakeAsync into DriveThroughSwapAsync (stops at SwapResponse) + DrivePvpHandshakeAsync (drives both, then drains the barrier-released Ready for each). Scripted/Bot single-client paths are unaffected (non-IHasHandshakePhase opponent releases Ready immediately). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -218,8 +218,7 @@ public class BattleNodeFlowTests
|
||||
await using var _b = clientB;
|
||||
await Task.WhenAll(clientA.ConsumeHandshakeAsync(ct), clientB.ConsumeHandshakeAsync(ct));
|
||||
|
||||
await DriveHandshakeAsync(clientA, vidA, key, ct);
|
||||
await DriveHandshakeAsync(clientB, vidB, key, ct);
|
||||
await DrivePvpHandshakeAsync(clientA, vidA, clientB, vidB, key, ct);
|
||||
|
||||
// Both are now AfterReady. A sends TurnEnd; both should receive TurnEnd + Judge.
|
||||
await clientA.SendMsgAsync(MakeEnvelopeWith(vidA, NetworkBattleUri.TurnEnd, pubSeq: 5), key, ct);
|
||||
@@ -268,8 +267,7 @@ public class BattleNodeFlowTests
|
||||
await using var _a = clientA;
|
||||
await using var _b = clientB;
|
||||
await Task.WhenAll(clientA.ConsumeHandshakeAsync(ct), clientB.ConsumeHandshakeAsync(ct));
|
||||
await DriveHandshakeAsync(clientA, vidA, key, ct);
|
||||
await DriveHandshakeAsync(clientB, vidB, key, ct);
|
||||
await DrivePvpHandshakeAsync(clientA, vidA, clientB, vidB, key, ct);
|
||||
|
||||
// A retires.
|
||||
await clientA.SendMsgAsync(MakeEnvelopeWith(vidA, NetworkBattleUri.Retire, pubSeq: 5), key, ct);
|
||||
@@ -314,8 +312,7 @@ public class BattleNodeFlowTests
|
||||
var (clientA, clientB) = await ConnectBothAsync(factory, pending.BattleId, vidA, vidB, key, ct);
|
||||
await using var _b = clientB;
|
||||
await Task.WhenAll(clientA.ConsumeHandshakeAsync(ct), clientB.ConsumeHandshakeAsync(ct));
|
||||
await DriveHandshakeAsync(clientA, vidA, key, ct);
|
||||
await DriveHandshakeAsync(clientB, vidB, key, ct);
|
||||
await DrivePvpHandshakeAsync(clientA, vidA, clientB, vidB, key, ct);
|
||||
|
||||
// Abruptly close A's WS (no Retire).
|
||||
await clientA.DisposeAsync();
|
||||
@@ -492,7 +489,10 @@ public class BattleNodeFlowTests
|
||||
|
||||
// -- helpers -------------------------------------------------------------
|
||||
|
||||
private static async Task DriveHandshakeAsync(
|
||||
/// <summary>Drives one PvP client from InitNetwork through Swap, stopping at the
|
||||
/// SwapResponse. Ready is NOT received here — the mulligan barrier withholds it until
|
||||
/// BOTH sides have swapped, so the caller drains it after driving both sides.</summary>
|
||||
private static async Task DriveThroughSwapAsync(
|
||||
RawSocketIoTestClient client, long vid, string key, CancellationToken ct)
|
||||
{
|
||||
long pubSeq = 1;
|
||||
@@ -507,7 +507,23 @@ public class BattleNodeFlowTests
|
||||
await client.SendMsgAsync(MakeEnvelopeWith(vid, NetworkBattleUri.Swap, pubSeq++,
|
||||
body: new Dictionary<string, object?> { ["idxList"] = new List<object?>() }), key, ct);
|
||||
await client.ReceiveSynchronizeAsync(ct); // Swap response
|
||||
await client.ReceiveSynchronizeAsync(ct); // Ready
|
||||
}
|
||||
|
||||
/// <summary>Drives both PvP clients through the full handshake including the mulligan
|
||||
/// barrier: each side swaps first (Ready withheld), then the second swap releases Ready
|
||||
/// to both. Leaves both at AfterReady with pubSeq up to 4 consumed per client.</summary>
|
||||
private static async Task DrivePvpHandshakeAsync(
|
||||
RawSocketIoTestClient clientA, long vidA,
|
||||
RawSocketIoTestClient clientB, long vidB, string key, CancellationToken ct)
|
||||
{
|
||||
await DriveThroughSwapAsync(clientA, vidA, key, ct);
|
||||
await DriveThroughSwapAsync(clientB, vidB, key, ct);
|
||||
|
||||
// B's Swap (the second) releases Ready to both sides.
|
||||
var aReady = await clientA.ReceiveSynchronizeAsync(ct);
|
||||
Assert.That(aReady.Uri, Is.EqualTo(NetworkBattleUri.Ready));
|
||||
var bReady = await clientB.ReceiveSynchronizeAsync(ct);
|
||||
Assert.That(bReady.Uri, Is.EqualTo(NetworkBattleUri.Ready));
|
||||
}
|
||||
|
||||
private static async Task<(RawSocketIoTestClient, RawSocketIoTestClient)> ConnectBothAsync(
|
||||
|
||||
Reference in New Issue
Block a user