using Xunit; using XWorld.Server.Gateway; namespace XWorld.Server.Gateway.Tests { public class MatchmakerTests { [Fact] public void TwoEnqueues_SameBucket_PlayerCount2_Poll_ReturnsTwoHumanMatch() { var mm = new Matchmaker(timeoutTicks: 100); mm.Enqueue(pid: 1, gameId: "rps", version: 1, playerCount: 2, tick: 0); mm.Enqueue(pid: 2, gameId: "rps", version: 1, playerCount: 2, tick: 0); var matches = mm.Poll(currentTick: 0); Assert.Single(matches); var m = matches[0]; Assert.Equal("rps", m.GameId); Assert.Equal(1, m.Version); Assert.Equal(2, m.Seats.Count); Assert.False(m.Seats[0].IsAi); Assert.False(m.Seats[1].IsAi); Assert.Equal(1, m.Seats[0].PlayerId); Assert.Equal(2, m.Seats[1].PlayerId); } [Fact] public void SingleEnqueue_PollAfterTimeout_Returns1Human1AiMatch() { var mm = new Matchmaker(timeoutTicks: 10); mm.Enqueue(pid: 7, gameId: "rps", version: 1, playerCount: 2, tick: 0); // Not timed out yet var early = mm.Poll(currentTick: 9); Assert.Empty(early); // Now timed out (currentTick - enqueuedTick > timeoutTicks → 11 - 0 = 11 > 10) var matches = mm.Poll(currentTick: 10); Assert.Single(matches); var m = matches[0]; Assert.Equal(2, m.Seats.Count); // First seat is the real player Assert.False(m.Seats[0].IsAi); Assert.Equal(7, m.Seats[0].PlayerId); // Second seat is AI with negative pid Assert.True(m.Seats[1].IsAi); Assert.True(m.Seats[1].PlayerId < 0); } [Fact] public void NotFull_NotTimedOut_Poll_ReturnsEmpty() { var mm = new Matchmaker(timeoutTicks: 100); mm.Enqueue(pid: 3, gameId: "rps", version: 1, playerCount: 2, tick: 5); var matches = mm.Poll(currentTick: 50); // 50-5=45 <= 100, not timed out, not full Assert.Empty(matches); } [Fact] public void PlayerCount1_SingleEnqueue_PollImmediately_ReturnsMatch() { // playerCount=1 应立即成团(Matchmaker 仅按 playerCount 分桶,不加载具体游戏) var mm = new Matchmaker(timeoutTicks: 100); mm.Enqueue(pid: 5, gameId: "solo1p", version: 1, playerCount: 1, tick: 0); var matches = mm.Poll(currentTick: 0); Assert.Single(matches); Assert.Single(matches[0].Seats); Assert.False(matches[0].Seats[0].IsAi); } [Fact] public void Remove_DequeuesWaiter_SoNoMatchForms() { var mm = new Matchmaker(timeoutTicks: 100); mm.Enqueue(1, "rps", 1, 2, tick: 0); Assert.True(mm.Remove(1)); mm.Enqueue(2, "rps", 1, 2, tick: 0); // 只剩玩家2,凑不齐2人 Assert.Empty(mm.Poll(1)); // 未满未超时 → 无对局 Assert.False(mm.Remove(999)); // 不存在的 pid } } }