package dto import ( "encoding/json" "strings" "testing" "time" "browser.local/platform/domain" ) func TestGameClientBridgeRequestConversionsInjectScopeAndCopyPayloads(t *testing.T) { payload := map[string]any{"players": []any{map[string]any{"name": "Alice"}}} queue := GameClientBridgeQueueRequest{ProfileKey: "scum-client", CommandType: "player.lookup", Payload: payload, IdempotencyKey: "lookup-1", Priority: 5, ExpiresAt: time.Now().UTC().Add(time.Minute)}.ToDomain("server-1", "game.scum") claim := GameClientBridgeClaimRequest{SessionToken: "session", Limit: 5}.ToDomain() ack := GameClientBridgeAckRequest{SessionToken: "session", FencingToken: 7}.ToDomain("command-1") result := GameClientBridgeResultRequest{SessionToken: "session", FencingToken: 7, Status: "succeeded", Payload: payload}.ToDomain("command-1") cancel := GameClientBridgeCancelRequest{Reason: "operator request"}.ToDomain("command-1") snapshot := GameClientBridgeSnapshotIngestRequest{SessionToken: "session", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 2, Payload: payload, KeepForSeconds: 60, MaxRecords: 4}.ToDomain() query := GameClientBridgeSnapshotQuery{ProfileKey: "scum-client", Type: "players", StreamKey: "current", Limit: 10}.ToDomain("server-1", "game.scum") if queue.ServerInstanceID != "server-1" || queue.PluginID != "game.scum" || claim.Limit != 5 || ack.CommandID != "command-1" || result.CommandID != "command-1" || cancel.CommandID != "command-1" || snapshot.Retention.MaxRecords != 4 || query.ServerInstanceID != "server-1" || query.PluginID != "game.scum" { t.Fatalf("request conversion lost route or body fields: queue=%#v claim=%#v ack=%#v result=%#v cancel=%#v snapshot=%#v query=%#v", queue, claim, ack, result, cancel, snapshot, query) } queue.Payload["players"].([]any)[0].(map[string]any)["name"] = "queue" result.Payload["players"].([]any)[0].(map[string]any)["name"] = "result" snapshot.Payload["players"].([]any)[0].(map[string]any)["name"] = "snapshot" if got := payload["players"].([]any)[0].(map[string]any)["name"]; got != "Alice" { t.Fatalf("request conversion aliases source payload: %v", got) } } func TestGameClientBridgeBrowserProjectionsAreCompleteAndOmitInternalData(t *testing.T) { now := time.Date(2026, 7, 20, 10, 0, 0, 0, time.UTC) command := domain.GameClientBridgeCommand{ ID: "command-1", ServerInstanceID: "server-1", PluginID: "game.scum", ProfileKey: "scum-client", CommandType: "diagnostic.ping", Payload: map[string]any{"message": "internal command payload"}, IdempotencyKey: "internal-idempotency", Priority: 9, State: domain.GameClientBridgeCommandCancelled, ApprovalState: domain.GameClientBridgeApprovalApproved, RequesterID: "operator-1", Claim: domain.GameClientBridgeClaim{SessionID: "internal-session-secret", InstallationID: "internal-installation", DeploymentGeneration: 9, FencingToken: 42, LeaseExpiresAt: now.Add(time.Minute)}, Result: domain.GameClientBridgeResult{Status: domain.GameClientBridgeResultCancelled, Summary: "cancelled safely", Payload: map[string]any{"code": "cancelled"}, CompletedBy: "internal-completing-session", CompletedAt: now.Add(3 * time.Minute)}, Cancellation: domain.GameClientBridgeCancellation{RequestedBy: "operator-2", Reason: "operator request", CancelledAt: now.Add(2 * time.Minute)}, ExpiresAt: now.Add(10 * time.Minute), CreatedAt: now, UpdatedAt: now.Add(3 * time.Minute), CompletedAt: now.Add(3 * time.Minute), } snapshot := domain.GameClientBridgeSnapshot{ID: "snapshot-1", ServerInstanceID: "server-1", PluginID: "game.scum", ProfileKey: "scum-client", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 2, SourceSessionID: "internal-source-session", ObservedAt: now, Payload: map[string]any{"players": []any{map[string]any{"name": "Alice"}}}, Retention: domain.GameClientBridgeRetention{KeepForSeconds: 3600, MaxRecords: 10}, CreatedAt: now.Add(time.Second), ExpiresAt: now.Add(time.Hour)} commandProjection := GameClientBridgeCommandFromDomain(command) snapshotProjection := GameClientBridgeSnapshotFromDomain(snapshot) if commandProjection.Result == nil || commandProjection.Result.Status != "cancelled" || commandProjection.Cancellation == nil || commandProjection.Cancellation.Reason != "operator request" || commandProjection.Priority != 9 || !commandProjection.CompletedAt.Equal(command.CompletedAt) { t.Fatalf("incomplete command projection: %#v", commandProjection) } if snapshotProjection.ProfileKey != "scum-client" || snapshotProjection.Retention.KeepForSeconds != 3600 || !snapshotProjection.CreatedAt.Equal(snapshot.CreatedAt) { t.Fatalf("incomplete snapshot projection: %#v", snapshotProjection) } commandProjection.Result.Payload["code"] = "changed" snapshotProjection.Payload["players"].([]any)[0].(map[string]any)["name"] = "changed" if command.Result.Payload["code"] != "cancelled" || snapshot.Payload["players"].([]any)[0].(map[string]any)["name"] != "Alice" { t.Fatal("browser projection aliases domain data") } encoded, err := json.Marshal(struct { Command GameClientBridgeCommandResponse `json:"command"` Snapshot GameClientBridgeSnapshotResponse `json:"snapshot"` }{commandProjection, snapshotProjection}) if err != nil { t.Fatalf("marshal projections: %v", err) } text := string(encoded) for _, forbidden := range []string{"internal-session-secret", "internal-installation", "internal-completing-session", "internal-source-session", "internal-idempotency", "internal command payload", "fencingToken", "deploymentGeneration", "sourceSessionId", "completedBy", "idempotencyKey", "leaseExpiresAt"} { if strings.Contains(text, forbidden) { t.Fatalf("browser projection leaked %q: %s", forbidden, text) } } } func TestGameClientBridgeListAndCompanionResponseConversions(t *testing.T) { now := time.Date(2026, 7, 20, 10, 0, 0, 0, time.UTC) command := domain.GameClientBridgeCommand{ ID: "command-1", ProfileKey: "scum-client", CommandType: "diagnostic.safe", Payload: map[string]any{"scope": "health"}, Priority: 3, State: domain.GameClientBridgeCommandSucceeded, Claim: domain.GameClientBridgeClaim{SessionID: "private-session", FencingToken: 7, ClaimedAt: now, AcknowledgedAt: now.Add(time.Second), LeaseExpiresAt: now.Add(time.Minute)}, Result: domain.GameClientBridgeResult{Status: domain.GameClientBridgeResultSucceeded, Summary: "ok", Payload: map[string]any{"healthy": true}, CompletedBy: "private-session", CompletedAt: now.Add(2 * time.Second)}, Cancellation: domain.GameClientBridgeCancellation{RequestedBy: "operator", Reason: "superseded", CancelledAt: now.Add(3 * time.Second)}, ExpiresAt: now.Add(5 * time.Minute), UpdatedAt: now.Add(2 * time.Second), CompletedAt: now.Add(2 * time.Second), } snapshot := domain.GameClientBridgeSnapshot{ID: "snapshot-1", ProfileKey: "scum-client", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 4, CreatedAt: now, ExpiresAt: now.Add(time.Hour)} claim := GameClientBridgeClaimBatchFromDomain([]domain.GameClientBridgeCommand{command}) ack := GameClientBridgeAckFromDomain(command) result := GameClientBridgeResultFromDomain(command) cancel := GameClientBridgeCancelFromDomain(command) ingest := GameClientBridgeSnapshotIngestFromDomain(snapshot) commands := GameClientBridgeCommandsFromDomain([]domain.GameClientBridgeCommand{command}) snapshots := GameClientBridgeSnapshotsFromDomain([]domain.GameClientBridgeSnapshot{snapshot}) if claim.Count != 1 || claim.Items[0].FencingToken != 7 || claim.Items[0].ProfileKey != "scum-client" || ack.CommandID != "command-1" || !ack.AcknowledgedAt.Equal(now.Add(time.Second)) || result.Result.Summary != "ok" || cancel.Cancellation.Reason != "superseded" || ingest.SnapshotID != "snapshot-1" || commands.Count != 1 || snapshots.Count != 1 { t.Fatalf("unexpected responses: claim=%#v ack=%#v result=%#v cancel=%#v ingest=%#v", claim, ack, result, cancel, ingest) } claim.Items[0].Payload["scope"] = "changed" result.Result.Payload["healthy"] = false if command.Payload["scope"] != "health" || command.Result.Payload["healthy"] != true { t.Fatal("companion response aliases domain payload") } for name, value := range map[string]any{"claim": claim, "ack": ack, "result": result, "cancel": cancel, "ingest": ingest, "commands": commands, "snapshots": snapshots} { encoded, err := json.Marshal(value) if err != nil { t.Fatalf("marshal %s response: %v", name, err) } if strings.Contains(string(encoded), "private-session") || strings.Contains(string(encoded), "sessionId") || strings.Contains(string(encoded), "completedBy") { t.Fatalf("%s response exposes a private session: %s", name, encoded) } } if GameClientBridgeCommandsFromDomain(nil).Items == nil || GameClientBridgeSnapshotsFromDomain(nil).Items == nil || GameClientBridgeClaimBatchFromDomain(nil).Items == nil { t.Fatal("list converters must serialize empty items as [] instead of null") } }