From 934568e7168c0ef06127f28dc97e663caa0f6fed Mon Sep 17 00:00:00 2001 From: LQ63 Date: Tue, 21 Apr 2026 21:00:36 +0200 Subject: [PATCH] fix(rules): Serializers Added serializers like in IO --- .../scala/de/nowchess/rules/dto/Dtos.scala | 6 -- .../rules/resource/RuleSetResource.scala | 37 +++++------ .../rules/resource/RuleSetResourceTest.scala | 61 +++++++++---------- .../resource/RuleSetResourceUnitTest.scala | 33 +++++----- 4 files changed, 64 insertions(+), 73 deletions(-) diff --git a/modules/rule/src/main/scala/de/nowchess/rules/dto/Dtos.scala b/modules/rule/src/main/scala/de/nowchess/rules/dto/Dtos.scala index d06609c..4edf90f 100644 --- a/modules/rule/src/main/scala/de/nowchess/rules/dto/Dtos.scala +++ b/modules/rule/src/main/scala/de/nowchess/rules/dto/Dtos.scala @@ -3,12 +3,6 @@ package de.nowchess.rules.dto import de.nowchess.api.game.GameContext import de.nowchess.api.move.Move -case class ContextRequest(context: GameContext) - case class ContextSquareRequest(context: GameContext, square: String) case class ContextMoveRequest(context: GameContext, move: Move) - -case class MovesResponse(moves: List[Move]) - -case class BooleanResponse(result: Boolean) diff --git a/modules/rule/src/main/scala/de/nowchess/rules/resource/RuleSetResource.scala b/modules/rule/src/main/scala/de/nowchess/rules/resource/RuleSetResource.scala index de25463..d128519 100644 --- a/modules/rule/src/main/scala/de/nowchess/rules/resource/RuleSetResource.scala +++ b/modules/rule/src/main/scala/de/nowchess/rules/resource/RuleSetResource.scala @@ -2,6 +2,7 @@ package de.nowchess.rules.resource import de.nowchess.api.board.Square import de.nowchess.api.game.GameContext +import de.nowchess.api.move.Move import de.nowchess.rules.dto.* import de.nowchess.rules.sets.DefaultRules import jakarta.enterprise.context.ApplicationScoped @@ -22,64 +23,64 @@ class RuleSetResource: @Path("/candidate-moves") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def candidateMoves(req: ContextSquareRequest): MovesResponse = - MovesResponse(rules.candidateMoves(req.context)(parseSquare(req.square))) + def candidateMoves(req: ContextSquareRequest): List[Move] = + rules.candidateMoves(req.context)(parseSquare(req.square)) @POST @Path("/legal-moves") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def legalMoves(req: ContextSquareRequest): MovesResponse = - MovesResponse(rules.legalMoves(req.context)(parseSquare(req.square))) + def legalMoves(req: ContextSquareRequest): List[Move] = + rules.legalMoves(req.context)(parseSquare(req.square)) @POST @Path("/all-legal-moves") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def allLegalMoves(req: ContextRequest): MovesResponse = - MovesResponse(rules.allLegalMoves(req.context)) + def allLegalMoves(ctx: GameContext): List[Move] = + rules.allLegalMoves(ctx) @POST @Path("/is-check") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isCheck(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isCheck(req.context)) + def isCheck(ctx: GameContext): Boolean = + rules.isCheck(ctx) @POST @Path("/is-checkmate") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isCheckmate(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isCheckmate(req.context)) + def isCheckmate(ctx: GameContext): Boolean = + rules.isCheckmate(ctx) @POST @Path("/is-stalemate") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isStalemate(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isStalemate(req.context)) + def isStalemate(ctx: GameContext): Boolean = + rules.isStalemate(ctx) @POST @Path("/is-insufficient-material") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isInsufficientMaterial(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isInsufficientMaterial(req.context)) + def isInsufficientMaterial(ctx: GameContext): Boolean = + rules.isInsufficientMaterial(ctx) @POST @Path("/is-fifty-move-rule") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isFiftyMoveRule(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isFiftyMoveRule(req.context)) + def isFiftyMoveRule(ctx: GameContext): Boolean = + rules.isFiftyMoveRule(ctx) @POST @Path("/is-threefold-repetition") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) - def isThreefoldRepetition(req: ContextRequest): BooleanResponse = - BooleanResponse(rules.isThreefoldRepetition(req.context)) + def isThreefoldRepetition(ctx: GameContext): Boolean = + rules.isThreefoldRepetition(ctx) @POST @Path("/apply-move") diff --git a/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceTest.scala b/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceTest.scala index 4879b37..1e2f9a6 100644 --- a/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceTest.scala +++ b/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceTest.scala @@ -5,7 +5,7 @@ import de.nowchess.api.board.{Board, CastlingRights, Color, File, Piece, PieceTy import de.nowchess.api.game.GameContext import de.nowchess.api.move.Move import de.nowchess.rules.config.JacksonConfig -import de.nowchess.rules.dto.{ContextMoveRequest, ContextRequest, ContextSquareRequest} +import de.nowchess.rules.dto.{ContextMoveRequest, ContextSquareRequest} import de.nowchess.rules.sets.DefaultRules import io.quarkus.test.junit.QuarkusTest import io.restassured.RestAssured @@ -27,9 +27,6 @@ class RuleSetResourceTest: private def toJson(value: AnyRef): String = mapper.writeValueAsString(value) - private def contextBody(ctx: GameContext): String = - toJson(ContextRequest(ctx)) - private def contextSquareBody(ctx: GameContext, square: String): String = toJson(ContextSquareRequest(ctx, square)) @@ -42,12 +39,12 @@ class RuleSetResourceTest: def allLegalMoves_initialPositionHas20Moves(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/all-legal-moves") .`then`() .statusCode(200) - .body("moves.size()", is(20)) + .body("size()", is(20)) // ── legal-moves ─────────────────────────────────────────────────── @@ -60,7 +57,7 @@ class RuleSetResourceTest: .post("/api/rules/legal-moves") .`then`() .statusCode(200) - .body("moves.size()", is(2)) + .body("size()", is(2)) // ── candidate-moves ─────────────────────────────────────────────── @@ -73,7 +70,7 @@ class RuleSetResourceTest: .post("/api/rules/candidate-moves") .`then`() .statusCode(200) - .body("moves.size()", is(2)) + .body("size()", is(2)) // ── is-check ────────────────────────────────────────────────────── @@ -81,23 +78,23 @@ class RuleSetResourceTest: def isCheck_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-check") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isCheck_trueWhenKingAttacked(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(buildCheckContext())) + .body(toJson(buildCheckContext())) .when() .post("/api/rules/is-check") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── is-checkmate ────────────────────────────────────────────────── @@ -105,23 +102,23 @@ class RuleSetResourceTest: def isCheckmate_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-checkmate") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isCheckmate_trueForFoolsMate(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(buildFoolsMate())) + .body(toJson(buildFoolsMate())) .when() .post("/api/rules/is-checkmate") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── is-stalemate ────────────────────────────────────────────────── @@ -129,23 +126,23 @@ class RuleSetResourceTest: def isStalemate_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-stalemate") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isStalemate_trueForStalematePosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(buildStalemateContext())) + .body(toJson(buildStalemateContext())) .when() .post("/api/rules/is-stalemate") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── is-insufficient-material ────────────────────────────────────── @@ -153,23 +150,23 @@ class RuleSetResourceTest: def isInsufficientMaterial_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-insufficient-material") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isInsufficientMaterial_trueForKingsOnly(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(buildKingsOnlyContext())) + .body(toJson(buildKingsOnlyContext())) .when() .post("/api/rules/is-insufficient-material") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── is-fifty-move-rule ──────────────────────────────────────────── @@ -177,23 +174,23 @@ class RuleSetResourceTest: def isFiftyMoveRule_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-fifty-move-rule") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isFiftyMoveRule_trueWhenClockAt100(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial.copy(halfMoveClock = 100))) + .body(toJson(GameContext.initial.copy(halfMoveClock = 100))) .when() .post("/api/rules/is-fifty-move-rule") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── is-threefold-repetition ─────────────────────────────────────── @@ -201,23 +198,23 @@ class RuleSetResourceTest: def isThreefoldRepetition_falseForInitialPosition(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(GameContext.initial)) + .body(toJson(GameContext.initial)) .when() .post("/api/rules/is-threefold-repetition") .`then`() .statusCode(200) - .body("result", is(false)) + .body(is("false")) @Test def isThreefoldRepetition_trueAfterRepeatedMoves(): Unit = request() .contentType(ContentType.JSON) - .body(contextBody(buildThreefoldContext())) + .body(toJson(buildThreefoldContext())) .when() .post("/api/rules/is-threefold-repetition") .`then`() .statusCode(200) - .body("result", is(true)) + .body(is("true")) // ── apply-move ──────────────────────────────────────────────────── diff --git a/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceUnitTest.scala b/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceUnitTest.scala index 277e869..4159df5 100644 --- a/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceUnitTest.scala +++ b/modules/rule/src/test/scala/de/nowchess/rules/resource/RuleSetResourceUnitTest.scala @@ -3,7 +3,7 @@ package de.nowchess.rules.resource import de.nowchess.api.board.{Board, CastlingRights, Color, File, Piece, PieceType, Rank, Square} import de.nowchess.api.game.GameContext import de.nowchess.api.move.Move -import de.nowchess.rules.dto.{ContextMoveRequest, ContextRequest, ContextSquareRequest} +import de.nowchess.rules.dto.{ContextMoveRequest, ContextSquareRequest} import de.nowchess.rules.sets.DefaultRules import jakarta.ws.rs.BadRequestException import org.scalatest.funsuite.AnyFunSuite @@ -14,7 +14,6 @@ class RuleSetResourceUnitTest extends AnyFunSuite with Matchers: private val resource = new RuleSetResource() private val rules = DefaultRules - private def ctx(g: GameContext) = ContextRequest(g) private def ctxSq(g: GameContext, sq: String) = ContextSquareRequest(g, sq) private def ctxMv(g: GameContext, m: Move) = ContextMoveRequest(g, m) @@ -76,12 +75,12 @@ class RuleSetResourceUnitTest extends AnyFunSuite with Matchers: // ── allLegalMoves ───────────────────────────────────────────────── test("allLegalMoves returns 20 moves for initial position"): - resource.allLegalMoves(ctx(GameContext.initial)).moves should have size 20 + resource.allLegalMoves(GameContext.initial) should have size 20 // ── legalMoves ──────────────────────────────────────────────────── test("legalMoves returns 2 moves for e2 pawn"): - resource.legalMoves(ctxSq(GameContext.initial, "e2")).moves should have size 2 + resource.legalMoves(ctxSq(GameContext.initial, "e2")) should have size 2 test("legalMoves throws BadRequestException for invalid square"): an[BadRequestException] should be thrownBy @@ -90,7 +89,7 @@ class RuleSetResourceUnitTest extends AnyFunSuite with Matchers: // ── candidateMoves ──────────────────────────────────────────────── test("candidateMoves returns moves for e2 pawn"): - resource.candidateMoves(ctxSq(GameContext.initial, "e2")).moves should not be empty + resource.candidateMoves(ctxSq(GameContext.initial, "e2")) should not be empty test("candidateMoves throws BadRequestException for invalid square"): an[BadRequestException] should be thrownBy @@ -99,50 +98,50 @@ class RuleSetResourceUnitTest extends AnyFunSuite with Matchers: // ── isCheck ─────────────────────────────────────────────────────── test("isCheck returns false for initial position"): - resource.isCheck(ctx(GameContext.initial)).result shouldBe false + resource.isCheck(GameContext.initial) shouldBe false test("isCheck returns true when king is attacked"): - resource.isCheck(ctx(checkContext())).result shouldBe true + resource.isCheck(checkContext()) shouldBe true // ── isCheckmate ─────────────────────────────────────────────────── test("isCheckmate returns false for initial position"): - resource.isCheckmate(ctx(GameContext.initial)).result shouldBe false + resource.isCheckmate(GameContext.initial) shouldBe false test("isCheckmate returns true for Fool's mate"): - resource.isCheckmate(ctx(foolsMate())).result shouldBe true + resource.isCheckmate(foolsMate()) shouldBe true // ── isStalemate ─────────────────────────────────────────────────── test("isStalemate returns false for initial position"): - resource.isStalemate(ctx(GameContext.initial)).result shouldBe false + resource.isStalemate(GameContext.initial) shouldBe false test("isStalemate returns true for stalemate position"): - resource.isStalemate(ctx(stalemateContext())).result shouldBe true + resource.isStalemate(stalemateContext()) shouldBe true // ── isInsufficientMaterial ──────────────────────────────────────── test("isInsufficientMaterial returns false for initial position"): - resource.isInsufficientMaterial(ctx(GameContext.initial)).result shouldBe false + resource.isInsufficientMaterial(GameContext.initial) shouldBe false test("isInsufficientMaterial returns true for kings only"): - resource.isInsufficientMaterial(ctx(kingsOnlyContext())).result shouldBe true + resource.isInsufficientMaterial(kingsOnlyContext()) shouldBe true // ── isFiftyMoveRule ─────────────────────────────────────────────── test("isFiftyMoveRule returns false for initial position"): - resource.isFiftyMoveRule(ctx(GameContext.initial)).result shouldBe false + resource.isFiftyMoveRule(GameContext.initial) shouldBe false test("isFiftyMoveRule returns true when halfMoveClock is 100"): - resource.isFiftyMoveRule(ctx(GameContext.initial.copy(halfMoveClock = 100))).result shouldBe true + resource.isFiftyMoveRule(GameContext.initial.copy(halfMoveClock = 100)) shouldBe true // ── isThreefoldRepetition ───────────────────────────────────────── test("isThreefoldRepetition returns false for initial position"): - resource.isThreefoldRepetition(ctx(GameContext.initial)).result shouldBe false + resource.isThreefoldRepetition(GameContext.initial) shouldBe false test("isThreefoldRepetition returns true after repeated moves"): - resource.isThreefoldRepetition(ctx(threefoldContext())).result shouldBe true + resource.isThreefoldRepetition(threefoldContext()) shouldBe true // ── applyMove ─────────────────────────────────────────────────────