feat: NCS-37 import FEN/PGN, export endpoints, NDJSON stream snapshot
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package de.nowchess.backcore.resource
|
||||
|
||||
import de.nowchess.backcore.dto.{ApiErrorResponse, ImportFenRequest, ImportPgnRequest}
|
||||
import de.nowchess.backcore.game.{GameMapper, GameStore}
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.{MediaType, Response}
|
||||
|
||||
@Path("/api/board/game/import")
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@ApplicationScoped
|
||||
class ImportResource @Inject() (store: GameStore):
|
||||
|
||||
@POST
|
||||
@Path("/fen")
|
||||
def importFen(req: ImportFenRequest): Response =
|
||||
store.importFen(Option(req).getOrElse(ImportFenRequest())) match
|
||||
case Right(session) => Response.status(201).entity(GameMapper.toGameFull(session)).build()
|
||||
case Left(err) => Response.status(400).entity(ApiErrorResponse("INVALID_FEN", err)).build()
|
||||
|
||||
@POST
|
||||
@Path("/pgn")
|
||||
def importPgn(req: ImportPgnRequest): Response =
|
||||
val body = Option(req).getOrElse(ImportPgnRequest())
|
||||
store.importPgn(body.pgn, None, None) match
|
||||
case Right(session) => Response.status(201).entity(GameMapper.toGameFull(session)).build()
|
||||
case Left(err) => Response.status(400).entity(ApiErrorResponse("INVALID_PGN", err)).build()
|
||||
@@ -0,0 +1,163 @@
|
||||
package de.nowchess.backcore.resource
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest
|
||||
import io.restassured.RestAssured
|
||||
import org.hamcrest.Matchers.{containsString, equalTo, matchesPattern, notNullValue}
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@QuarkusTest
|
||||
class ImportExportTest:
|
||||
|
||||
private val startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
|
||||
// ─── Import FEN ────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
def importFenReturns201WithCorrectPosition(): Unit =
|
||||
RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body(s"""{"fen":"$startFen"}""")
|
||||
.when()
|
||||
.post("/api/board/game/import/fen")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.body("gameId", matchesPattern("[A-Za-z0-9]{8}"))
|
||||
.body("state.fen", equalTo(startFen))
|
||||
.body("state.turn", equalTo("white"))
|
||||
|
||||
@Test
|
||||
def importFenWithCustomPositionWorks(): Unit =
|
||||
val fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
|
||||
RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body(s"""{"fen":"$fen"}""")
|
||||
.when()
|
||||
.post("/api/board/game/import/fen")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.body("state.fen", equalTo(fen))
|
||||
.body("state.turn", equalTo("black"))
|
||||
|
||||
@Test
|
||||
def importFenWithInvalidFenReturns400(): Unit =
|
||||
RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("""{"fen":"not-a-fen"}""")
|
||||
.when()
|
||||
.post("/api/board/game/import/fen")
|
||||
.`then`()
|
||||
.statusCode(400)
|
||||
|
||||
// ─── Import PGN ────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
def importPgnReturns201(): Unit =
|
||||
RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("""{"pgn":"1. e4 e5 2. Nf3 Nc6 *"}""")
|
||||
.when()
|
||||
.post("/api/board/game/import/pgn")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.body("gameId", matchesPattern("[A-Za-z0-9]{8}"))
|
||||
.body("state.turn", equalTo("white"))
|
||||
|
||||
@Test
|
||||
def importPgnWithInvalidPgnReturns400(): Unit =
|
||||
RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("""{"pgn":"1. z9 *"}""")
|
||||
.when()
|
||||
.post("/api/board/game/import/pgn")
|
||||
.`then`()
|
||||
.statusCode(400)
|
||||
|
||||
// ─── Export FEN ────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
def exportFenReturnsStartingFen(): Unit =
|
||||
val gameId = RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("{}")
|
||||
.when()
|
||||
.post("/api/board/game")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.extract()
|
||||
.path[String]("gameId")
|
||||
|
||||
RestAssured.`given`()
|
||||
.when()
|
||||
.get(s"/api/board/game/$gameId/export/fen")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.body(equalTo(startFen))
|
||||
|
||||
@Test
|
||||
def exportFenOnUnknownGameReturns404(): Unit =
|
||||
RestAssured.`given`()
|
||||
.when()
|
||||
.get("/api/board/game/XXXXXXXX/export/fen")
|
||||
.`then`()
|
||||
.statusCode(404)
|
||||
|
||||
// ─── Export PGN ────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
def exportPgnReturnsText(): Unit =
|
||||
val gameId = RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("{}")
|
||||
.when()
|
||||
.post("/api/board/game")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.extract()
|
||||
.path[String]("gameId")
|
||||
|
||||
RestAssured.`given`()
|
||||
.when()
|
||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
|
||||
RestAssured.`given`()
|
||||
.when()
|
||||
.get(s"/api/board/game/$gameId/export/pgn")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.body(containsString("e4"))
|
||||
|
||||
// ─── Stream ────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
def streamReturnsNdjsonSnapshot(): Unit =
|
||||
val gameId = RestAssured.`given`()
|
||||
.contentType("application/json")
|
||||
.body("{}")
|
||||
.when()
|
||||
.post("/api/board/game")
|
||||
.`then`()
|
||||
.statusCode(201)
|
||||
.extract()
|
||||
.path[String]("gameId")
|
||||
|
||||
val body = RestAssured.`given`()
|
||||
.when()
|
||||
.get(s"/api/board/game/$gameId/stream")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.contentType("application/x-ndjson")
|
||||
.extract()
|
||||
.body()
|
||||
.asString()
|
||||
|
||||
assert(body.trim.startsWith("""{"type":"gameFull""""), s"Expected gameFull event, got: $body")
|
||||
|
||||
@Test
|
||||
def streamOnUnknownGameReturns404(): Unit =
|
||||
RestAssured.`given`()
|
||||
.when()
|
||||
.get("/api/board/game/XXXXXXXX/stream")
|
||||
.`then`()
|
||||
.statusCode(404)
|
||||
Reference in New Issue
Block a user