WIP: feat: NCS-37 Quarkus integration #29

Closed
lq64 wants to merge 13 commits from feat/NCS-37 into main
Owner

Summary

Implements the full REST API defined in docs/api-spec.yaml as a new modules/backcore Quarkus 3.32.4 module. The module follows the exact same
Quarkus + Scala 3.5.1 setup established on the feat/quarkus branch (plugin, BOM, Jakarta CDI, @QuarkusTest + JUnit 5), and sits as a clean
adapter layer on top of the existing api, core, io, and rule modules — none of which were modified.


Endpoints implemented

Method Path Description
POST /api/board/game Create a new game
GET /api/board/game/{gameId} Get full game state
GET /api/board/game/{gameId}/stream NDJSON snapshot of current state
POST /api/board/game/{gameId}/move/{uci} Make a move (UCI notation)
GET /api/board/game/{gameId}/moves List legal moves (optional ?square=e2 filter)
POST /api/board/game/{gameId}/undo Undo last move
POST /api/board/game/{gameId}/redo Redo last undone move
POST /api/board/game/{gameId}/resign Resign the game
POST /api/board/game/{gameId}/draw/{action} Draw offer/accept/decline/claim
POST /api/board/game/import/fen Create game from FEN string
POST /api/board/game/import/pgn Create game from PGN
GET /api/board/game/{gameId}/export/fen Export current position as FEN
GET /api/board/game/{gameId}/export/pgn Export full game as PGN

Architecture

Why a separate module (not embedded in core)

core is a framework-agnostic domain library used by the terminal UI and future CLIs. Adding Quarkus to it would force all consumers to pull in
the entire runtime and violate the existing architectural principle ("GameEngine never imports UI code"). backcore is the web adapter — a third
delivery mechanism alongside ui.

Key design decisions

  • GameStore (@ApplicationScoped) — in-memory Map[String, GameSession] with synchronized access. Holds one GameSession per game.
  • No GameEngineGameEngine was designed for the terminal (auto-resets after checkmate, observer pattern). backcore calls
    RuleSet.applyMove directly and manages GameContext state in GameStore, giving full lifecycle control.
  • CommandInvoker for undo/redo — reused from modules/core. Each move is stored as a MoveCommand with previousContext for undo and
    moveResult.newContext for redo.
  • GameResult sealed trait — captures why a game ended (Checkmate, Stalemate, Resign, AgreedDraw, FiftyMoveDraw,
    InsufficientMaterial), decoupled from GameStatus strings in the API spec.
  • Draw offer state — tracked per-session in GameSession.drawOfferedBy: Option[Color]. The offer persists across moves (REST model has no
    implicit withdrawal on move, unlike OTB chess).
  • Stream endpoint — simplified to a single NDJSON line snapshot ({"type":"gameFull","game":{...}}\n) rather than a persistent SSE
    connection.
  • PGN field in GameState — populated with SAN move text via PgnExporter.exportGame(Map.empty, moves), which returns move-text-only when
    given no headers.

Module structure

modules/backcore/
├── build.gradle.kts
├── src/main/
│ ├── resources/application.yml
│ └── scala/de/nowchess/backcore/
│ ├── config/JacksonConfig.scala # registers DefaultScalaModule
│ ├── dto/Dtos.scala # all request/response DTOs
│ ├── game/
│ │ ├── GameId.scala # 8-char alphanumeric ID generator
│ │ ├── GameMapper.scala # GameSession → DTOs, status computation
│ │ ├── GameResult.scala # sealed trait for game-over reasons
│ │ ├── GameSession.scala # per-game state (context + invoker + metadata)
│ │ └── GameStore.scala # @ApplicationScoped in-memory store
│ └── resource/
│ ├── GameResource.scala # game lifecycle, resign, draw, export, stream
│ ├── ImportResource.scala # FEN/PGN import
│ └── MoveResource.scala # move, legal moves, undo, redo
└── src/test/scala/de/nowchess/backcore/
├── BackcoreStartupTest.scala
└── resource/
├── GameResourceTest.scala
├── ImportExportTest.scala
├── MoveResourceTest.scala
├── ResignDrawTest.scala
└── UndoRedoTest.scala


Tests

34 @QuarkusTest integration tests, all passing. Each TDD cycle wrote tests before implementation:

Test class Tests Coverage
BackcoreStartupTest 1 Container boots
GameResourceTest 4 createGame, getGame, 404
MoveResourceTest 6 makeMove, legal moves, 400/404
UndoRedoTest 4 undo/redo, empty-stack 400
ResignDrawTest 8 resign, offer/accept/decline/claim
ImportExportTest 10 FEN/PGN import, FEN/PGN export, stream

Build

./gradlew :modules:backcore:test    # 34 tests, all green
./gradlew :modules:backcore:build   # produces quarkus-run.jar

Smoke test

./gradlew :modules:backcore:quarkusDev

curl -s -X POST http://localhost:8080/api/board/game \
     -H "Content-Type: application/json" -d '{}' | jq .

curl -s http://localhost:8080/api/board/game/{gameId}/moves | jq '.moves | length'

