Skip to content

Commit 98dec3d

Browse files
committed
Extend database#Config test cases and test with config#FromYAMLFile()
1 parent d8c089d commit 98dec3d

File tree

1 file changed

+267
-69
lines changed

1 file changed

+267
-69
lines changed

database/config_test.go

Lines changed: 267 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,102 +3,300 @@ package database
33
import (
44
"github.com/creasty/defaults"
55
"github.com/icinga/icinga-go-library/config"
6+
"github.com/icinga/icinga-go-library/testutils"
67
"github.com/stretchr/testify/require"
8+
"os"
79
"testing"
810
)
911

12+
// minimalYaml is a constant string representing a minimal valid YAML configuration for
13+
// connecting to a PostgreSQL database. PostgreSQL is explicitly chosen here to
14+
// test whether the default type (which is MySQL) is correctly overridden.
15+
const minimalYaml = `
16+
type: pgsql
17+
host: localhost
18+
user: icinga
19+
database: icingadb
20+
password: secret`
21+
22+
// minimalEnv returns a map of environment variables representing a minimal valid configuration for
23+
// connecting to a PostgreSQL database. PostgreSQL is explicitly chosen here to
24+
// test whether the default type (which is MySQL) is correctly overridden.
25+
func minimalEnv() map[string]string {
26+
return map[string]string{
27+
"TYPE": "pgsql",
28+
"HOST": "localhost",
29+
"USER": "icinga",
30+
"DATABASE": "icingadb",
31+
"PASSWORD": "secret",
32+
}
33+
}
34+
35+
// withMinimalEnv takes a map of environment variables and merges it with the
36+
// minimal environment configuration returned from minimalEnv,
37+
// overriding any existing keys with the provided values.
38+
// It returns the resulting map.
39+
func withMinimalEnv(v map[string]string) map[string]string {
40+
env := minimalEnv()
41+
42+
for key, value := range v {
43+
env[key] = value
44+
}
45+
46+
return env
47+
}
48+
1049
func TestConfig(t *testing.T) {
1150
var defaultOptions Options
1251
require.NoError(t, defaults.Set(&defaultOptions), "setting default options")
1352

14-
subtests := []struct {
15-
name string
16-
opts config.EnvOptions
17-
expected Config
18-
error bool
19-
}{
53+
configTests := []testutils.TestCase[Config, testutils.ConfigTestData]{
54+
{
55+
Name: "Unknown database type",
56+
Data: testutils.ConfigTestData{
57+
Yaml: `type: invalid`,
58+
Env: map[string]string{"TYPE": "invalid"},
59+
},
60+
Error: testutils.ErrorContains(`unknown database type "invalid"`),
61+
},
62+
{
63+
Name: "Database host missing",
64+
Data: testutils.ConfigTestData{
65+
Yaml: `type: pgsql`,
66+
Env: map[string]string{"TYPE": "pgsql"},
67+
},
68+
Error: testutils.ErrorContains("database host missing"),
69+
},
2070
{
21-
name: "empty-missing-fields",
22-
opts: config.EnvOptions{},
23-
error: true,
71+
Name: "Database user missing",
72+
Data: testutils.ConfigTestData{
73+
Yaml: `
74+
type: pgsql
75+
host: localhost`,
76+
Env: map[string]string{
77+
"TYPE": "pgsql",
78+
"HOST": "localhost",
79+
},
80+
},
81+
Error: testutils.ErrorContains("database user missing"),
82+
},
83+
{
84+
Name: "Database name missing",
85+
Data: testutils.ConfigTestData{
86+
Yaml: `
87+
type: pgsql
88+
host: localhost
89+
user: icinga`,
90+
Env: map[string]string{
91+
"TYPE": "pgsql",
92+
"HOST": "localhost",
93+
"USER": "icinga",
94+
},
95+
},
96+
Error: testutils.ErrorContains("database name missing"),
2497
},
2598
{
26-
name: "unknown-db-type",
27-
opts: config.EnvOptions{Environment: map[string]string{"TYPE": "☃"}},
28-
error: true,
99+
Name: "Minimal config",
100+
Data: testutils.ConfigTestData{
101+
Yaml: minimalYaml,
102+
Env: minimalEnv(),
103+
},
104+
Expected: Config{
105+
Type: "pgsql",
106+
Host: "localhost",
107+
User: "icinga",
108+
Database: "icingadb",
109+
Password: "secret",
110+
Options: defaultOptions,
111+
},
29112
},
30113
{
31-
name: "minimal-config",
32-
opts: config.EnvOptions{Environment: map[string]string{
33-
"HOST": "db.example.com",
34-
"USER": "user",
35-
"DATABASE": "db",
36-
}},
37-
expected: Config{
38-
Type: "mysql",
39-
Host: "db.example.com",
40-
Database: "db",
41-
User: "user",
114+
Name: "Retain defaults",
115+
Data: testutils.ConfigTestData{
116+
Yaml: `
117+
host: localhost
118+
user: icinga
119+
database: icinga`,
120+
Env: map[string]string{
121+
"HOST": "localhost",
122+
"USER": "icinga",
123+
"DATABASE": "icinga",
124+
},
125+
},
126+
Expected: Config{
127+
Type: "mysql", // Default
128+
Host: "localhost",
129+
User: "icinga",
130+
Database: "icinga",
42131
Options: defaultOptions,
43132
},
44133
},
45134
{
46-
name: "tls",
47-
opts: config.EnvOptions{Environment: map[string]string{
48-
"HOST": "db.example.com",
49-
"USER": "user",
50-
"DATABASE": "db",
51-
"TLS": "true",
52-
"CERT": "/var/empty/db.crt",
53-
"CA": "/var/empty/ca.crt",
54-
}},
55-
expected: Config{
56-
Type: "mysql",
57-
Host: "db.example.com",
58-
Database: "db",
59-
User: "user",
135+
Name: "TLS",
136+
Data: testutils.ConfigTestData{
137+
Yaml: minimalYaml + `
138+
tls: true
139+
cert: cert.pem
140+
key: key.pem
141+
ca: ca.pem`,
142+
Env: withMinimalEnv(map[string]string{
143+
"TLS": "1",
144+
"CERT": "cert.pem",
145+
"KEY": "key.pem",
146+
"CA": "ca.pem",
147+
}),
148+
},
149+
Expected: Config{
150+
Type: "pgsql",
151+
Host: "localhost",
152+
User: "icinga",
153+
Database: "icingadb",
154+
Password: "secret",
155+
Options: defaultOptions,
60156
TlsOptions: config.TLS{
61157
Enable: true,
62-
Cert: "/var/empty/db.crt",
63-
Ca: "/var/empty/ca.crt",
158+
Cert: "cert.pem",
159+
Key: "key.pem",
160+
Ca: "ca.pem",
64161
},
65-
Options: defaultOptions,
66162
},
67163
},
68164
{
69-
name: "options",
70-
opts: config.EnvOptions{Environment: map[string]string{
71-
"HOST": "db.example.com",
72-
"USER": "user",
73-
"DATABASE": "db",
74-
"OPTIONS_MAX_CONNECTIONS": "1",
75-
"OPTIONS_MAX_ROWS_PER_TRANSACTION": "65535",
76-
}},
77-
expected: Config{
78-
Type: "mysql",
79-
Host: "db.example.com",
80-
Database: "db",
81-
User: "user",
165+
Name: "max_connections cannot be 0",
166+
Data: testutils.ConfigTestData{
167+
Yaml: minimalYaml + `
168+
options:
169+
max_connections: 0`,
170+
Env: withMinimalEnv(map[string]string{"OPTIONS_MAX_CONNECTIONS": "0"}),
171+
},
172+
Error: testutils.ErrorContains("max_connections cannot be 0"),
173+
},
174+
{
175+
Name: "max_connections_per_table must be at least 1",
176+
Data: testutils.ConfigTestData{
177+
Yaml: minimalYaml + `
178+
options:
179+
max_connections_per_table: 0`,
180+
Env: withMinimalEnv(map[string]string{"OPTIONS_MAX_CONNECTIONS_PER_TABLE": "0"}),
181+
},
182+
Error: testutils.ErrorContains("max_connections_per_table must be at least 1"),
183+
},
184+
{
185+
Name: "max_placeholders_per_statement must be at least 1",
186+
Data: testutils.ConfigTestData{
187+
Yaml: minimalYaml + `
188+
options:
189+
max_placeholders_per_statement: 0`,
190+
Env: withMinimalEnv(map[string]string{"OPTIONS_MAX_PLACEHOLDERS_PER_STATEMENT": "0"}),
191+
},
192+
Error: testutils.ErrorContains("max_placeholders_per_statement must be at least 1"),
193+
},
194+
{
195+
Name: "max_rows_per_transaction must be at least 1",
196+
Data: testutils.ConfigTestData{
197+
Yaml: minimalYaml + `
198+
options:
199+
max_rows_per_transaction: 0`,
200+
Env: withMinimalEnv(map[string]string{"OPTIONS_MAX_ROWS_PER_TRANSACTION": "0"}),
201+
},
202+
Error: testutils.ErrorContains("max_rows_per_transaction must be at least 1"),
203+
},
204+
{
205+
Name: "wsrep_sync_wait can only be set to a number between 0 and 15",
206+
Data: testutils.ConfigTestData{
207+
Yaml: minimalYaml + `
208+
options:
209+
wsrep_sync_wait: 16`,
210+
Env: withMinimalEnv(map[string]string{"OPTIONS_WSREP_SYNC_WAIT": "16"}),
211+
},
212+
Error: testutils.ErrorContains("wsrep_sync_wait can only be set to a number between 0 and 15"),
213+
},
214+
{
215+
Name: "Options retain defaults",
216+
Data: testutils.ConfigTestData{
217+
Yaml: minimalYaml + `
218+
options:
219+
max_connections: 8
220+
max_connections_per_table: 4`,
221+
Env: withMinimalEnv(map[string]string{
222+
"OPTIONS_MAX_CONNECTIONS": "8",
223+
"OPTIONS_MAX_CONNECTIONS_PER_TABLE": "4",
224+
}),
225+
},
226+
Expected: Config{
227+
Type: "pgsql",
228+
Host: "localhost",
229+
User: "icinga",
230+
Database: "icingadb",
231+
Password: "secret",
232+
Options: Options{
233+
MaxConnections: 8,
234+
MaxConnectionsPerTable: 4,
235+
MaxPlaceholdersPerStatement: defaultOptions.MaxPlaceholdersPerStatement,
236+
MaxRowsPerTransaction: defaultOptions.MaxRowsPerTransaction,
237+
WsrepSyncWait: defaultOptions.WsrepSyncWait,
238+
},
239+
},
240+
},
241+
{
242+
Name: "Options",
243+
Data: testutils.ConfigTestData{
244+
Yaml: minimalYaml + `
245+
options:
246+
max_connections: 8
247+
max_connections_per_table: 4
248+
max_placeholders_per_statement: 4096
249+
max_rows_per_transaction: 2048
250+
wsrep_sync_wait: 15`,
251+
Env: withMinimalEnv(map[string]string{
252+
"OPTIONS_MAX_CONNECTIONS": "8",
253+
"OPTIONS_MAX_CONNECTIONS_PER_TABLE": "4",
254+
"OPTIONS_MAX_PLACEHOLDERS_PER_STATEMENT": "4096",
255+
"OPTIONS_MAX_ROWS_PER_TRANSACTION": "2048",
256+
"OPTIONS_WSREP_SYNC_WAIT": "15",
257+
}),
258+
},
259+
Expected: Config{
260+
Type: "pgsql",
261+
Host: "localhost",
262+
User: "icinga",
263+
Database: "icingadb",
264+
Password: "secret",
82265
Options: Options{
83-
MaxConnections: 1,
84-
MaxConnectionsPerTable: 8,
85-
MaxPlaceholdersPerStatement: 8192,
86-
MaxRowsPerTransaction: 65535,
87-
WsrepSyncWait: 7,
266+
MaxConnections: 8,
267+
MaxConnectionsPerTable: 4,
268+
MaxPlaceholdersPerStatement: 4096,
269+
MaxRowsPerTransaction: 2048,
270+
WsrepSyncWait: 15,
88271
},
89272
},
90273
},
91274
}
92275

93-
for _, test := range subtests {
94-
t.Run(test.name, func(t *testing.T) {
95-
var out Config
96-
if err := config.FromEnv(&out, test.opts); test.error {
97-
require.Error(t, err)
98-
} else {
99-
require.NoError(t, err)
100-
require.Equal(t, test.expected, out)
101-
}
102-
})
103-
}
276+
t.Run("FromEnv", func(t *testing.T) {
277+
for _, tc := range configTests {
278+
t.Run(tc.Name, tc.F(func(data testutils.ConfigTestData) (Config, error) {
279+
var actual Config
280+
281+
err := config.FromEnv(&actual, config.EnvOptions{Environment: data.Env})
282+
283+
return actual, err
284+
}))
285+
}
286+
})
287+
288+
t.Run("FromYAMLFile", func(t *testing.T) {
289+
for _, tc := range configTests {
290+
t.Run(tc.Name+"/FromYAMLFile", tc.F(func(data testutils.ConfigTestData) (Config, error) {
291+
var actual Config
292+
293+
var err error
294+
testutils.WithYAMLFile(t, data.Yaml, func(file *os.File) {
295+
err = config.FromYAMLFile(file.Name(), &actual)
296+
})
297+
298+
return actual, err
299+
}))
300+
}
301+
})
104302
}

0 commit comments

Comments
 (0)