Behavior-preserving; 271 BattleNode/Matching/Services tests green, full solution builds. "BattleType" meant two things: the Sessions.BattleType enum (Pvp/Bot) and an int "mode id" field. Renamed the int field on MatchContext AND the BattleStartBody wire DTO to BattleModeId (wire key stays "battleType" via JsonPropertyName), so BattleType now means only the enum project-wide. New Bridge/BattleModes.cs (TakeTwo = 11) replaces every 11 literal — both prod MatchContextBuilder sites and the test fixtures/assertions. The arbitrary-passthrough 42 and bot 0 stay literal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
2.6 KiB
C#
79 lines
2.6 KiB
C#
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NUnit.Framework;
|
|
using SVSim.BattleNode.Bridge;
|
|
using SVSim.BattleNode.Hosting;
|
|
using SVSim.BattleNode.Sessions.Participants;
|
|
using SVSim.UnitTests.BattleNode.Infrastructure;
|
|
|
|
namespace SVSim.UnitTests.BattleNode.Hosting;
|
|
|
|
[TestFixture]
|
|
public class WaitingRoomTests
|
|
{
|
|
[Test]
|
|
public void Pair_on_empty_slot_returns_null()
|
|
{
|
|
var room = new WaitingRoom();
|
|
var participant = NewParticipant(viewerId: 1);
|
|
|
|
var paired = room.Pair("bid-1", participant);
|
|
|
|
Assert.That(paired, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Park_then_Pair_resolves_with_each_arriver_seeing_the_other()
|
|
{
|
|
var room = new WaitingRoom();
|
|
var first = NewParticipant(viewerId: 1);
|
|
var second = NewParticipant(viewerId: 2);
|
|
|
|
var parkTask = room.ParkAsync("bid-1", first, TimeSpan.FromSeconds(5), CancellationToken.None);
|
|
// Yield so Park's TryAdd lands first.
|
|
await Task.Yield();
|
|
|
|
var firstReturnedToSecond = room.Pair("bid-1", second);
|
|
var secondReturnedToFirst = await parkTask;
|
|
|
|
Assert.That(firstReturnedToSecond, Is.SameAs(first),
|
|
"Pair must return the first arriver to the second.");
|
|
Assert.That(secondReturnedToFirst, Is.SameAs(second),
|
|
"Park must return the second arriver to the first.");
|
|
}
|
|
|
|
[Test]
|
|
public async Task Park_times_out_returns_null_and_evicts_slot()
|
|
{
|
|
var room = new WaitingRoom();
|
|
var first = NewParticipant(viewerId: 1);
|
|
|
|
var second = await room.ParkAsync("bid-1", first, TimeSpan.FromMilliseconds(50), CancellationToken.None);
|
|
|
|
Assert.That(second, Is.Null);
|
|
// Slot should be evicted; a subsequent Pair returns null (no first arriver).
|
|
var paired = room.Pair("bid-1", NewParticipant(viewerId: 2));
|
|
Assert.That(paired, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Evict_is_idempotent()
|
|
{
|
|
var room = new WaitingRoom();
|
|
|
|
Assert.DoesNotThrow(() => room.Evict("bid-1"));
|
|
Assert.DoesNotThrow(() => room.Evict("bid-1"));
|
|
}
|
|
|
|
private static RealParticipant NewParticipant(long viewerId)
|
|
{
|
|
var ws = new TestWebSocket();
|
|
var ctx = new MatchContext(
|
|
SelfDeckCardIds: Array.Empty<long>(),
|
|
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
|
CountryCode: "KOR", UserName: "Player", SleeveId: "0",
|
|
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0,
|
|
BattleModeId: BattleModes.TakeTwo);
|
|
return new RealParticipant(ws, viewerId, ctx, NullLogger<RealParticipant>.Instance);
|
|
}
|
|
}
|