package protocol import "testing" // Golden wire frames as emitted by the Kotlin server (kotlinx.serialization). These // strings are the cross-language contract: the Kotlin side pins the encode in // ServerMessageSerializationTest; this test pins the Go decode of the exact same bytes, // so a discriminator (@SerialName) or field-name drift fails loudly on one side. func TestDecodeGoldenServerFrames(t *testing.T) { cases := []struct { name string json string wantType string eventBear bool check func(t *testing.T, m ServerMessage) }{ { name: "session.announced", json: `{"type":"session.announced","sessionId":"s1","workflowId":"healthcheck","sequence":6,"sessionSequence":1}`, wantType: TypeSessionAnnounced, eventBear: true, check: func(t *testing.T, m ServerMessage) { if m.SessionID != "s1" || m.WorkflowID != "healthcheck" { t.Fatalf("session.announced fields: %+v", m) } }, }, { name: "chat.turn", json: `{"type":"chat.turn","sessionId":"s1","turnId":"t9","role":"ROUTER","content":"hi there","sequence":7,"sessionSequence":2}`, wantType: TypeChatTurn, eventBear: true, check: func(t *testing.T, m ServerMessage) { if m.Role != "ROUTER" || m.Content != "hi there" || m.TurnID != "t9" { t.Fatalf("chat.turn fields: %+v", m) } }, }, { name: "stage.started", json: `{"type":"stage.started","sessionId":"s1","stageId":"write_script","occurredAt":123,"sequence":8,"sessionSequence":3}`, wantType: TypeStageStarted, eventBear: true, check: func(t *testing.T, m ServerMessage) { if m.StageID != "write_script" { t.Fatalf("stage.started fields: %+v", m) } }, }, { name: "tool.completed", json: `{"type":"tool.completed","sessionId":"s1","toolName":"shell","outputSummary":"ok","occurredAt":1,"diff":null,"sequence":9,"sessionSequence":4}`, wantType: TypeToolCompleted, eventBear: true, check: func(t *testing.T, m ServerMessage) { if m.ToolName != "shell" || m.Summary != "ok" { t.Fatalf("tool.completed fields: %+v", m) } }, }, { name: "snapshot_complete (non-event control frame)", json: `{"type":"snapshot_complete"}`, wantType: TypeSnapshotComplete, eventBear: false, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { m, err := Decode([]byte(c.json)) if err != nil { t.Fatalf("decode failed: %v", err) } if m.Type != c.wantType { t.Fatalf("type = %q, want %q", m.Type, c.wantType) } if m.IsEventBearing() != c.eventBear { t.Fatalf("IsEventBearing = %v, want %v", m.IsEventBearing(), c.eventBear) } if c.check != nil { c.check(t, m) } }) } }