test(battle-node): drop scripted smoke test; retarget deck-plumbing test to PvP
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,73 +15,6 @@ namespace SVSim.UnitTests.BattleNode.Integration;
|
||||
[TestFixture]
|
||||
public class BattleNodeFlowTests
|
||||
{
|
||||
/// <summary>
|
||||
/// End-to-end smoke for the v1.2 scripted lifecycle. Boots the EmulatedEntrypoint via
|
||||
/// SVSimTestFactory, mints a battle through IMatchingBridge with a fixture MatchContext,
|
||||
/// opens a raw Socket.IO v2 client against the in-process TestServer, and drives
|
||||
/// InitNetwork → Loaded → Swap → TurnEnd × 2, asserting the right scripted frames come
|
||||
/// back in order including the two-cycle three-frame opponent-turn loop (TurnStart +
|
||||
/// TurnEnd + Judge per cycle).
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Timeout(30000)]
|
||||
public async Task ClientWalksHandshakeToReady_ReceivesAllScriptedFrames()
|
||||
{
|
||||
await using var factory = new SVSimTestFactory();
|
||||
var bridge = factory.Services.GetRequiredService<IMatchingBridge>();
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
var ct = cts.Token;
|
||||
var pending = bridge.RegisterBattle(
|
||||
new SVSim.BattleNode.Bridge.BattlePlayer(906243102, FixtureCtx()),
|
||||
p2: null,
|
||||
SVSim.BattleNode.Sessions.BattleType.Scripted);
|
||||
|
||||
var key = MakeKey();
|
||||
var encryptedVid = NodeCrypto.EncryptForNode("906243102", key);
|
||||
var wsUri = new Uri($"ws://localhost/socket.io/?BattleId={pending.BattleId}&viewerId={Uri.EscapeDataString(encryptedVid)}&EIO=3&transport=websocket");
|
||||
|
||||
var wsClient = factory.Server.CreateWebSocketClient();
|
||||
var ws = await wsClient.ConnectAsync(wsUri, ct);
|
||||
await using var client = new RawSocketIoTestClient(ws);
|
||||
await client.ConsumeHandshakeAsync(ct);
|
||||
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.InitNetwork, pubSeq: 1), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.InitNetwork));
|
||||
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.InitBattle, pubSeq: 2), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Matched));
|
||||
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Loaded, pubSeq: 3), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.BattleStart));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Deal));
|
||||
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Swap, pubSeq: 4,
|
||||
body: new Dictionary<string, object?> { ["idxList"] = new List<object?>() }), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Swap));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Ready));
|
||||
|
||||
// --- v1.2 opponent turn loop: drive two consecutive cycles ---
|
||||
// Cycle 1: player ends turn -> server pushes opponent TurnStart + TurnEnd + Judge.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.TurnEnd, pubSeq: 5), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.TurnStart));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.TurnEnd));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Judge));
|
||||
|
||||
// Cycle 2: same burst again -- session phase reset to AfterReady, so the next TurnEnd matches.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.TurnEnd, pubSeq: 6), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.TurnStart));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.TurnEnd));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Judge));
|
||||
}
|
||||
|
||||
private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri, long pubSeq, Dictionary<string, object?>? body = null) =>
|
||||
new(uri, ViewerId: 906243102, Uuid: "udid-test", Bid: null, Try: 0,
|
||||
Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General
|
||||
: uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching
|
||||
: EmitCategory.Battle,
|
||||
PubSeq: pubSeq, PlaySeq: null, Body: new RawBody(body ?? new Dictionary<string, object?>()));
|
||||
|
||||
private static string MakeKey()
|
||||
{
|
||||
var seq = 0;
|
||||
@@ -101,7 +34,7 @@ public class BattleNodeFlowTests
|
||||
/// against an actual seeded viewer.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Timeout(30000)]
|
||||
[Timeout(60000)]
|
||||
public async Task Matched_frame_contains_drafted_deck_cards()
|
||||
{
|
||||
await using var factory = new SVSimTestFactory();
|
||||
@@ -133,21 +66,23 @@ public class BattleNodeFlowTests
|
||||
var ctx = await builder.BuildForTwoPickAsync(vid);
|
||||
var bridge = factory.Services.GetRequiredService<IMatchingBridge>();
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(45));
|
||||
var ct = cts.Token;
|
||||
var vidB = vid + 1;
|
||||
var pending = bridge.RegisterBattle(
|
||||
new SVSim.BattleNode.Bridge.BattlePlayer(vid, ctx),
|
||||
p2: null,
|
||||
SVSim.BattleNode.Sessions.BattleType.Scripted);
|
||||
new SVSim.BattleNode.Bridge.BattlePlayer(vidB, FixtureCtx()),
|
||||
SVSim.BattleNode.Sessions.BattleType.Pvp);
|
||||
|
||||
var key = MakeKey();
|
||||
var encryptedVid = NodeCrypto.EncryptForNode(vid.ToString(), key);
|
||||
var wsUri = new Uri($"ws://localhost/socket.io/?BattleId={pending.BattleId}&viewerId={Uri.EscapeDataString(encryptedVid)}&EIO=3&transport=websocket");
|
||||
|
||||
var wsClient = factory.Server.CreateWebSocketClient();
|
||||
var ws = await wsClient.ConnectAsync(wsUri, ct);
|
||||
await using var client = new RawSocketIoTestClient(ws);
|
||||
await client.ConsumeHandshakeAsync(ct);
|
||||
// PvP constructs the BattleSession on the SECOND arriver, so connecting only P1 parks it
|
||||
// forever. Connect BOTH clients, then drive P1 (the seeded viewer) through
|
||||
// InitNetwork/InitBattle to harvest its own Matched — pushed to the sender before the
|
||||
// mulligan barrier, so B's handshake is not needed for P1's Matched to arrive.
|
||||
var (client, clientB) = await ConnectBothAsync(factory, pending.BattleId, vid, vidB, key, ct);
|
||||
await using var _a = client;
|
||||
await using var _b = clientB;
|
||||
await Task.WhenAll(client.ConsumeHandshakeAsync(ct), clientB.ConsumeHandshakeAsync(ct));
|
||||
|
||||
// InitNetwork → ack
|
||||
await client.SendMsgAsync(MakeEnvelopeWith(vid, NetworkBattleUri.InitNetwork, pubSeq: 1), key, ct);
|
||||
|
||||
Reference in New Issue
Block a user