fix(chess): encode required fields in Jester content JSON

JesterContent fields version, fen, and history were omitted from
serialized JSON because kotlinx.serialization skips default values.
Jester's isStartGameEvent checks arrayEquals(json.history, []) which
fails when the field is absent (undefined !== []).

Added @EncodeDefault to version, fen, and history so they are always
included in the JSON output regardless of value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-03-23 09:08:28 +02:00
parent 677bc6c34a
commit 9d8d0e249d
2 changed files with 58 additions and 4 deletions
@@ -20,18 +20,26 @@
*/
package com.vitorpamplona.quartz.nip64Chess.jester
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
/**
* JSON content structure for Jester events
* JSON content structure for Jester events.
*
* Fields marked @EncodeDefault are always serialized even when they match
* the default value. This is required for jester.nyo.dev compatibility —
* jester's isStartGameEvent checks arrayEquals(json.history, []) which
* fails if the field is absent.
*/
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class JesterContent(
val version: String = "0",
@EncodeDefault val version: String = "0",
val kind: Int,
val fen: String = JesterProtocol.FEN_START,
@EncodeDefault val fen: String = JesterProtocol.FEN_START,
val move: String? = null,
val history: List<String> = emptyList(),
@EncodeDefault val history: List<String> = emptyList(),
val nonce: String? = null,
// Extended fields for Amethyst (backward compatible - jesterui ignores unknown fields)
val playerColor: String? = null, // "white" or "black" - challenger's color choice
@@ -568,6 +568,52 @@ class ChessInteropBugTest {
)
}
// ==========================================================================
// CATEGORY 5: JESTER CONTENT SERIALIZATION
// ==========================================================================
@Test
fun `serialized start content includes version fen and history fields`() {
// Jester's isStartGameEvent checks arrayEquals(json.history, [])
// which fails if history field is absent from JSON
val content =
com.vitorpamplona.quartz.nip64Chess.jester.JesterContent(
kind = 0,
nonce = "test1234",
playerColor = "white",
)
val json =
com.vitorpamplona.quartz.nip01Core.core.JsonMapper
.toJson(content)
assertTrue(json.contains("\"version\""), "JSON must include version field: $json")
assertTrue(json.contains("\"fen\""), "JSON must include fen field: $json")
assertTrue(json.contains("\"history\""), "JSON must include history field: $json")
assertTrue(json.contains("\"history\":[]"), "history must be empty array: $json")
}
@Test
fun `serialized move content includes version fen and history fields`() {
val content =
com.vitorpamplona.quartz.nip64Chess.jester.JesterContent(
kind = 1,
fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
move = "e4",
history = listOf("e4"),
)
val json =
com.vitorpamplona.quartz.nip01Core.core.JsonMapper
.toJson(content)
assertTrue(json.contains("\"version\""), "JSON must include version field: $json")
assertTrue(json.contains("\"fen\""), "JSON must include fen field: $json")
assertTrue(json.contains("\"history\""), "JSON must include history field: $json")
}
// ==========================================================================
// HELPERS
// ==========================================================================
private fun createMoveEvent(
id: String,
pubKey: String,