---
Out of scope / follow-up

- Persistent storage (DB) — currently in-memory only; games are lost on restart
- Authentication — Bearer token reserved per api-spec; endpoints are currently open
- True SSE streaming — /stream returns a one-shot NDJSON snapshot, not a persistent connection
- Fifty-move rule promotion pending state — promotionPending status not yet triggered (requires pawn-reaches-back-rank detection without promotion
 character)
 

NOTE: The TUI and GUI both connect to the local GameEngine in-process. The Quarkus server runs its own separate in-memory GameStore — they are
independent for now (no shared state between the UI and the REST API). If you want them to share game state later, that would be a separate
ticket.
 
NOTE: Quarkus does't use GameEngine. Instead it uses the newly generated GameSession. Does this fit our architectural vision?
### Summary Implements the full REST API defined in `docs/api-spec.yaml` as a new `modules/backcore` Quarkus 3.32.4 module. The module follows the exact same Quarkus + Scala 3.5.1 setup established on the `feat/quarkus` branch (plugin, BOM, Jakarta CDI, `@QuarkusTest` + JUnit 5), and sits as a clean adapter layer on top of the existing `api`, `core`, `io`, and `rule` modules — none of which were modified. --- ### Endpoints implemented | Method | Path | Description | |--------|------|-------------| | `POST` | `/api/board/game` | Create a new game | | `GET` | `/api/board/game/{gameId}` | Get full game state | | `GET` | `/api/board/game/{gameId}/stream` | NDJSON snapshot of current state | | `POST` | `/api/board/game/{gameId}/move/{uci}` | Make a move (UCI notation) | | `GET` | `/api/board/game/{gameId}/moves` | List legal moves (optional `?square=e2` filter) | | `POST` | `/api/board/game/{gameId}/undo` | Undo last move | | `POST` | `/api/board/game/{gameId}/redo` | Redo last undone move | | `POST` | `/api/board/game/{gameId}/resign` | Resign the game | | `POST` | `/api/board/game/{gameId}/draw/{action}` | Draw offer/accept/decline/claim | | `POST` | `/api/board/game/import/fen` | Create game from FEN string | | `POST` | `/api/board/game/import/pgn` | Create game from PGN | | `GET` | `/api/board/game/{gameId}/export/fen` | Export current position as FEN | | `GET` | `/api/board/game/{gameId}/export/pgn` | Export full game as PGN | --- ### Architecture **Why a separate module (not embedded in `core`)** `core` is a framework-agnostic domain library used by the terminal UI and future CLIs. Adding Quarkus to it would force all consumers to pull in the entire runtime and violate the existing architectural principle ("GameEngine never imports UI code"). `backcore` is the web adapter — a third delivery mechanism alongside `ui`. **Key design decisions** - **`GameStore` (`@ApplicationScoped`)** — in-memory `Map[String, GameSession]` with `synchronized` access. Holds one `GameSession` per game. - **No `GameEngine`** — `GameEngine` was designed for the terminal (auto-resets after checkmate, observer pattern). `backcore` calls `RuleSet.applyMove` directly and manages `GameContext` state in `GameStore`, giving full lifecycle control. - **`CommandInvoker` for undo/redo** — reused from `modules/core`. Each move is stored as a `MoveCommand` with `previousContext` for undo and `moveResult.newContext` for redo. - **`GameResult` sealed trait** — captures why a game ended (`Checkmate`, `Stalemate`, `Resign`, `AgreedDraw`, `FiftyMoveDraw`, `InsufficientMaterial`), decoupled from `GameStatus` strings in the API spec. - **Draw offer state** — tracked per-session in `GameSession.drawOfferedBy: Option[Color]`. The offer persists across moves (REST model has no implicit withdrawal on move, unlike OTB chess). - **Stream endpoint** — simplified to a single NDJSON line snapshot (`{"type":"gameFull","game":{...}}\n`) rather than a persistent SSE connection. - **PGN field in `GameState`** — populated with SAN move text via `PgnExporter.exportGame(Map.empty, moves)`, which returns move-text-only when given no headers. --- ### Module structure modules/backcore/ ├── build.gradle.kts ├── src/main/ │ ├── resources/application.yml │ └── scala/de/nowchess/backcore/ │ ├── config/JacksonConfig.scala # registers DefaultScalaModule │ ├── dto/Dtos.scala # all request/response DTOs │ ├── game/ │ │ ├── GameId.scala # 8-char alphanumeric ID generator │ │ ├── GameMapper.scala # GameSession → DTOs, status computation │ │ ├── GameResult.scala # sealed trait for game-over reasons │ │ ├── GameSession.scala # per-game state (context + invoker + metadata) │ │ └── GameStore.scala # @ApplicationScoped in-memory store │ └── resource/ │ ├── GameResource.scala # game lifecycle, resign, draw, export, stream │ ├── ImportResource.scala # FEN/PGN import │ └── MoveResource.scala # move, legal moves, undo, redo └── src/test/scala/de/nowchess/backcore/ ├── BackcoreStartupTest.scala └── resource/ ├── GameResourceTest.scala ├── ImportExportTest.scala ├── MoveResourceTest.scala ├── ResignDrawTest.scala └── UndoRedoTest.scala --- ### Tests 34 `@QuarkusTest` integration tests, all passing. Each TDD cycle wrote tests before implementation: | Test class | Tests | Coverage | |------------|-------|----------| | `BackcoreStartupTest` | 1 | Container boots | | `GameResourceTest` | 4 | createGame, getGame, 404 | | `MoveResourceTest` | 6 | makeMove, legal moves, 400/404 | | `UndoRedoTest` | 4 | undo/redo, empty-stack 400 | | `ResignDrawTest` | 8 | resign, offer/accept/decline/claim | | `ImportExportTest` | 10 | FEN/PGN import, FEN/PGN export, stream | --- ### Build ```bash ./gradlew :modules:backcore:test # 34 tests, all green ./gradlew :modules:backcore:build # produces quarkus-run.jar Smoke test ./gradlew :modules:backcore:quarkusDev curl -s -X POST http://localhost:8080/api/board/game \ -H "Content-Type: application/json" -d '{}' | jq . curl -s http://localhost:8080/api/board/game/{gameId}/moves | jq '.moves | length' --- Out of scope / follow-up - Persistent storage (DB) — currently in-memory only; games are lost on restart - Authentication — Bearer token reserved per api-spec; endpoints are currently open - True SSE streaming — /stream returns a one-shot NDJSON snapshot, not a persistent connection - Fifty-move rule promotion pending state — promotionPending status not yet triggered (requires pawn-reaches-back-rank detection without promotion character) NOTE: The TUI and GUI both connect to the local GameEngine in-process. The Quarkus server runs its own separate in-memory GameStore — they are independent for now (no shared state between the UI and the REST API). If you want them to share game state later, that would be a separate ticket. NOTE: Quarkus does't use GameEngine. Instead it uses the newly generated GameSession. Does this fit our architectural vision?
shosho996 requested review from shosho996 2026-04-13 21:57:20 +02:00
lq64 added 9 commits 2026-04-14 21:44:33 +02:00
Introduces the backcore Quarkus REST module with build configuration,
application.yml, and a smoke test confirming Quarkus boots successfully.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add domain model (GameResult, GameSession, GameId, GameStore, GameMapper),
DTOs, JacksonConfig, and GameResource REST endpoints with QuarkusTest coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added quarkus test compatability with sonar through jacoco reporter
style: fix CRLF line endings introduced by rebase
Build & Test (NowChessSystems) TeamCity build failed
db955c08a5
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lq64 force-pushed feat/NCS-37 from 020941e404 to db955c08a5 2026-04-14 21:44:33 +02:00 Compare
lq64 added 1 commit 2026-04-14 23:24:29 +02:00
feat(backcore): Quarkus compatible with GUI/TUI
Build & Test (NowChessSystems) TeamCity build failed
8fd6adc1f4
Added quarkus backcore run functionality so that it launches the TUI, GUI, Quarkus via modules: backcore:run
lq64 added 1 commit 2026-04-15 09:43:25 +02:00
test(backcore): achieve 100% line coverage
Build & Test (NowChessSystems) TeamCity build failed
52197125f7
- Remove dead GameResult variants (Checkmate, Stalemate, InsufficientMaterial) that were never produced
- Fix ImportResource.importPgn to return 400 for null body instead of silently succeeding with empty PGN
- Add JaCoCo exclusions for companion objects and private ServiceState (only compiler-level synthetics)
- Add integration tests: all move types in toLegalMoveDto (capture/castle/en-passant/promotion), undo/redo/resign/exportPgn 404 paths, null-body endpoints
- Add unit tests: all parsePromotionChar branches (r/b/n/wildcard), drawAction claim success, engine setter, findMatchingMove orElse path, check status in GameMapper
- Add DtoCoverageTest and GameDomainCoverageTest covering synthetic methods (equals, hashCode, copy, productElement, productElementName, canEqual) and singleton serialization (writeReplace)

Result: LINE 300/300 (100%)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Janis added 1 commit 2026-04-15 10:14:52 +02:00
Janis added 1 commit 2026-04-17 18:03:22 +02:00
feat: Bruno
Build & Test (NowChessSystems) TeamCity build failed
e69bbd233a
shosho996 reviewed 2026-04-17 20:18:29 +02:00
shosho996 left a comment
Owner

Why is there bot stuff in Quarkus branch? :( the diff is so big now too
image.png

Why is there bot stuff in Quarkus branch? :( the diff is so big now too <img width="686" alt="image.png" src="attachments/aa7b61b7-f3a1-4253-befc-1ff59f180507">
Member

Doesn't matter the branch is deprecated and will not be merged. Refactor in NCS-51

Doesn't matter the branch is deprecated and will not be merged. Refactor in NCS-51
Janis closed this pull request 2026-04-21 12:37:09 +02:00
Janis deleted branch feat/NCS-37 2026-04-21 12:37:12 +02:00
Some checks are pending
Build & Test (NowChessSystems) TeamCity build failed

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NowChess/NowChessSystems#29