using FluentAssertions;
using Game.TicTacToe;
using OECS;
using OECS.PlayTest;
using Xunit;
using Xunit.Abstractions;
namespace Game.TicTacToe.Tests;
///
/// Baseline snapshot and log tests for TicTacToe.
/// Captures the world state as text and R3 change logs so they can be
/// reviewed manually and compared across changes.
///
public class SnapshotTests
{
private readonly ITestOutputHelper _output;
public SnapshotTests(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void Snapshot_InitialBoard()
{
var world = new World();
for (int r = 0; r < 3; r++)
for (int c = 0; c < 3; c++)
world.AddComponent(world.CreateEntity(), new Cell { Row = r, Col = c });
world.SetSingleton(new GameState
{
CurrentPlayer = Player.X,
Status = GameStatus.Playing,
MoveCount = 0
});
var snapshot = TestHelpers.SnapshotWorld(world);
_output.WriteLine(snapshot);
snapshot.Should().Contain("GameState: X's turn, 0 moves");
snapshot.Should().Contain("Cells: 9 total, 0 marked");
}
[Fact]
public void Snapshot_AfterThreeMoves()
{
var (world, group) = TestHelpers.SetupGame();
TestHelpers.PlayMoves(world, group, (0, 0), (1, 1), (2, 2));
var snapshot = TestHelpers.SnapshotWorld(world);
_output.WriteLine(snapshot);
snapshot.Should().Contain("GameState: O's turn, 3 moves");
snapshot.Should().Contain("Cells: 9 total, 3 marked");
snapshot.Should().Contain("(0,0): X");
snapshot.Should().Contain("(1,1): O");
snapshot.Should().Contain("(2,2): X");
}
[Fact]
public void Snapshot_XWins()
{
var (world, group) = TestHelpers.SetupGame();
TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (0, 1), (1, 1), (0, 2));
var snapshot = TestHelpers.SnapshotWorld(world);
_output.WriteLine(snapshot);
snapshot.Should().Contain("GameState: XWon");
snapshot.Should().Contain("Cells: 9 total, 5 marked");
}
[Fact]
public void Snapshot_Draw()
{
var (world, group) = TestHelpers.SetupGame();
TestHelpers.PlayMoves(world, group,
(0, 0), (0, 1), (0, 2),
(1, 1), (1, 0), (1, 2),
(2, 1), (2, 0), (2, 2));
var snapshot = TestHelpers.SnapshotWorld(world);
_output.WriteLine(snapshot);
snapshot.Should().Contain("GameState: Draw");
snapshot.Should().Contain("Cells: 9 total, 9 marked");
}
[Fact]
public void Log_ReactivityDuringGame()
{
var (world, group) = TestHelpers.SetupGame();
using var capture = new ObservableCapture(world);
capture.FormatWith(m => m.Player.ToString());
capture.FormatWith(s => $"{s.Status} ({s.CurrentPlayer}, {s.MoveCount} moves)");
TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (0, 1), (1, 1), (0, 2));
var logText = string.Join("\n", capture.GetLogLines());
_output.WriteLine(logText);
capture.GetLogLines().Should().Contain(l => l.Contains("ComponentAdded") && l.Contains("Mark"));
capture.GetLogLines().Should().Contain(l => l.Contains("ComponentModified") && l.Contains("GameState"));
}
[Fact]
public void Serialization_RoundTrip_PreservesSnapshot()
{
var (world, group) = TestHelpers.SetupGame();
TestHelpers.PlayMoves(world, group, (0, 0), (1, 1), (2, 2));
var before = TestHelpers.SnapshotWorld(world);
using var stream = new MemoryStream();
WorldSerializer.Save(world, stream);
stream.Position = 0;
var world2 = new World();
WorldSerializer.Load(world2, stream);
var after = TestHelpers.SnapshotWorld(world2);
_output.WriteLine("=== Before ===");
_output.WriteLine(before);
_output.WriteLine("=== After ===");
_output.WriteLine(after);
after.Should().Be(before);
}
}