package combatturns import ( "encoding/json" "strings" "testing" ) func TestExtractionResponseDecodingPreservesValidatorOwnedSemantics(t *testing.T) { content := []byte(`{"combat_turns":[{"actor":"","turn_kind":"unsupported","round":-1,"actions":[{"category":"unsupported","declaration":"","targets":[],"resolution":""}],"summary":"","source_refs":[{"start_unit_id":0,"end_unit_id":-1}]}]}`) var response extractionResponse if err := json.Unmarshal(content, &response); err != nil { t.Fatalf("json.Unmarshal() error = %v, want semantic candidate", err) } turn := response.CombatTurns[0] if turn.Round == nil || *turn.Round != -1 || turn.TurnKind != "unsupported" || turn.Actions[0].Category != "unsupported" || turn.Actions[0].Resolution == nil || *turn.Actions[0].Resolution != "" { t.Fatalf("decoded turn = %#v, want validator-owned values preserved", turn) } if turn.SourceRefs[0] != (combatSourceRefResponse{StartUnitID: 0, EndUnitID: -1}) { t.Fatalf("decoded source reference = %#v, want nonpositive values preserved", turn.SourceRefs[0]) } } func TestExtractionResponseDecodingDistinguishesMissingAndNullNullableFields(t *testing.T) { validNulls := []byte(`{"combat_turns":[{"actor":"Aria","turn_kind":"turn","round":null,"actions":[{"category":"attack","declaration":"attacks","targets":[],"resolution":null}],"summary":"attacks","source_refs":[{"start_unit_id":1,"end_unit_id":1}]}]}`) var response extractionResponse if err := json.Unmarshal(validNulls, &response); err != nil { t.Fatalf("json.Unmarshal(nulls) error = %v", err) } if response.CombatTurns[0].Round != nil || response.CombatTurns[0].Actions[0].Resolution != nil { t.Fatalf("decoded nullables = %#v, want explicit null", response.CombatTurns[0]) } for _, test := range []struct { name string content string field string }{ { name: "missing round", content: `{"combat_turns":[{"actor":"Aria","turn_kind":"turn","actions":[],"summary":"attacks","source_refs":[]}]}`, field: "round", }, { name: "missing resolution", content: `{"combat_turns":[{"actor":"Aria","turn_kind":"turn","round":null,"actions":[{"category":"attack","declaration":"attacks","targets":[]}],"summary":"attacks","source_refs":[]}]}`, field: "resolution", }, } { t.Run(test.name, func(t *testing.T) { var candidate extractionResponse err := json.Unmarshal([]byte(test.content), &candidate) if err == nil || !strings.Contains(err.Error(), test.field) { t.Fatalf("json.Unmarshal() error = %v, want missing %s failure", err, test.field) } }) } }