using FluentAssertions.Execution; using Shogi.AcceptanceTests.TestSetup; using Shogi.Contracts.Api.Commands; using Shogi.Contracts.Types; using System.Net; using System.Net.Http.Json; using Xunit.Abstractions; namespace Shogi.AcceptanceTests; //#pragma warning disable xUnit1033 // There is a bug which provides a false positive of xUnit1033. //#pragma warning restore xUnit1033 public class ApiTests(AatTestFixture fixture, ITestOutputHelper console) : IClassFixture { private readonly HttpClient httpClient = fixture.HttpClient; private readonly HttpClient player2HttpClient = fixture.OtherHttpClient; private string sessionId = string.Empty; [Fact] public async Task CreateSession() { try { // Arrange await this.SetupTestSession(); // Act var response = await this.ReadTestSession(); // Assert response.Should().NotBeNull(); response!.BoardState.Board.Should().NotBeEmpty(); ValidateBoard(response.BoardState.Board); response.BoardState.Player1Hand.Should().BeEmpty(); response.BoardState.Player2Hand.Should().BeEmpty(); response.BoardState.PlayerInCheck.Should().BeNull(); response.BoardState.WhoseTurn.Should().Be(WhichPlayer.Player1); response.Player1.Should().NotBeNull(); response.Player2.Should().BeNullOrEmpty(); response.SessionId.Should().NotBeEmpty(); } finally { // Annul await this.DeleteTestSession(); } static void ValidateBoard(Dictionary board) { using var scope = new AssertionScope(); board["A1"]!.WhichPiece.Should().Be(WhichPiece.Lance); board["A1"]!.Owner.Should().Be(WhichPlayer.Player1); board["A1"]!.IsPromoted.Should().Be(false); board["B1"]!.WhichPiece.Should().Be(WhichPiece.Knight); board["B1"]!.Owner.Should().Be(WhichPlayer.Player1); board["B1"]!.IsPromoted.Should().Be(false); board["C1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C1"]!.Owner.Should().Be(WhichPlayer.Player1); board["C1"]!.IsPromoted.Should().Be(false); board["D1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D1"]!.Owner.Should().Be(WhichPlayer.Player1); board["D1"]!.IsPromoted.Should().Be(false); board["E1"]!.WhichPiece.Should().Be(WhichPiece.King); board["E1"]!.Owner.Should().Be(WhichPlayer.Player1); board["E1"]!.IsPromoted.Should().Be(false); board["F1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F1"]!.Owner.Should().Be(WhichPlayer.Player1); board["F1"]!.IsPromoted.Should().Be(false); board["G1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G1"]!.Owner.Should().Be(WhichPlayer.Player1); board["G1"]!.IsPromoted.Should().Be(false); board["H1"]!.WhichPiece.Should().Be(WhichPiece.Knight); board["H1"]!.Owner.Should().Be(WhichPlayer.Player1); board["H1"]!.IsPromoted.Should().Be(false); board["I1"]!.WhichPiece.Should().Be(WhichPiece.Lance); board["I1"]!.Owner.Should().Be(WhichPlayer.Player1); board["I1"]!.IsPromoted.Should().Be(false); board["A2"].Should().BeNull(); board["B2"]!.WhichPiece.Should().Be(WhichPiece.Bishop); board["B2"]!.Owner.Should().Be(WhichPlayer.Player1); board["B2"]!.IsPromoted.Should().Be(false); board["C2"].Should().BeNull(); board["D2"].Should().BeNull(); board["E2"].Should().BeNull(); board["F2"].Should().BeNull(); board["G2"].Should().BeNull(); board["H2"]!.WhichPiece.Should().Be(WhichPiece.Rook); board["H2"]!.Owner.Should().Be(WhichPlayer.Player1); board["H2"]!.IsPromoted.Should().Be(false); board["I2"].Should().BeNull(); board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["A3"]!.Owner.Should().Be(WhichPlayer.Player1); board["A3"]!.IsPromoted.Should().Be(false); board["B3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["B3"]!.Owner.Should().Be(WhichPlayer.Player1); board["B3"]!.IsPromoted.Should().Be(false); board["C3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["C3"]!.Owner.Should().Be(WhichPlayer.Player1); board["C3"]!.IsPromoted.Should().Be(false); board["D3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["D3"]!.Owner.Should().Be(WhichPlayer.Player1); board["D3"]!.IsPromoted.Should().Be(false); board["E3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["E3"]!.Owner.Should().Be(WhichPlayer.Player1); board["E3"]!.IsPromoted.Should().Be(false); board["F3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["F3"]!.Owner.Should().Be(WhichPlayer.Player1); board["F3"]!.IsPromoted.Should().Be(false); board["G3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["G3"]!.Owner.Should().Be(WhichPlayer.Player1); board["G3"]!.IsPromoted.Should().Be(false); board["H3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["H3"]!.Owner.Should().Be(WhichPlayer.Player1); board["H3"]!.IsPromoted.Should().Be(false); board["I3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["I3"]!.Owner.Should().Be(WhichPlayer.Player1); board["I3"]!.IsPromoted.Should().Be(false); board["A4"].Should().BeNull(); board["B4"].Should().BeNull(); board["C4"].Should().BeNull(); board["D4"].Should().BeNull(); board["E4"].Should().BeNull(); board["F4"].Should().BeNull(); board["G4"].Should().BeNull(); board["H4"].Should().BeNull(); board["I4"].Should().BeNull(); board["A5"].Should().BeNull(); board["B5"].Should().BeNull(); board["C5"].Should().BeNull(); board["D5"].Should().BeNull(); board["E5"].Should().BeNull(); board["F5"].Should().BeNull(); board["G5"].Should().BeNull(); board["H5"].Should().BeNull(); board["I5"].Should().BeNull(); board["A6"].Should().BeNull(); board["B6"].Should().BeNull(); board["C6"].Should().BeNull(); board["D6"].Should().BeNull(); board["E6"].Should().BeNull(); board["F6"].Should().BeNull(); board["G6"].Should().BeNull(); board["H6"].Should().BeNull(); board["I6"].Should().BeNull(); board["A7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["A7"]!.Owner.Should().Be(WhichPlayer.Player2); board["A7"]!.IsPromoted.Should().Be(false); board["B7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["B7"]!.Owner.Should().Be(WhichPlayer.Player2); board["B7"]!.IsPromoted.Should().Be(false); board["C7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["C7"]!.Owner.Should().Be(WhichPlayer.Player2); board["C7"]!.IsPromoted.Should().Be(false); board["D7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["D7"]!.Owner.Should().Be(WhichPlayer.Player2); board["D7"]!.IsPromoted.Should().Be(false); board["E7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["E7"]!.Owner.Should().Be(WhichPlayer.Player2); board["E7"]!.IsPromoted.Should().Be(false); board["F7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["F7"]!.Owner.Should().Be(WhichPlayer.Player2); board["F7"]!.IsPromoted.Should().Be(false); board["G7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["G7"]!.Owner.Should().Be(WhichPlayer.Player2); board["G7"]!.IsPromoted.Should().Be(false); board["H7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["H7"]!.Owner.Should().Be(WhichPlayer.Player2); board["H7"]!.IsPromoted.Should().Be(false); board["I7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); board["I7"]!.Owner.Should().Be(WhichPlayer.Player2); board["I7"]!.IsPromoted.Should().Be(false); board["A8"].Should().BeNull(); board["B8"]!.WhichPiece.Should().Be(WhichPiece.Rook); board["B8"]!.Owner.Should().Be(WhichPlayer.Player2); board["B8"]!.IsPromoted.Should().Be(false); board["C8"].Should().BeNull(); board["D8"].Should().BeNull(); board["E8"].Should().BeNull(); board["F8"].Should().BeNull(); board["G8"].Should().BeNull(); board["H8"]!.WhichPiece.Should().Be(WhichPiece.Bishop); board["H8"]!.Owner.Should().Be(WhichPlayer.Player2); board["H8"]!.IsPromoted.Should().Be(false); board["I8"].Should().BeNull(); board["A9"]!.WhichPiece.Should().Be(WhichPiece.Lance); board["A9"]!.Owner.Should().Be(WhichPlayer.Player2); board["A9"]!.IsPromoted.Should().Be(false); board["B9"]!.WhichPiece.Should().Be(WhichPiece.Knight); board["B9"]!.Owner.Should().Be(WhichPlayer.Player2); board["B9"]!.IsPromoted.Should().Be(false); board["C9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C9"]!.Owner.Should().Be(WhichPlayer.Player2); board["C9"]!.IsPromoted.Should().Be(false); board["D9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D9"]!.Owner.Should().Be(WhichPlayer.Player2); board["D9"]!.IsPromoted.Should().Be(false); board["E9"]!.WhichPiece.Should().Be(WhichPiece.King); board["E9"]!.Owner.Should().Be(WhichPlayer.Player2); board["E9"]!.IsPromoted.Should().Be(false); board["F9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F9"]!.Owner.Should().Be(WhichPlayer.Player2); board["F9"]!.IsPromoted.Should().Be(false); board["G9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G9"]!.Owner.Should().Be(WhichPlayer.Player2); board["G9"]!.IsPromoted.Should().Be(false); board["H9"]!.WhichPiece.Should().Be(WhichPiece.Knight); board["H9"]!.Owner.Should().Be(WhichPlayer.Player2); board["H9"]!.IsPromoted.Should().Be(false); board["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance); board["I9"]!.Owner.Should().Be(WhichPlayer.Player2); board["I9"]!.IsPromoted.Should().Be(false); } } [Fact] public async Task ReadAllSessionsMetadata() { try { // Arrange await this.SetupTestSession(); var testSession = await this.ReadTestSession(); // Act var readAllResponse = await this.httpClient.GetFromJsonAsync( new Uri("Sessions", UriKind.Relative), Contracts.ShogiApiJsonSerializerSettings.SystemTextJsonSerializerOptions); // Assert readAllResponse.Should().NotBeNull(); readAllResponse!.First().SessionId.Should().Be(testSession.SessionId); } finally { // Annul await this.DeleteTestSession(); } } [Fact] public async Task JoinSession_Player2IsNotSet_SetsPlayer2() { try { // Arrange await this.SetupTestSession(); // Act var joinResponse = await this.player2HttpClient.PatchAsync(new Uri($"Sessions/{this.sessionId}/Join", UriKind.Relative), null); // Assert joinResponse.StatusCode.Should().Be(HttpStatusCode.OK); var readSessionResponse = await this.ReadTestSession(); readSessionResponse.Player2.Should().NotBeNullOrEmpty(); } finally { await this.DeleteTestSession(); } } [Fact] public async Task JoinSession_SessionIsFull_Conflict() { try { // Arrange await this.SetupTestSession(); var joinResponse = await this.player2HttpClient.PatchAsync(new Uri($"Sessions/{this.sessionId}/Join", UriKind.Relative), null); joinResponse.StatusCode.Should().Be(HttpStatusCode.OK); var readSessionResponse = await this.ReadTestSession(); readSessionResponse.Player2.Should().NotBeNull(); // Act joinResponse = await this.player2HttpClient.PatchAsync(new Uri($"Sessions/{this.sessionId}/Join", UriKind.Relative), null); // Assert joinResponse.StatusCode.Should().Be(HttpStatusCode.Conflict); } finally { await this.DeleteTestSession(); } } [Fact] public async Task MovePieceCommand_MovingPieceFromBoard_MovesThePiece() { try { // Arrange await this.SetupTestSession(); var movePawnCommand = new MovePieceCommand { From = "A3", To = "A4", }; // Act var response = await this.httpClient.PatchAsync(new Uri($"Sessions/{this.sessionId}/Move", UriKind.Relative), JsonContent.Create(movePawnCommand)); response.StatusCode.Should().Be(HttpStatusCode.NoContent, because: await response.Content.ReadAsStringAsync()); // Assert var session = await this.ReadTestSession(); session.BoardState.Board.Should().ContainKey("A3"); session.BoardState.Board["A3"].Should().BeNull(); session.BoardState.Board["A4"].Should().NotBeNull(); session.BoardState.Board["A4"]!.IsPromoted.Should().BeFalse(); session.BoardState.Board["A4"]!.Owner.Should().Be(WhichPlayer.Player1); session.BoardState.Board["A4"]!.WhichPiece.Should().Be(WhichPiece.Pawn); } finally { // Annul await this.DeleteTestSession(); } } private async Task SetupTestSession() { var createResponse = await this.httpClient.PostAsync(new Uri("Sessions", UriKind.Relative), null); createResponse.StatusCode.Should().Be(HttpStatusCode.Created); this.sessionId = await createResponse.Content.ReadAsStringAsync(); } private Task ReadTestSession() { return this.httpClient.GetFromJsonAsync(new Uri($"Sessions/{Uri.EscapeDataString(this.sessionId)}", UriKind.Relative))!; } private async Task DeleteTestSession() { var response = await this.httpClient.DeleteAsync(new Uri($"Sessions/{Uri.EscapeDataString(this.sessionId)}", UriKind.Relative)); response.StatusCode.Should().Be(HttpStatusCode.NoContent, because: await response.Content.ReadAsStringAsync()); } }