Compare commits

..

11 Commits

Author SHA1 Message Date
Janis e7d556fa06 feat: update Scala library version and enhance test configurations
Build & Test (NowChessSystems) TeamCity build failed
2026-04-21 14:48:57 +02:00
Janis 9a36555f1a feat: add lint command to CLAUDE.md and create lint script 2026-04-21 14:31:36 +02:00
Janis e5a6cc30eb feat: NCS-53 changed IO to MicroService for easier scaling 2026-04-21 14:16:29 +02:00
TeamCity d7f7c37111 ci: bump version with Build-44 2026-04-21 11:13:30 +00:00
Janis 2f1e3aded0 refactor: align JSON string formatting in JsonParserTest
Build & Test (NowChessSystems) TeamCity build finished
2026-04-21 13:02:18 +02:00
shosho996 1401297e7f test: NCS-45 IO Test reduction (#32)
Build & Test (NowChessSystems) TeamCity build failed
Co-authored-by: shahdlala66 <shahd.lala66@gmail.com>
Reviewed-on: #32
Co-authored-by: Shahd Lala <shosho996@blackhole.local>
Co-committed-by: Shahd Lala <shosho996@blackhole.local>
2026-04-21 12:39:19 +02:00
Janis 5ad5efb41e feat: NCS-37 Quarkus integration (#35)
Build & Test (NowChessSystems) TeamCity build failed
Reviewed-on: #35
Reviewed-by: Leon Hermann <lq@blackhole.local>
2026-04-21 12:35:20 +02:00
TeamCity f215ec681a ci: bump version with Build-43 2026-04-19 20:53:56 +00:00
Janis 0091d50467 feat: NCS-40 Rework Draw System (#34)
Build & Test (NowChessSystems) TeamCity build finished
Reviewed-on: #34
Reviewed-by: Shahd Lala <shosho996@blackhole.local>
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2026-04-19 22:44:48 +02:00
TeamCity 2e4c7549b5 ci: bump version with Build-42 2026-04-19 14:01:11 +00:00
Janis dceab0875e feat: NCS-41 Bot Platform (#33)
Build & Test (NowChessSystems) TeamCity build finished
Co-authored-by: Janis <janis@nowchess.de>
Reviewed-on: #33
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2026-04-19 15:52:08 +02:00
9 changed files with 2522633 additions and 200 deletions
+1 -5
View File
@@ -36,11 +36,7 @@ val coverageExclusions = listOf(
"**/core/src/main/scala/de/nowchess/chess/registry/GameEntry.scala",
"**/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala",
// GameResource — REST integration layer with @Inject var fields; mocking dependencies for unit tests is infeasible with Quarkus DI; integration tests would require @QuarkusTest which Scoverage doesn't instrument
"**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala",
// IoResource — same rationale as GameResource; @QuarkusTest not instrumented by Scoverage
"**/io/src/main/scala/de/nowchess/io/service/resource/IoResource.scala",
// JacksonConfig — Quarkus lifecycle hook, no testable logic beyond ObjectMapper registration
"**/io/src/main/scala/de/nowchess/io/service/config/JacksonConfig.scala",
"**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala"
)
// Converts a Sonar-style glob to a scoverage regex (matched against full source path).
Executable → Regular
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
{
"version": 1,
"created": "2026-04-13T19:58:38.629943",
"total_positions": 3022562,
"stockfish_depth": 12,
"sources": [
{
"type": "legacy_import",
"path": "data/training_data.jsonl",
"count": 2009355,
"note": "Migrated from data/training_data.jsonl"
},
{
"type": "test_extend",
"count": 4,
"actual_count": 3
},
{
"type": "test_new_positions",
"count": 3,
"actual_count": 3
},
{
"type": "test_mixed",
"count": 5,
"actual_count": 0
},
{
"type": "test_all_dups",
"count": 2,
"actual_count": 0
},
{
"type": "guaranteed_unique",
"count": 10,
"actual_count": 8
},
{
"type": "merged_sources",
"count": 600000,
"sources": [
{
"type": "tactical",
"count": 600000,
"max_puzzles": 600000
}
],
"actual_count": 599993
},
{
"type": "merged_sources",
"count": 500000,
"sources": [
{
"type": "lichess",
"count": 500000,
"params": {
"min_depth": 20,
"max_positions": 500000
}
}
],
"actual_count": 500000
}
]
}
@@ -21,6 +21,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection
classOf[LegalMovesResponseDto],
classOf[OkResponseDto],
classOf[PlayerInfoDto],
classOf[GameContext],
classOf[Color],
classOf[Piece],
@@ -34,6 +35,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection
classOf[PromotionPiece],
classOf[GameResult],
classOf[DrawReason],
),
)
class NativeReflectionConfig
@@ -32,7 +32,7 @@ class IoResource:
def importFen(body: ImportFenRequest): Uni[Response] =
Uni.createFrom().item {
FenParser.parseFen(body.fen) match
case Left(err) =>
case Left(err) =>
Response.status(400).entity(IoErrorDto("INVALID_FEN", err)).build()
case Right(ctx) =>
Response.ok(ctx).build()
@@ -52,7 +52,7 @@ class IoResource:
def importPgn(body: ImportPgnRequest): Uni[Response] =
Uni.createFrom().item {
PgnParser.importGameContext(body.pgn) match
case Left(err) =>
case Left(err) =>
Response.status(400).entity(IoErrorDto("INVALID_PGN", err)).build()
case Right(ctx) =>
Response.ok(ctx).build()
@@ -1,62 +0,0 @@
package de.nowchess.io.json
import com.fasterxml.jackson.core.`type`.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import de.nowchess.api.board.{File, Rank, Square}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
class SquareKeyDeserializerTest extends AnyFunSuite with Matchers:
private def mapper: ObjectMapper =
val m = new ObjectMapper()
val mod = new SimpleModule()
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
m.registerModule(DefaultScalaModule)
m.registerModule(mod)
m
private def readMap(json: String): Map[Square, Int] =
mapper.readValue(json, new TypeReference[Map[Square, Int]] {})
test("deserializes valid algebraic key") {
val result = readMap("""{"e4":1}""")
result(Square(File.E, Rank.R4)) shouldBe 1
}
test("deserializes a1 corner") {
val result = readMap("""{"a1":1}""")
result(Square(File.A, Rank.R1)) shouldBe 1
}
test("deserializes h8 corner") {
val result = readMap("""{"h8":1}""")
result(Square(File.H, Rank.R8)) shouldBe 1
}
test("deserializes multiple squares") {
val result = readMap("""{"a1":1,"h8":2,"e4":3}""")
result(Square(File.A, Rank.R1)) shouldBe 1
result(Square(File.H, Rank.R8)) shouldBe 2
result(Square(File.E, Rank.R4)) shouldBe 3
}
// scalafix:off DisableSyntax.null
test("deserializeKey returns null for invalid square") {
new SquareKeyDeserializer().deserializeKey("invalid", null) shouldBe null
}
test("deserializeKey returns null for wrong-length key") {
new SquareKeyDeserializer().deserializeKey("e44", null) shouldBe null
}
test("deserializeKey returns null for bad file") {
new SquareKeyDeserializer().deserializeKey("z4", null) shouldBe null
}
test("deserializeKey returns null for bad rank") {
new SquareKeyDeserializer().deserializeKey("e9", null) shouldBe null
}
// scalafix:on DisableSyntax.null
@@ -1,50 +0,0 @@
package de.nowchess.io.json
import com.fasterxml.jackson.core.`type`.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import de.nowchess.api.board.{File, Rank, Square}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
class SquareKeySerializerTest extends AnyFunSuite with Matchers:
private def mapper: ObjectMapper =
val m = new ObjectMapper()
val mod = new SimpleModule()
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
m.registerModule(DefaultScalaModule)
m.registerModule(mod)
m
test("serializes square as algebraic notation") {
val json = mapper.writeValueAsString(Map(Square(File.E, Rank.R4) -> 1))
json should include("\"e4\"")
}
test("serializes a1 corner") {
val json = mapper.writeValueAsString(Map(Square(File.A, Rank.R1) -> 1))
json should include("\"a1\"")
}
test("serializes h8 corner") {
val json = mapper.writeValueAsString(Map(Square(File.H, Rank.R8) -> 1))
json should include("\"h8\"")
}
test("round-trips with SquareKeyDeserializer") {
val rt = {
val m = new ObjectMapper()
val mod = new SimpleModule()
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
m.registerModule(DefaultScalaModule)
m.registerModule(mod)
m
}
val original = Map(Square(File.D, Rank.R5) -> 99)
val json = rt.writeValueAsString(original)
val result = rt.readValue(json, new TypeReference[Map[Square, Int]] {})
result shouldBe original
}
@@ -1,81 +0,0 @@
package de.nowchess.io.service.resource
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import de.nowchess.api.board.Square
import de.nowchess.api.game.GameContext
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
import io.quarkus.test.junit.QuarkusTest
import io.restassured.RestAssured
import io.restassured.http.ContentType
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
@QuarkusTest
class IoResourceTest:
private lazy val testMapper: ObjectMapper =
val m = new ObjectMapper()
val mod = new SimpleModule()
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
m.registerModule(new DefaultScalaModule())
m.registerModule(mod)
m
private def contextJson(ctx: GameContext): String = testMapper.writeValueAsString(ctx)
@Test
def importFenReturns200(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body("""{"fen":"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"}""")
.post("/io/import/fen")
assertEquals(200, resp.statusCode())
@Test
def importFenInvalidReturns400(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body("""{"fen":"not-a-fen"}""")
.post("/io/import/fen")
assertEquals(400, resp.statusCode())
@Test
def importPgnReturns200(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body("""{"pgn":"1. e4 e5"}""")
.post("/io/import/pgn")
assertEquals(200, resp.statusCode())
@Test
def importPgnInvalidReturns400(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body("""{"pgn":"not valid pgn !!!###"}""")
.post("/io/import/pgn")
assertEquals(400, resp.statusCode())
@Test
def exportFenReturns200WithFen(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body(contextJson(GameContext.initial))
.post("/io/export/fen")
assertEquals(200, resp.statusCode())
assertTrue(resp.getBody.asString().contains("rnbqkbnr"))
@Test
def exportPgnReturns200(): Unit =
val resp = RestAssured
.`given`()
.contentType(ContentType.JSON)
.body(contextJson(GameContext.initial))
.post("/io/export/pgn")
assertEquals(200, resp.statusCode())