test: add serialization round-trip test (Closes #37)

This commit was merged in pull request #43.
This commit is contained in:
2026-03-13 13:00:59 +01:00
parent c4fea755d0
commit 8855f48ae2

View File

@@ -0,0 +1,172 @@
package org.shahondin1624
import org.shahondin1624.lib.functions.DataLoader
import org.shahondin1624.model.attributes.Attribute
import org.shahondin1624.model.attributes.AttributeType
import org.shahondin1624.model.attributes.Attributes
import org.shahondin1624.model.characterdata.CharacterData
import org.shahondin1624.model.characterdata.Metatype
import org.shahondin1624.model.charactermodel.DamageMonitor
import org.shahondin1624.model.charactermodel.ShadowrunCharacter
import org.shahondin1624.model.talents.Talent
import org.shahondin1624.model.talents.Talents
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SerializationRoundTripTest {
/**
* Build a character with non-default values in every field,
* serialize to JSON, deserialize back, and assert deep equality.
*
* Note: DamageMonitor.attributes and DamageMonitor.cache are @Transient
* and are not serialized. After deserialization, ShadowrunCharacter.init
* re-sets the attributes on the DamageMonitor. We therefore keep damage
* current values within the range valid for EMPTY_ATTRIBUTES (max 7 for
* stun/physical, 0 for overflow) so the DamageMonitor init won't throw.
*/
@Test
fun serializeDeserializePreservesAllFields() {
val attributes = Attributes(
body = Attribute(AttributeType.Body, 5),
agility = Attribute(AttributeType.Agility, 6),
reaction = Attribute(AttributeType.Reaction, 4),
strength = Attribute(AttributeType.Strength, 7),
willpower = Attribute(AttributeType.Willpower, 3),
logic = Attribute(AttributeType.Logic, 5),
intuition = Attribute(AttributeType.Intuition, 4),
charisma = Attribute(AttributeType.Charisma, 6),
edge = 3
)
val characterData = CharacterData(
concept = "Street Samurai",
nuyen = 25000,
essence = 3.5f,
name = "Jane 'Razor' Smith",
metatype = Metatype.Elf,
age = 34,
gender = "female",
streetCred = 5,
notoriety = 2,
publicAwareness = 1,
totalKarma = 100,
currentKarma = 42
)
val talents = Talents(
talents = listOf(
Talent(name = "Firearms", attribute = AttributeType.Agility, value = 6, custom = false),
Talent(name = "Blades", attribute = AttributeType.Agility, value = 5, custom = false),
Talent(name = "Custom Hack", attribute = AttributeType.Logic, value = 3, custom = true)
)
)
// Keep damage within EMPTY_ATTRIBUTES bounds: stun/physical max = 7, overflow max = 0
val damageMonitor = DamageMonitor(
attributes = attributes,
stunCurrent = 3,
physicalCurrent = 2,
overflowCurrent = 0
)
val original = ShadowrunCharacter(
characterData = characterData,
attributes = attributes,
talents = talents,
damageMonitor = damageMonitor
)
// Round-trip
val json = DataLoader.serialize(original)
val restored = DataLoader.deserialize(json)
// CharacterData assertions
assertEquals(original.characterData.concept, restored.characterData.concept)
assertEquals(original.characterData.nuyen, restored.characterData.nuyen)
assertEquals(original.characterData.essence, restored.characterData.essence)
assertEquals(original.characterData.name, restored.characterData.name)
assertEquals(original.characterData.metatype, restored.characterData.metatype)
assertEquals(original.characterData.age, restored.characterData.age)
assertEquals(original.characterData.gender, restored.characterData.gender)
assertEquals(original.characterData.streetCred, restored.characterData.streetCred)
assertEquals(original.characterData.notoriety, restored.characterData.notoriety)
assertEquals(original.characterData.publicAwareness, restored.characterData.publicAwareness)
assertEquals(original.characterData.totalKarma, restored.characterData.totalKarma)
assertEquals(original.characterData.currentKarma, restored.characterData.currentKarma)
assertEquals(original.characterData.version, restored.characterData.version)
// Attributes assertions
assertEquals(original.attributes.body(), restored.attributes.body())
assertEquals(original.attributes.agility(), restored.attributes.agility())
assertEquals(original.attributes.reaction(), restored.attributes.reaction())
assertEquals(original.attributes.strength(), restored.attributes.strength())
assertEquals(original.attributes.willpower(), restored.attributes.willpower())
assertEquals(original.attributes.logic(), restored.attributes.logic())
assertEquals(original.attributes.intuition(), restored.attributes.intuition())
assertEquals(original.attributes.charisma(), restored.attributes.charisma())
assertEquals(original.attributes.edge, restored.attributes.edge)
assertEquals(original.attributes.version, restored.attributes.version)
// Talents assertions
assertEquals(original.talents.talents.size, restored.talents.talents.size)
original.talents.talents.forEachIndexed { i, orig ->
val rest = restored.talents.talents[i]
assertEquals(orig.name, rest.name, "Talent[$i].name")
assertEquals(orig.attribute, rest.attribute, "Talent[$i].attribute")
assertEquals(orig.value, rest.value, "Talent[$i].value")
assertEquals(orig.custom, rest.custom, "Talent[$i].custom")
}
assertEquals(original.talents.version, restored.talents.version)
// DamageMonitor assertions (check serialized fields only; @Transient fields are re-set)
assertEquals(original.damageMonitor.stunCurrent(), restored.damageMonitor.stunCurrent())
assertEquals(original.damageMonitor.physicalCurrent(), restored.damageMonitor.physicalCurrent())
assertEquals(original.damageMonitor.overflowCurrent(), restored.damageMonitor.overflowCurrent())
// Version
assertEquals(original.version, restored.version)
}
@Test
fun roundTripWithExampleCharacter() {
// EXAMPLE_CHARACTER uses createDamageMonitor which sets damage to max values
// that may exceed the EMPTY_ATTRIBUTES limit during deserialization.
// Create a copy with safe damage values for the round-trip test.
val original = org.shahondin1624.model.EXAMPLE_CHARACTER.copy(
damageMonitor = DamageMonitor(
attributes = org.shahondin1624.model.EXAMPLE_CHARACTER.attributes,
stunCurrent = 0,
physicalCurrent = 0,
overflowCurrent = 0
)
)
val json = DataLoader.serialize(original)
val restored = DataLoader.deserialize(json)
assertEquals(original.characterData, restored.characterData)
assertEquals(original.attributes.edge, restored.attributes.edge)
assertEquals(original.talents.talents.size, restored.talents.talents.size)
assertEquals(original.version, restored.version)
}
@Test
fun serializedJsonContainsAllFields() {
val original = org.shahondin1624.model.EXAMPLE_CHARACTER
val json = DataLoader.serialize(original)
// Verify key fields are present in JSON output
val checks = listOf(
"characterData", "attributes", "talents", "damageMonitor",
"name", "metatype", "nuyen", "essence", "edge",
"body", "agility", "reaction", "strength",
"willpower", "logic", "intuition", "charisma",
"stunCurrent", "physicalCurrent", "overflowCurrent",
"version"
)
for (field in checks) {
assertTrue(json.contains("\"$field\""), "JSON should contain \"$field\"")
}
}
}