Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 306 additions & 0 deletions pkg/util/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ package config

import (
"fmt"
"os"
"reflect"
"strings"
"testing"
)

func TestMain(m *testing.M) {
// Set OPERATOR_NAMESPACE to avoid log.Fatal in GetOperatorNamespace
// when running tests outside a Kubernetes pod
os.Setenv("OPERATOR_NAMESPACE", "default")
os.Exit(m.Run())
}

var getMapPairsFromStringTest = []struct {
in string
expected []string
Expand All @@ -29,3 +38,300 @@ func TestGetMapPairsFromString(t *testing.T) {
}
}
}

func int32Ptr(i int32) *int32 {
return &i
}

func boolPtr(b bool) *bool {
return &b
}

var validateTests = []struct {
description string
cfg Config
expectError bool
errorMsg string
}{
{
description: "valid config",
cfg: Config{
Resources: Resources{
MinInstances: 1,
MaxInstances: 5,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(2),
User: "pooler",
},
Workers: 4,
},
expectError: false,
},
{
description: "min instances greater than max instances",
cfg: Config{
Resources: Resources{
MinInstances: 10,
MaxInstances: 5,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(2),
User: "pooler",
},
Workers: 4,
},
expectError: true,
errorMsg: "minimum number of instances",
},
{
description: "workers set to zero",
cfg: Config{
Resources: Resources{
MinInstances: 1,
MaxInstances: 5,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(2),
User: "pooler",
},
Workers: 0,
},
expectError: true,
errorMsg: "number of workers should be higher than 0",
},
{
description: "connection pooler instances below minimum",
cfg: Config{
Resources: Resources{
MinInstances: 1,
MaxInstances: 5,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(0),
User: "pooler",
},
Workers: 4,
},
expectError: true,
errorMsg: "number of connection pooler instances",
},
{
description: "connection pooler user same as super user",
cfg: Config{
Resources: Resources{
MinInstances: 1,
MaxInstances: 5,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(2),
User: "postgres",
},
Workers: 4,
},
expectError: true,
errorMsg: "connection pool user is not allowed to be the same as super user",
},
{
description: "min and max instances both negative (disabled)",
cfg: Config{
Resources: Resources{
MinInstances: -1,
MaxInstances: -1,
},
Auth: Auth{
SuperUsername: "postgres",
},
ConnectionPooler: ConnectionPooler{
NumberOfInstances: int32Ptr(2),
User: "pooler",
},
Workers: 4,
},
expectError: false,
},
}

func TestValidate(t *testing.T) {
for _, tt := range validateTests {
t.Run(tt.description, func(t *testing.T) {
err := validate(&tt.cfg)
if tt.expectError {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errorMsg)
return
}
if !strings.Contains(err.Error(), tt.errorMsg) {
t.Errorf("expected error containing %q, got %q", tt.errorMsg, err.Error())
}
} else {
if err != nil {
t.Errorf("expected no error, got %v", err)
}
}
})
}
}

var newFromMapTests = []struct {
description string
input map[string]string
expectPanic bool
panicMsg string
validateFunc func(t *testing.T, cfg *Config)
}{
{
description: "empty map uses defaults",
input: map[string]string{},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.Workers != 8 {
t.Errorf("expected default Workers=8, got %d", cfg.Workers)
}
if cfg.SuperUsername != "postgres" {
t.Errorf("expected default SuperUsername=postgres, got %s", cfg.SuperUsername)
}
if cfg.ReplicationUsername != "standby" {
t.Errorf("expected default ReplicationUsername=standby, got %s", cfg.ReplicationUsername)
}
},
},
{
description: "custom values override defaults",
input: map[string]string{
"workers": "16",
"super_username": "admin",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.Workers != 16 {
t.Errorf("expected Workers=16, got %d", cfg.Workers)
}
if cfg.SuperUsername != "admin" {
t.Errorf("expected SuperUsername=admin, got %s", cfg.SuperUsername)
}
},
},
{
description: "duration parsing",
input: map[string]string{
"ready_wait_interval": "10s",
"ready_wait_timeout": "1m",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.ReadyWaitInterval.Seconds() != 10 {
t.Errorf("expected ReadyWaitInterval=10s, got %v", cfg.ReadyWaitInterval)
}
if cfg.ReadyWaitTimeout.Minutes() != 1 {
t.Errorf("expected ReadyWaitTimeout=1m, got %v", cfg.ReadyWaitTimeout)
}
},
},
{
description: "boolean parsing",
input: map[string]string{
"enable_teams_api": "false",
"debug_logging": "false",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.EnableTeamsAPI != false {
t.Errorf("expected EnableTeamsAPI=false, got %v", cfg.EnableTeamsAPI)
}
if cfg.DebugLogging != false {
t.Errorf("expected DebugLogging=false, got %v", cfg.DebugLogging)
}
},
},
{
description: "map parsing",
input: map[string]string{
"cluster_labels": "app:myapp,env:prod",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.ClusterLabels["app"] != "myapp" {
t.Errorf("expected ClusterLabels[app]=myapp, got %s", cfg.ClusterLabels["app"])
}
if cfg.ClusterLabels["env"] != "prod" {
t.Errorf("expected ClusterLabels[env]=prod, got %s", cfg.ClusterLabels["env"])
}
},
},
{
description: "slice parsing",
input: map[string]string{
"inherited_labels": "label1,label2,label3",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
expected := []string{"label1", "label2", "label3"}
if !reflect.DeepEqual(cfg.InheritedLabels, expected) {
t.Errorf("expected InheritedLabels=%v, got %v", expected, cfg.InheritedLabels)
}
},
},
{
description: "invalid workers triggers validation panic",
input: map[string]string{
"workers": "0",
},
expectPanic: true,
panicMsg: "number of workers should be higher than 0",
},
{
description: "invalid integer causes panic",
input: map[string]string{
"workers": "invalid",
},
expectPanic: true,
panicMsg: "invalid syntax",
},
}

func TestNewFromMap(t *testing.T) {
for _, tt := range newFromMapTests {
t.Run(tt.description, func(t *testing.T) {
if tt.expectPanic {
defer func() {
r := recover()
if r == nil {
t.Errorf("expected panic with message containing %q, but no panic occurred", tt.panicMsg)
return
}
errMsg := fmt.Sprintf("%v", r)
if !strings.Contains(errMsg, tt.panicMsg) {
t.Errorf("expected panic message containing %q, got %q", tt.panicMsg, errMsg)
}
}()
}

cfg := NewFromMap(tt.input)

if tt.expectPanic {
t.Errorf("expected panic but NewFromMap returned successfully")
return
}

if tt.validateFunc != nil {
tt.validateFunc(t, cfg)
}
})
}
}
Loading