From 626e1a62ab03e8f8aa203c1ed7fcd8331b5c2554 Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 20 Apr 2026 21:33:20 +0200 Subject: [PATCH] test: add @QuarkusTest integration tests for GameRegistry and GameResource Add QuarkusTest integration tests for: - GameRegistryImpl: store, get, update, generateId - GameResource: createGame, getGame, makeMove, getLegalMoves, resignGame, undoMove, redoMove, drawAction, importFen, importPgn, exportFen, exportPgn Tests run and pass but Scoverage doesn't instrument @QuarkusTest classes. Coverage won't be reported in metrics, but correctness is verified. Co-Authored-By: Claude Haiku 4.5 --- .../chess/registry/GameRegistryImplTest.scala | 58 +++++++ .../GameResourceIntegrationTest.scala | 152 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 modules/core/src/test/scala/de/nowchess/chess/registry/GameRegistryImplTest.scala create mode 100644 modules/core/src/test/scala/de/nowchess/chess/resource/GameResourceIntegrationTest.scala diff --git a/modules/core/src/test/scala/de/nowchess/chess/registry/GameRegistryImplTest.scala b/modules/core/src/test/scala/de/nowchess/chess/registry/GameRegistryImplTest.scala new file mode 100644 index 0000000..49e6c64 --- /dev/null +++ b/modules/core/src/test/scala/de/nowchess/chess/registry/GameRegistryImplTest.scala @@ -0,0 +1,58 @@ +package de.nowchess.chess.registry + +import de.nowchess.api.player.{PlayerId, PlayerInfo} +import de.nowchess.chess.engine.GameEngine +import io.quarkus.test.junit.QuarkusTest +import jakarta.inject.Inject +import org.junit.jupiter.api.{Test, DisplayName} +import org.junit.jupiter.api.Assertions.* + +import scala.compiletime.uninitialized + +@QuarkusTest +@DisplayName("GameRegistryImpl") +class GameRegistryImplTest: + + @Inject + var registry: GameRegistry = uninitialized + + @Test + @DisplayName("store saves entry") + def testStore(): Unit = + val entry = GameEntry("g1", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2")) + registry.store(entry) + assertTrue(registry.get("g1").isDefined) + + @Test + @DisplayName("get returns stored entry") + def testGet(): Unit = + val entry = GameEntry("g2", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2")) + registry.store(entry) + val retrieved = registry.get("g2") + assertTrue(retrieved.isDefined) + assertEquals("g2", retrieved.get.gameId) + + @Test + @DisplayName("get returns None for unknown id") + def testGetUnknown(): Unit = + assertTrue(registry.get("unknown").isEmpty) + + @Test + @DisplayName("update modifies existing entry") + def testUpdate(): Unit = + val entry = GameEntry("g3", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2")) + registry.store(entry) + val updated = entry.copy(resigned = true) + registry.update(updated) + val retrieved = registry.get("g3") + assertTrue(retrieved.isDefined) + assertTrue(retrieved.get.resigned) + + @Test + @DisplayName("generateId produces unique ids") + def testGenerateId(): Unit = + val id1 = registry.generateId() + val id2 = registry.generateId() + assertNotEquals(id1, id2) + assertFalse(id1.isEmpty) + assertFalse(id2.isEmpty) diff --git a/modules/core/src/test/scala/de/nowchess/chess/resource/GameResourceIntegrationTest.scala b/modules/core/src/test/scala/de/nowchess/chess/resource/GameResourceIntegrationTest.scala new file mode 100644 index 0000000..f7b8b6f --- /dev/null +++ b/modules/core/src/test/scala/de/nowchess/chess/resource/GameResourceIntegrationTest.scala @@ -0,0 +1,152 @@ +package de.nowchess.chess.resource + +import de.nowchess.api.dto.* +import de.nowchess.chess.exception.BadRequestException +import io.quarkus.test.junit.QuarkusTest +import jakarta.inject.Inject +import org.junit.jupiter.api.{Test, DisplayName} +import org.junit.jupiter.api.Assertions.* + +import scala.compiletime.uninitialized + +@QuarkusTest +@DisplayName("GameResource Integration") +class GameResourceIntegrationTest: + + @Inject + var resource: GameResource = uninitialized + + @Test + @DisplayName("createGame returns 201") + def testCreateGame(): Unit = + val req = CreateGameRequestDto(None, None) + val resp = resource.createGame(req) + assertEquals(201, resp.getStatus) + val dto = resp.getEntity.asInstanceOf[GameFullDto] + assertNotNull(dto.gameId) + + @Test + @DisplayName("getGame returns 200") + def testGetGame(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val getResp = resource.getGame(gameId) + assertEquals(200, getResp.getStatus) + val dto = getResp.getEntity.asInstanceOf[GameFullDto] + assertEquals(gameId, dto.gameId) + + @Test + @DisplayName("makeMove advances game") + def testMakeMove(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val moveResp = resource.makeMove(gameId, "e2e4") + assertEquals(200, moveResp.getStatus) + val state = moveResp.getEntity.asInstanceOf[GameStateDto] + assertEquals("black", state.turn) + + @Test + @DisplayName("makeMove with invalid UCI throws") + def testMakeMoveInvalid(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + assertThrows(classOf[BadRequestException], () => resource.makeMove(gameId, "invalid")) + + @Test + @DisplayName("getLegalMoves returns moves") + def testGetLegalMoves(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val movesResp = resource.getLegalMoves(gameId, "") + assertEquals(200, movesResp.getStatus) + val dto = movesResp.getEntity.asInstanceOf[LegalMovesResponseDto] + assertFalse(dto.moves.isEmpty) + + @Test + @DisplayName("resignGame updates state") + def testResignGame(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val resignResp = resource.resignGame(gameId) + assertEquals(200, resignResp.getStatus) + val getResp = resource.getGame(gameId) + val state = getResp.getEntity.asInstanceOf[GameFullDto].state + assertEquals("resign", state.status) + + @Test + @DisplayName("undoMove reverts") + def testUndoMove(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + resource.makeMove(gameId, "e2e4") + val undoResp = resource.undoMove(gameId) + assertEquals(200, undoResp.getStatus) + val state = undoResp.getEntity.asInstanceOf[GameStateDto] + assertEquals("white", state.turn) + + @Test + @DisplayName("redoMove restores") + def testRedoMove(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + resource.makeMove(gameId, "e2e4") + resource.undoMove(gameId) + val redoResp = resource.redoMove(gameId) + assertEquals(200, redoResp.getStatus) + val state = redoResp.getEntity.asInstanceOf[GameStateDto] + assertEquals("black", state.turn) + + @Test + @DisplayName("drawAction offer") + def testDrawActionOffer(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val resp = resource.drawAction(gameId, "offer") + assertEquals(200, resp.getStatus) + + @Test + @DisplayName("drawAction accept") + def testDrawActionAccept(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + resource.drawAction(gameId, "offer") + val resp = resource.drawAction(gameId, "accept") + assertEquals(200, resp.getStatus) + + @Test + @DisplayName("importFen creates game") + def testImportFen(): Unit = + val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + val req = ImportFenRequestDto(fen, None, None) + val resp = resource.importFen(req) + assertEquals(201, resp.getStatus) + val dto = resp.getEntity.asInstanceOf[GameFullDto] + assertEquals(fen, dto.state.fen) + + @Test + @DisplayName("importPgn creates game") + def testImportPgn(): Unit = + val req = ImportPgnRequestDto("1. e4 c5") + val resp = resource.importPgn(req) + assertEquals(201, resp.getStatus) + val dto = resp.getEntity.asInstanceOf[GameFullDto] + assertTrue(dto.state.moves.length > 0) + + @Test + @DisplayName("exportFen returns FEN") + def testExportFen(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + val resp = resource.exportFen(gameId) + assertEquals(200, resp.getStatus) + assertTrue(resp.getEntity.asInstanceOf[String].contains("rnbqkbnr")) + + @Test + @DisplayName("exportPgn returns PGN") + def testExportPgn(): Unit = + val createResp = resource.createGame(CreateGameRequestDto(None, None)) + val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId + resource.makeMove(gameId, "e2e4") + val resp = resource.exportPgn(gameId) + assertEquals(200, resp.getStatus) + assertTrue(resp.getEntity.asInstanceOf[String].contains("1."))