89 lines
3.5 KiB
C#
89 lines
3.5 KiB
C#
using System.Collections.Generic;
|
|
using Xunit;
|
|
using XWorld.Framework;
|
|
using XWorld.Server.Host;
|
|
using RPS.Core;
|
|
|
|
namespace XWorld.Server.Host.Tests
|
|
{
|
|
/// <summary>
|
|
/// Proves "framework zero-change loads a new game via ALC": loads RPS through the existing ALC contract.
|
|
/// Loads RPS from TestGames/rps/1, drives it with 1 human + 1 AI seat, advances past 60s,
|
|
/// asserts broadcasts decode and game progresses to Finished.
|
|
/// </summary>
|
|
public class RpsModuleLoadTests
|
|
{
|
|
private sealed class FakeOutput : IRoomOutput
|
|
{
|
|
public readonly List<NetMessage> Broadcasts = new List<NetMessage>();
|
|
public void Broadcast(NetMessage m) => Broadcasts.Add(m);
|
|
public void SendTo(int playerId, NetMessage m) { }
|
|
}
|
|
|
|
private static readonly IReadOnlyList<PlayerInfo> Players = new[]
|
|
{
|
|
new PlayerInfo { PlayerId = 1, Name = "Human", IsAI = false },
|
|
new PlayerInfo { PlayerId = -1, Name = "AI", IsAI = true },
|
|
};
|
|
|
|
[Fact]
|
|
public void Load_Rps_v1_Via_ALC_ProducesRoom_ThatImplementsSharedInterface()
|
|
{
|
|
var module = new GameModuleLoader().Load(TestGames.Root, "rps", 1);
|
|
Assert.Equal("rps", module.GameId);
|
|
Assert.Equal(1, module.Version);
|
|
|
|
IGameServerRoom room = module.CreateRoom();
|
|
Assert.NotNull(room);
|
|
|
|
// Verify ALC isolation: the implementation type lives in the game ALC,
|
|
// not the default context where IGameServerRoom is defined.
|
|
Assert.NotEqual(typeof(IGameServerRoom).Assembly, room.GetType().Assembly);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_Rps_v1_Via_ALC_BroadcastsSnapshots_AndProgressesToFinished()
|
|
{
|
|
var module = new GameModuleLoader().Load(TestGames.Root, "rps", 1);
|
|
IGameServerRoom room = module.CreateRoom();
|
|
|
|
var output = new FakeOutput();
|
|
bool ended = false;
|
|
var ctx = new RoomCtx(
|
|
new DeterministicRandom(99),
|
|
new ManualClock(),
|
|
new ConsoleLogger(),
|
|
new InMemoryStorage(),
|
|
output,
|
|
_ => ended = true);
|
|
|
|
room.OnRoomStart(Players, ctx);
|
|
|
|
// Initial broadcast on start
|
|
Assert.True(output.Broadcasts.Count >= 1);
|
|
|
|
// Decode the first snapshot to confirm Round=1, Phase=Choosing
|
|
var logic = new RpsLogic();
|
|
var first = logic.Decode(output.Broadcasts[0].Payload);
|
|
Assert.Equal(1, first.Round);
|
|
Assert.Equal(RpsPhase.Choosing, first.Phase);
|
|
|
|
// Drive with large dt to tick through all rounds within 60s total:
|
|
// Each tick dt=10f: 10+10+10+10+10+10 = 60s → should finish by tick 6
|
|
for (int i = 0; i < 10 && !ended; i++)
|
|
room.OnTick(10f);
|
|
|
|
Assert.True(ended, "EndRoom must have been called (game finished within 10 large ticks)");
|
|
Assert.True(output.Broadcasts.Count > 1, "Should have multiple broadcasts");
|
|
|
|
// Decode the last snapshot — must be Finished
|
|
var last = output.Broadcasts[output.Broadcasts.Count - 1];
|
|
var finalState = logic.Decode(last.Payload);
|
|
Assert.Equal(RpsPhase.Finished, finalState.Phase);
|
|
// Winner is 0, 1, or 2 (draw) — must be a valid value
|
|
Assert.True(finalState.Winner >= 0 && finalState.Winner <= 2,
|
|
$"Winner should be 0, 1, or 2 but was {finalState.Winner}");
|
|
}
|
|
}
|
|
}
|