package adminstate import ( "encoding/json" "errors" "strings" "testing" "time" ) func TestValidateSetUpstreamCommand(t *testing.T) { t.Parallel() now := time.Date(2026, 7, 29, 10, 0, 0, 0, time.UTC) valid := SetUpstreamCommand{ RequestID: "req-upstream", Actor: Actor{ID: "admin-a", SourceIP: "192.0.2.10"}, OccurredAt: now, Name: "provider-a", Enabled: true, } if err := valid.Validate(); err != nil { t.Fatalf("SetUpstreamCommand.Validate(valid): %v", err) } tests := []struct { name string mutate func(*SetUpstreamCommand) }{ {name: "missing request", mutate: func(command *SetUpstreamCommand) { command.RequestID = "" }}, {name: "missing actor", mutate: func(command *SetUpstreamCommand) { command.Actor.ID = "" }}, {name: "invalid source", mutate: func(command *SetUpstreamCommand) { command.Actor.SourceIP = "not-an-ip" }}, {name: "zero time", mutate: func(command *SetUpstreamCommand) { command.OccurredAt = time.Time{} }}, {name: "invalid name", mutate: func(command *SetUpstreamCommand) { command.Name = "provider/a" }}, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() command := valid test.mutate(&command) if err := command.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("SetUpstreamCommand.Validate() error = %v, want ErrInvalidCommand", err) } }) } } func TestValidateSwitchRoutingCommand(t *testing.T) { t.Parallel() now := time.Date(2026, 7, 29, 10, 0, 0, 0, time.UTC) valid := SwitchRoutingCommand{ RequestID: "req-switch", Actor: Actor{ID: "admin-a"}, OccurredAt: now, Name: "checkout", ExpectedCurrent: "provider-a", Target: "provider-b", Reason: "capacity", } if err := valid.Validate(); err != nil { t.Fatalf("SwitchRoutingCommand.Validate(valid): %v", err) } for _, test := range []struct { name string mutate func(*SwitchRoutingCommand) }{ {name: "missing routing", mutate: func(command *SwitchRoutingCommand) { command.Name = "" }}, {name: "missing expected", mutate: func(command *SwitchRoutingCommand) { command.ExpectedCurrent = "" }}, {name: "missing target", mutate: func(command *SwitchRoutingCommand) { command.Target = "" }}, {name: "reason too long", mutate: func(command *SwitchRoutingCommand) { command.Reason = strings.Repeat("r", MaxReasonBytes+1) }}, } { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() command := valid test.mutate(&command) if err := command.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("SwitchRoutingCommand.Validate() error = %v, want ErrInvalidCommand", err) } }) } } func TestValidateCommitConfigCommandAndReferences(t *testing.T) { t.Parallel() valid := validCommitConfigCommand() if err := valid.Validate(); err != nil { t.Fatalf("CommitConfigCommand.Validate(valid): %v", err) } for _, test := range []struct { name string mutate func(*CommitConfigCommand) }{ {name: "invalid checksum", mutate: func(command *CommitConfigCommand) { command.Checksum = "sha256:bad" }}, {name: "duplicate upstream", mutate: func(command *CommitConfigCommand) { command.Upstreams = append(command.Upstreams, command.Upstreams[0]) }}, {name: "duplicate routing", mutate: func(command *CommitConfigCommand) { command.Routings = append(command.Routings, command.Routings[0]) }}, {name: "duplicate candidate", mutate: func(command *CommitConfigCommand) { command.Routings[0].Upstreams = append(command.Routings[0].Upstreams, "provider-a") }}, {name: "unknown candidate", mutate: func(command *CommitConfigCommand) { command.Routings[0].Upstreams[0] = "provider-missing" }}, {name: "current not candidate", mutate: func(command *CommitConfigCommand) { command.Routings[0].CurrentUpstream = "provider-c" }}, {name: "empty candidate list", mutate: func(command *CommitConfigCommand) { command.Routings[0].Upstreams = nil }}, } { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() command := cloneCommitConfigCommand(valid) test.mutate(&command) if err := command.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("CommitConfigCommand.Validate() error = %v, want ErrInvalidCommand", err) } }) } } func TestValidateOutboxCommands(t *testing.T) { t.Parallel() now := time.Date(2026, 7, 29, 10, 0, 0, 0, time.UTC) if err := (ClaimCommand{ ConsumerID: "publisher-a", Now: now, Limit: 100, Lease: time.Minute, }).Validate(); err != nil { t.Fatalf("ClaimCommand.Validate(valid): %v", err) } if err := (AcknowledgeCommand{ ConsumerID: "publisher-a", Now: now, EventIDs: []uint64{1, 2}, }).Validate(); err != nil { t.Fatalf("AcknowledgeCommand.Validate(valid): %v", err) } invalidClaims := []ClaimCommand{ {Now: now, Limit: 1, Lease: time.Second}, {ConsumerID: "publisher-a", Limit: 1, Lease: time.Second}, {ConsumerID: "publisher-a", Now: now, Lease: time.Second}, {ConsumerID: "publisher-a", Now: now, Limit: MaxPageSize + 1, Lease: time.Second}, {ConsumerID: "publisher-a", Now: now, Limit: 1}, } for _, command := range invalidClaims { if err := command.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("ClaimCommand.Validate(%+v) error = %v", command, err) } } invalidAcks := []AcknowledgeCommand{ {Now: now, EventIDs: []uint64{1}}, {ConsumerID: "publisher-a", EventIDs: []uint64{1}}, {ConsumerID: "publisher-a", Now: now}, {ConsumerID: "publisher-a", Now: now, EventIDs: []uint64{0}}, {ConsumerID: "publisher-a", Now: now, EventIDs: []uint64{1, 1}}, } for _, command := range invalidAcks { if err := command.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("AcknowledgeCommand.Validate(%+v) error = %v", command, err) } } } func TestAuditQueryValidate(t *testing.T) { t.Parallel() if err := (AuditQuery{Limit: 10}).Validate(); err != nil { t.Fatalf("AuditQuery.Validate(valid): %v", err) } for _, query := range []AuditQuery{{}, {Limit: -1}, {Limit: MaxPageSize + 1}} { if err := query.Validate(); !errors.Is(err, ErrInvalidCommand) { t.Fatalf("AuditQuery.Validate(%+v) error = %v, want ErrInvalidCommand", query, err) } } } func TestCloneValuesDoNotShareMutableState(t *testing.T) { t.Parallel() command := validCommitConfigCommand() clonedCommand := cloneCommitConfigCommand(command) clonedCommand.Upstreams[0].Name = "changed" clonedCommand.Routings[0].Upstreams[0] = "changed" if command.Upstreams[0].Name != "provider-a" || command.Routings[0].Upstreams[0] != "provider-a" { t.Fatalf("cloneCommitConfigCommand shared input storage: %+v", command) } snapshot := Snapshot{ Revision: 3, Config: &ConfigRevision{Revision: 3, ConfigVersion: "cfg-3"}, Upstreams: []UpstreamState{{Name: "provider-a", Enabled: true}}, Routings: []RoutingState{{Name: "checkout", Upstreams: []string{"provider-a"}, CurrentUpstream: "provider-a"}}, } clonedSnapshot := cloneSnapshot(snapshot) clonedSnapshot.Config.ConfigVersion = "changed" clonedSnapshot.Upstreams[0].Name = "changed" clonedSnapshot.Routings[0].Upstreams[0] = "changed" if snapshot.Config.ConfigVersion != "cfg-3" || snapshot.Upstreams[0].Name != "provider-a" || snapshot.Routings[0].Upstreams[0] != "provider-a" { t.Fatalf("cloneSnapshot shared input storage: %+v", snapshot) } event := Event{Payload: json.RawMessage(`{"enabled":true}`)} clonedEvent := cloneEvent(event) clonedEvent.Payload[2] = 'X' if string(event.Payload) != `{"enabled":true}` { t.Fatalf("cloneEvent shared payload storage: %s", event.Payload) } } func validCommitConfigCommand() CommitConfigCommand { return CommitConfigCommand{ RequestID: "req-config", Actor: Actor{ID: "admin-a", SourceIP: "192.0.2.10"}, OccurredAt: time.Date(2026, 7, 29, 10, 0, 0, 0, time.UTC), ConfigVersion: "cfg-1", Checksum: strings.Repeat("a", SHA256HexBytes), Source: "configs/proxy-pool.yaml", Upstreams: []UpstreamDefinition{ {Name: "provider-a", Enabled: true}, {Name: "provider-b", Enabled: true}, }, Routings: []RoutingDefinition{{ Name: "checkout", Enabled: true, Upstreams: []string{"provider-a", "provider-b"}, CurrentUpstream: "provider-a", }}, } }