diff --git a/pkg/spec/postgresql_test.go b/pkg/spec/postgresql_test.go new file mode 100644 index 000000000..0ec47821d --- /dev/null +++ b/pkg/spec/postgresql_test.go @@ -0,0 +1,347 @@ +package spec + +import ( + "reflect" + "testing" + "time" + "encoding/json" + "bytes" + + "k8s.io/client-go/pkg/api/unversioned" + "k8s.io/client-go/pkg/api/v1" +) + +var pTests = []struct { + s string + time time.Time + weekday time.Weekday + weekdayProvided bool +}{ + {"Mon:16:08", mustParseTime("16:08"), time.Monday, true}, + {"Sun:11:00", mustParseTime("11:00"), time.Sunday, true}, + {"23:59", mustParseTime("23:59"), time.Weekday(0), false}, +} + +var pErr = []string{"Thr:00:12", "26:09", "Std:26:09", "Saturday:00:00"} + +var clusterNames = []struct { + s string + team string + clusterName string +}{ + {"acid-test", "acid", "test"}, + {"test-my-name", "test", "my-name"}, + {"my-team-another-test", "my-team", "another-test"}, + {"------strange-team-cluster", "-----", "strange-team-cluster"}, +} + +var wrongClusterNames = []struct { + s string + team string +}{ + {"acid-test", "test"}, + {"-test", ""}, + {"-test", "-"}, + {"", "-"}, + {"-", "-"}, +} + +var maintenanceWindows = []struct { + s string + m MaintenanceWindow +}{{`"10:00-20:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("10:00"), + StartWeekday: time.Monday, + EndTime: mustParseTime("20:00"), + EndWeekday: time.Sunday, + }}, + {`"Tue:10:00-Sun:23:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("10:00"), + StartWeekday: time.Tuesday, + EndTime: mustParseTime("23:00"), + EndWeekday: time.Sunday, + }}, + {`"Mon:10:00-Mon:10:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("10:00"), + StartWeekday: time.Monday, + EndTime: mustParseTime("10:00"), + EndWeekday: time.Monday, + }}, + {`"Sun:00:00-Sun:00:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("00:00"), + StartWeekday: time.Sunday, + EndTime: mustParseTime("00:00"), + EndWeekday: time.Sunday, + }}, + {`"00:00-10:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("00:00"), + StartWeekday: time.Monday, + EndTime: mustParseTime("10:00"), + EndWeekday: time.Sunday, + }}, + {`"00:00-00:00"`, + MaintenanceWindow{ + StartTime: mustParseTime("00:00"), + StartWeekday: time.Monday, + EndTime: mustParseTime("00:00"), + EndWeekday: time.Sunday, + }}, +} + +var wrongMaintenanceWindows = [][]byte{ + []byte(`"Mon:12:00-Sun:11:00"`), + []byte(`"Mon:12:00-Mon:11:00"`), + []byte(`"Wed:00:00-Tue:26:00"`), + []byte(`"Sun:00:00-Mon:00:00"`), + []byte(`"Wed:00:00-Mon:10:00"`), + []byte(`"10:00-00:00"`), + []byte(`"Mon:00:00:00-Tue:10:00:00"`), + []byte(`"Mon:00:00"`), +} + +var cl = []byte(`{ + "kind": "Postgresql", + "apiVersion": "acid.zalan.do/v1", + "metadata": { + "name": "acid-testcluster1" + }, + "spec": { + "teamId": "ACID", + "volume": { + "size": "5Gi", + "storageClass": "SSD" + }, + "numberOfInstances": 2, + "users": { + "zalando": [ + "superuser", + "createdb" + ] + }, + "allowedSourceRanges": [ + "127.0.0.1/32" + ], + "postgresql": { + "version": "9.6", + "parameters": { + "shared_buffers": "32MB", + "max_connections": "10", + "log_statement": "all" + } + }, + "resources": { + "requests": { + "cpu": "10m", + "memory": "50Mi" + }, + "limits": { + "cpu": "300m", + "memory": "3000Mi" + } + }, + "patroni": { + "initdb": { + "encoding": "UTF8", + "locale": "en_US.UTF-8", + "data-checksums": "true" + }, + "pg_hba": [ + "hostssl all all 0.0.0.0/0 md5", + "host all all 0.0.0.0/0 md5" + ], + "ttl": 30, + "loop_wait": 10, + "retry_timeout": 10, + "maximum_lag_on_failover": 33554432 + }, + "maintenanceWindows": [ + "01:00-06:00", + "Sat:00:00-Sat:04:00" + ] + } +}`) + +var clExp = Postgresql{ + TypeMeta: unversioned.TypeMeta{ + Kind: "Postgresql", + APIVersion: "acid.zalan.do/v1", + }, + Metadata: v1.ObjectMeta{ + Name: "acid-testcluster1", + }, + Spec: PostgresSpec{ + PostgresqlParam: PostgresqlParam{ + PgVersion: "9.6", + Parameters: map[string]string{ + "shared_buffers": "32MB", + "max_connections": "10", + "log_statement": "all", + }, + }, + Volume: Volume{ + Size: "5Gi", + StorageClass: "SSD", + }, + Patroni: Patroni{ + InitDB: map[string]string{ + "encoding": "UTF8", + "locale": "en_US.UTF-8", + "data-checksums": "true", + }, + PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"}, + TTL: 30, + LoopWait: 10, + RetryTimeout: 10, + MaximumLagOnFailover: 33554432, + }, + Resources: Resources{ + ResourceRequest: ResourceDescription{CPU: "10m", Memory: "50Mi"}, + ResourceLimits: ResourceDescription{CPU: "300m", Memory: "3000Mi"}, + }, + TeamID: "ACID", + AllowedSourceRanges: []string{"127.0.0.1/32"}, + NumberOfInstances: 2, + Users: map[string]UserFlags{"zalando": UserFlags{"superuser", "createdb"}}, + MaintenanceWindows: []MaintenanceWindow{MaintenanceWindow{ + StartWeekday: time.Monday, + StartTime: mustParseTime("01:00"), + EndTime: mustParseTime("06:00"), + EndWeekday: time.Sunday, + }, + MaintenanceWindow{ + StartWeekday: time.Saturday, + StartTime: mustParseTime("00:00"), + EndTime: mustParseTime("04:00"), + EndWeekday: time.Saturday, + }, + }, + ClusterName: "testcluster1", + }, + Status: "", + Error: nil, +} + +func mustParseTime(s string) time.Time { + v, err := time.Parse("15:04", s) + if err != nil { + panic(err) + } + + return v +} + +func TestParseTime(t *testing.T) { + for _, tt := range pTests { + aTime, weekday, weekdayProvided, err := ParseTime(tt.s) + if err != nil { + t.Errorf("ParseTime error: %v", err) + } + + if aTime != tt.time { + t.Errorf("Expected time: %v, got: %v", tt.time, aTime) + } + + if weekday != tt.weekday { + t.Errorf("Expected weekday: %v, got: %v", tt.weekday, weekday) + } + + if weekdayProvided != tt.weekdayProvided { + t.Errorf("Expected weekdayProvided: %t, got: %t", tt.weekdayProvided, weekdayProvided) + } + } +} + +func TestParseTimeError(t *testing.T) { + for _, tt := range pErr { + _, _, _, err := ParseTime(tt) + if err == nil { + t.Errorf("Error expected for '%s'", tt) + } + } +} + +func TestClusterName(t *testing.T) { + for _, tt := range clusterNames { + name, err := extractClusterName(tt.s, tt.team) + if err != nil { + t.Errorf("extractClusterName error: %v", err) + } + if name != tt.clusterName { + t.Errorf("Expected cluserName: %s, got: %s", tt.clusterName, name) + } + } +} + +func TestClusterNameError(t *testing.T) { + for _, tt := range wrongClusterNames { + _, err := extractClusterName(tt.s, tt.team) + if err == nil { + t.Errorf("Error expected for '%s'", tt) + } + } +} + +func TestUnmarshalMaintenanceWindow(t *testing.T) { + for _, tt := range maintenanceWindows { + var m MaintenanceWindow + err := m.UnmarshalJSON([]byte(tt.s)) + if err != nil { + t.Errorf("Unmarshal Error: %v", err) + } + + if !reflect.DeepEqual(m, tt.m) { + t.Errorf("Expected maintenace window: %#v, got: %#v", tt.m, m) + } + } +} + +func TestMarshalMaintenanceWindow(t *testing.T) { + for _, tt := range maintenanceWindows { + s, err := tt.m.MarshalJSON() + if err != nil { + t.Errorf("Marshal Error: %v", err) + } + + if string(s) != tt.s { + t.Errorf("Expected Marshal: %s, got: %s", tt.s, string(s)) + } + } +} + +func TestUnmarshalMaintWindowsErrs(t *testing.T) { + for _, tt := range wrongMaintenanceWindows { + var m MaintenanceWindow + err := m.UnmarshalJSON(tt) + if err == nil { + t.Errorf("Error expected for '%s'", tt) + } + } +} + +func TestPostgresqlUnmarshal(t *testing.T) { + var cluster Postgresql + err := cluster.UnmarshalJSON(cl) + if err != nil { + t.Errorf("Unmarshal Error: %v", err) + } + + if !reflect.DeepEqual(cluster, clExp) { + t.Errorf("Expected Postgresql: %#v, got %#v", clExp, cluster) + } +} + +func TestPostgresqlMarshal(t *testing.T) { + m, err := json.Marshal(clExp) + if err != nil { + t.Errorf("Marshal Error: %v", err) + } + + if bytes.Compare(m, cl) != 0 { + t.Errorf("Expected postgresql marshal: %s, got %s", string(cl), string(m)) + } +} \ No newline at end of file