1package setting
2
3import (
4	"bufio"
5	"math/rand"
6	"net/url"
7	"os"
8	"path"
9	"path/filepath"
10	"runtime"
11	"strings"
12	"testing"
13	"time"
14
15	"github.com/stretchr/testify/assert"
16	"github.com/stretchr/testify/require"
17
18	"gopkg.in/ini.v1"
19)
20
21const (
22	windows = "windows"
23)
24
25func TestLoadingSettings(t *testing.T) {
26	skipStaticRootValidation = true
27
28	t.Run("Given the default ini files", func(t *testing.T) {
29		cfg := NewCfg()
30		err := cfg.Load(CommandLineArgs{HomePath: "../../", Config: "../../conf/defaults.ini"})
31		require.Nil(t, err)
32
33		require.Equal(t, "admin", cfg.AdminUser)
34		require.Equal(t, "http://localhost:3000/", cfg.RendererCallbackUrl)
35	})
36
37	t.Run("default.ini should have no semi-colon commented entries", func(t *testing.T) {
38		file, err := os.Open("../../conf/defaults.ini")
39		if err != nil {
40			t.Errorf("failed to load defaults.ini file: %v", err)
41		}
42		defer func() {
43			err := file.Close()
44			require.Nil(t, err)
45		}()
46
47		scanner := bufio.NewScanner(file)
48		for scanner.Scan() {
49			// This only catches values commented out with ";" and will not catch those that are commented out with "#".
50			if strings.HasPrefix(scanner.Text(), ";") {
51				t.Errorf("entries in defaults.ini must not be commented or environment variables will not work: %v", scanner.Text())
52			}
53		}
54	})
55
56	t.Run("sample.ini should load successfully", func(t *testing.T) {
57		customInitPath := CustomInitPath
58		CustomInitPath = "conf/sample.ini"
59		cfg := NewCfg()
60		err := cfg.Load(CommandLineArgs{HomePath: "../../"})
61		require.Nil(t, err)
62		// Restore CustomInitPath to avoid side effects.
63		CustomInitPath = customInitPath
64	})
65
66	t.Run("Should be able to override via environment variables", func(t *testing.T) {
67		err := os.Setenv("GF_SECURITY_ADMIN_USER", "superduper")
68		require.NoError(t, err)
69
70		cfg := NewCfg()
71		err = cfg.Load(CommandLineArgs{HomePath: "../../"})
72		require.Nil(t, err)
73
74		require.Equal(t, "superduper", cfg.AdminUser)
75		require.Equal(t, filepath.Join(HomePath, "data"), cfg.DataPath)
76		require.Equal(t, filepath.Join(cfg.DataPath, "log"), cfg.LogsPath)
77	})
78
79	t.Run("Should replace password when defined in environment", func(t *testing.T) {
80		err := os.Setenv("GF_SECURITY_ADMIN_PASSWORD", "supersecret")
81		require.NoError(t, err)
82
83		cfg := NewCfg()
84		err = cfg.Load(CommandLineArgs{HomePath: "../../"})
85		require.Nil(t, err)
86
87		require.Contains(t, appliedEnvOverrides, "GF_SECURITY_ADMIN_PASSWORD=*********")
88	})
89
90	t.Run("Should replace password in URL when url environment is defined", func(t *testing.T) {
91		err := os.Setenv("GF_DATABASE_URL", "mysql://user:secret@localhost:3306/database")
92		require.NoError(t, err)
93
94		cfg := NewCfg()
95		err = cfg.Load(CommandLineArgs{HomePath: "../../"})
96		require.Nil(t, err)
97
98		require.Contains(t, appliedEnvOverrides, "GF_DATABASE_URL=mysql://user:xxxxx@localhost:3306/database")
99	})
100
101	t.Run("Should get property map from command line args array", func(t *testing.T) {
102		cfg := NewCfg()
103		props := cfg.getCommandLineProperties([]string{"cfg:test=value", "cfg:map.test=1"})
104
105		require.Equal(t, 2, len(props))
106		require.Equal(t, "value", props["test"])
107		require.Equal(t, "1", props["map.test"])
108	})
109
110	t.Run("Should be able to override via command line", func(t *testing.T) {
111		if runtime.GOOS == windows {
112			cfg := NewCfg()
113			err := cfg.Load(CommandLineArgs{
114				HomePath: "../../",
115				Args:     []string{`cfg:paths.data=c:\tmp\data`, `cfg:paths.logs=c:\tmp\logs`},
116			})
117			require.Nil(t, err)
118			require.Equal(t, `c:\tmp\data`, cfg.DataPath)
119			require.Equal(t, `c:\tmp\logs`, cfg.LogsPath)
120		} else {
121			cfg := NewCfg()
122			err := cfg.Load(CommandLineArgs{
123				HomePath: "../../",
124				Args:     []string{"cfg:paths.data=/tmp/data", "cfg:paths.logs=/tmp/logs"},
125			})
126			require.Nil(t, err)
127
128			require.Equal(t, "/tmp/data", cfg.DataPath)
129			require.Equal(t, "/tmp/logs", cfg.LogsPath)
130		}
131	})
132
133	t.Run("Should be able to override defaults via command line", func(t *testing.T) {
134		cfg := NewCfg()
135		err := cfg.Load(CommandLineArgs{
136			HomePath: "../../",
137			Args: []string{
138				"cfg:default.server.domain=test2",
139			},
140			Config: filepath.Join(HomePath, "pkg/setting/testdata/override.ini"),
141		})
142		require.Nil(t, err)
143
144		require.Equal(t, "test2", cfg.Domain)
145	})
146
147	t.Run("Defaults can be overridden in specified config file", func(t *testing.T) {
148		if runtime.GOOS == windows {
149			cfg := NewCfg()
150			err := cfg.Load(CommandLineArgs{
151				HomePath: "../../",
152				Config:   filepath.Join(HomePath, "pkg/setting/testdata/override_windows.ini"),
153				Args:     []string{`cfg:default.paths.data=c:\tmp\data`},
154			})
155			require.Nil(t, err)
156
157			require.Equal(t, `c:\tmp\override`, cfg.DataPath)
158		} else {
159			cfg := NewCfg()
160			err := cfg.Load(CommandLineArgs{
161				HomePath: "../../",
162				Config:   filepath.Join(HomePath, "pkg/setting/testdata/override.ini"),
163				Args:     []string{"cfg:default.paths.data=/tmp/data"},
164			})
165			require.Nil(t, err)
166
167			require.Equal(t, "/tmp/override", cfg.DataPath)
168		}
169	})
170
171	t.Run("Command line overrides specified config file", func(t *testing.T) {
172		if runtime.GOOS == windows {
173			cfg := NewCfg()
174			err := cfg.Load(CommandLineArgs{
175				HomePath: "../../",
176				Config:   filepath.Join(HomePath, "pkg/setting/testdata/override_windows.ini"),
177				Args:     []string{`cfg:paths.data=c:\tmp\data`},
178			})
179			require.Nil(t, err)
180
181			require.Equal(t, `c:\tmp\data`, cfg.DataPath)
182		} else {
183			cfg := NewCfg()
184			err := cfg.Load(CommandLineArgs{
185				HomePath: "../../",
186				Config:   filepath.Join(HomePath, "pkg/setting/testdata/override.ini"),
187				Args:     []string{"cfg:paths.data=/tmp/data"},
188			})
189			require.Nil(t, err)
190
191			require.Equal(t, "/tmp/data", cfg.DataPath)
192		}
193	})
194
195	t.Run("Can use environment variables in config values", func(t *testing.T) {
196		if runtime.GOOS == windows {
197			err := os.Setenv("GF_DATA_PATH", `c:\tmp\env_override`)
198			require.NoError(t, err)
199			cfg := NewCfg()
200			err = cfg.Load(CommandLineArgs{
201				HomePath: "../../",
202				Args:     []string{"cfg:paths.data=${GF_DATA_PATH}"},
203			})
204			require.Nil(t, err)
205
206			require.Equal(t, `c:\tmp\env_override`, cfg.DataPath)
207		} else {
208			err := os.Setenv("GF_DATA_PATH", "/tmp/env_override")
209			require.NoError(t, err)
210			cfg := NewCfg()
211			err = cfg.Load(CommandLineArgs{
212				HomePath: "../../",
213				Args:     []string{"cfg:paths.data=${GF_DATA_PATH}"},
214			})
215			require.Nil(t, err)
216
217			require.Equal(t, "/tmp/env_override", cfg.DataPath)
218		}
219	})
220
221	t.Run("instance_name default to hostname even if hostname env is empty", func(t *testing.T) {
222		cfg := NewCfg()
223		err := cfg.Load(CommandLineArgs{
224			HomePath: "../../",
225		})
226		require.Nil(t, err)
227
228		hostname, err := os.Hostname()
229		require.Nil(t, err)
230		require.Equal(t, hostname, InstanceName)
231	})
232
233	t.Run("Reading callback_url should add trailing slash", func(t *testing.T) {
234		cfg := NewCfg()
235		err := cfg.Load(CommandLineArgs{
236			HomePath: "../../",
237			Args:     []string{"cfg:rendering.callback_url=http://myserver/renderer"},
238		})
239		require.Nil(t, err)
240
241		require.Equal(t, "http://myserver/renderer/", cfg.RendererCallbackUrl)
242	})
243
244	t.Run("Only sync_ttl should return the value sync_ttl", func(t *testing.T) {
245		cfg := NewCfg()
246		err := cfg.Load(CommandLineArgs{
247			HomePath: "../../",
248			Args:     []string{"cfg:auth.proxy.sync_ttl=2"},
249		})
250		require.Nil(t, err)
251
252		require.Equal(t, 2, cfg.AuthProxySyncTTL)
253	})
254
255	t.Run("Only ldap_sync_ttl should return the value ldap_sync_ttl", func(t *testing.T) {
256		cfg := NewCfg()
257		err := cfg.Load(CommandLineArgs{
258			HomePath: "../../",
259			Args:     []string{"cfg:auth.proxy.ldap_sync_ttl=5"},
260		})
261		require.Nil(t, err)
262
263		require.Equal(t, 5, cfg.AuthProxySyncTTL)
264	})
265
266	t.Run("ldap_sync should override ldap_sync_ttl that is default value", func(t *testing.T) {
267		cfg := NewCfg()
268		err := cfg.Load(CommandLineArgs{
269			HomePath: "../../",
270			Args:     []string{"cfg:auth.proxy.sync_ttl=5"},
271		})
272		require.Nil(t, err)
273
274		require.Equal(t, 5, cfg.AuthProxySyncTTL)
275	})
276
277	t.Run("ldap_sync should not override ldap_sync_ttl that is different from default value", func(t *testing.T) {
278		cfg := NewCfg()
279		err := cfg.Load(CommandLineArgs{
280			HomePath: "../../",
281			Args:     []string{"cfg:auth.proxy.ldap_sync_ttl=12", "cfg:auth.proxy.sync_ttl=5"},
282		})
283		require.Nil(t, err)
284
285		require.Equal(t, 12, cfg.AuthProxySyncTTL)
286	})
287
288	t.Run("Test reading string values from .ini file", func(t *testing.T) {
289		iniFile, err := ini.Load(path.Join(HomePath, "pkg/setting/testdata/invalid.ini"))
290		require.Nil(t, err)
291
292		t.Run("If key is found - should return value from ini file", func(t *testing.T) {
293			value := valueAsString(iniFile.Section("server"), "alt_url", "")
294			require.Equal(t, "https://grafana.com/", value)
295		})
296
297		t.Run("If key is not found - should return default value", func(t *testing.T) {
298			value := valueAsString(iniFile.Section("server"), "extra_url", "default_url_val")
299			require.Equal(t, "default_url_val", value)
300		})
301	})
302}
303
304func TestParseAppURLAndSubURL(t *testing.T) {
305	testCases := []struct {
306		rootURL           string
307		expectedAppURL    string
308		expectedAppSubURL string
309	}{
310		{rootURL: "http://localhost:3000/", expectedAppURL: "http://localhost:3000/"},
311		{rootURL: "http://localhost:3000", expectedAppURL: "http://localhost:3000/"},
312		{rootURL: "http://localhost:3000/grafana", expectedAppURL: "http://localhost:3000/grafana/", expectedAppSubURL: "/grafana"},
313		{rootURL: "http://localhost:3000/grafana/", expectedAppURL: "http://localhost:3000/grafana/", expectedAppSubURL: "/grafana"},
314	}
315
316	for _, tc := range testCases {
317		f := ini.Empty()
318		cfg := NewCfg()
319		s, err := f.NewSection("server")
320		require.NoError(t, err)
321		_, err = s.NewKey("root_url", tc.rootURL)
322		require.NoError(t, err)
323		appURL, appSubURL, err := cfg.parseAppUrlAndSubUrl(s)
324		require.NoError(t, err)
325		require.Equal(t, tc.expectedAppURL, appURL)
326		require.Equal(t, tc.expectedAppSubURL, appSubURL)
327	}
328}
329
330func TestAuthDurationSettings(t *testing.T) {
331	const maxInactiveDaysTest = 240 * time.Hour
332
333	f := ini.Empty()
334	cfg := NewCfg()
335	sec, err := f.NewSection("auth")
336	require.NoError(t, err)
337	_, err = sec.NewKey("login_maximum_inactive_lifetime_days", "10")
338	require.NoError(t, err)
339	_, err = sec.NewKey("login_maximum_inactive_lifetime_duration", "")
340	require.NoError(t, err)
341	err = readAuthSettings(f, cfg)
342	require.NoError(t, err)
343	require.Equal(t, maxInactiveDaysTest, cfg.LoginMaxInactiveLifetime)
344
345	f = ini.Empty()
346	sec, err = f.NewSection("auth")
347	require.NoError(t, err)
348	_, err = sec.NewKey("login_maximum_inactive_lifetime_duration", "824h")
349	require.NoError(t, err)
350	maxInactiveDurationTest, err := time.ParseDuration("824h")
351	require.NoError(t, err)
352	err = readAuthSettings(f, cfg)
353	require.NoError(t, err)
354	require.Equal(t, maxInactiveDurationTest, cfg.LoginMaxInactiveLifetime)
355
356	f = ini.Empty()
357	sec, err = f.NewSection("auth")
358	require.NoError(t, err)
359	_, err = sec.NewKey("login_maximum_lifetime_days", "24")
360	require.NoError(t, err)
361	_, err = sec.NewKey("login_maximum_lifetime_duration", "")
362	require.NoError(t, err)
363	maxLifetimeDaysTest, err := time.ParseDuration("576h")
364	require.NoError(t, err)
365	err = readAuthSettings(f, cfg)
366	require.NoError(t, err)
367	require.Equal(t, maxLifetimeDaysTest, cfg.LoginMaxLifetime)
368
369	f = ini.Empty()
370	sec, err = f.NewSection("auth")
371	require.NoError(t, err)
372	_, err = sec.NewKey("login_maximum_lifetime_duration", "824h")
373	require.NoError(t, err)
374	maxLifetimeDurationTest, err := time.ParseDuration("824h")
375	require.NoError(t, err)
376	err = readAuthSettings(f, cfg)
377	require.NoError(t, err)
378	require.Equal(t, maxLifetimeDurationTest, cfg.LoginMaxLifetime)
379
380	f = ini.Empty()
381	sec, err = f.NewSection("auth")
382	require.NoError(t, err)
383	_, err = sec.NewKey("login_maximum_lifetime_days", "")
384	require.NoError(t, err)
385	_, err = sec.NewKey("login_maximum_lifetime_duration", "")
386	require.NoError(t, err)
387	maxLifetimeDurationTest, err = time.ParseDuration("720h")
388	require.NoError(t, err)
389	err = readAuthSettings(f, cfg)
390	require.NoError(t, err)
391	require.Equal(t, maxLifetimeDurationTest, cfg.LoginMaxLifetime)
392}
393
394func TestGetCDNPath(t *testing.T) {
395	var err error
396	cfg := NewCfg()
397	cfg.BuildVersion = "v7.5.0-11124"
398	cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com")
399	require.NoError(t, err)
400
401	require.Equal(t, "http://cdn.grafana.com/grafana-oss/v7.5.0-11124/", cfg.GetContentDeliveryURL("grafana-oss"))
402	require.Equal(t, "http://cdn.grafana.com/grafana/v7.5.0-11124/", cfg.GetContentDeliveryURL("grafana"))
403}
404
405func TestGetContentDeliveryURLWhenNoCDNRootURLIsSet(t *testing.T) {
406	cfg := NewCfg()
407	require.Equal(t, "", cfg.GetContentDeliveryURL("grafana-oss"))
408}
409
410func TestGetCDNPathWithPreReleaseVersionAndSubPath(t *testing.T) {
411	var err error
412	cfg := NewCfg()
413	cfg.BuildVersion = "v7.5.0-11124pre"
414	cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com/sub")
415	require.NoError(t, err)
416	require.Equal(t, "http://cdn.grafana.com/sub/grafana-oss/v7.5.0-11124pre/", cfg.GetContentDeliveryURL("grafana-oss"))
417	require.Equal(t, "http://cdn.grafana.com/sub/grafana/v7.5.0-11124pre/", cfg.GetContentDeliveryURL("grafana"))
418}
419
420// Adding a case for this in case we switch to proper semver version strings
421func TestGetCDNPathWithAlphaVersion(t *testing.T) {
422	var err error
423	cfg := NewCfg()
424	cfg.BuildVersion = "v7.5.0-alpha.11124"
425	cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com")
426	require.NoError(t, err)
427	require.Equal(t, "http://cdn.grafana.com/grafana-oss/v7.5.0-alpha.11124/", cfg.GetContentDeliveryURL("grafana-oss"))
428	require.Equal(t, "http://cdn.grafana.com/grafana/v7.5.0-alpha.11124/", cfg.GetContentDeliveryURL("grafana"))
429}
430
431func TestAlertingEnabled(t *testing.T) {
432	anyBoolean := func() bool {
433		return rand.Int63()%2 == 0
434	}
435
436	testCases := []struct {
437		desc                   string
438		unifiedAlertingEnabled string
439		legacyAlertingEnabled  string
440		featureToggleSet       bool
441		isEnterprise           bool
442		verifyCfg              func(*testing.T, Cfg, *ini.File)
443	}{
444		{
445			desc:                   "when legacy alerting is enabled and unified is disabled",
446			legacyAlertingEnabled:  "true",
447			unifiedAlertingEnabled: "false",
448			isEnterprise:           anyBoolean(),
449			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
450				err := readAlertingSettings(f)
451				require.NoError(t, err)
452				err = cfg.readFeatureToggles(f)
453				require.NoError(t, err)
454				err = cfg.ReadUnifiedAlertingSettings(f)
455				require.NoError(t, err)
456				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
457				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
458				assert.NotNil(t, AlertingEnabled)
459				assert.Equal(t, *AlertingEnabled, true)
460			},
461		},
462		{
463			desc:                   "when legacy alerting is disabled and unified is enabled",
464			legacyAlertingEnabled:  "false",
465			unifiedAlertingEnabled: "true",
466			isEnterprise:           anyBoolean(),
467			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
468				err := readAlertingSettings(f)
469				require.NoError(t, err)
470				err = cfg.readFeatureToggles(f)
471				require.NoError(t, err)
472				err = cfg.ReadUnifiedAlertingSettings(f)
473				require.NoError(t, err)
474				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
475				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, true)
476				assert.NotNil(t, AlertingEnabled)
477				assert.Equal(t, *AlertingEnabled, false)
478			},
479		},
480		{
481			desc:                   "when both alerting are enabled",
482			legacyAlertingEnabled:  "true",
483			unifiedAlertingEnabled: "true",
484			isEnterprise:           anyBoolean(),
485			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
486				err := readAlertingSettings(f)
487				require.NoError(t, err)
488				err = cfg.readFeatureToggles(f)
489				require.NoError(t, err)
490				err = cfg.ReadUnifiedAlertingSettings(f)
491				require.Error(t, err)
492			},
493		},
494		{
495			desc:                   "when legacy alerting is invalid (or not defined) and unified is disabled",
496			legacyAlertingEnabled:  "",
497			unifiedAlertingEnabled: "false",
498			isEnterprise:           anyBoolean(),
499			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
500				err := readAlertingSettings(f)
501				require.NoError(t, err)
502				err = cfg.readFeatureToggles(f)
503				require.NoError(t, err)
504				err = cfg.ReadUnifiedAlertingSettings(f)
505				require.NoError(t, err)
506				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
507				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
508				assert.NotNil(t, AlertingEnabled)
509				assert.Equal(t, *AlertingEnabled, true)
510			},
511		},
512		{
513			desc:                   "when legacy alerting is invalid (or not defined) and unified is enabled",
514			legacyAlertingEnabled:  "",
515			unifiedAlertingEnabled: "true",
516			isEnterprise:           anyBoolean(),
517			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
518				err := readAlertingSettings(f)
519				require.NoError(t, err)
520				err = cfg.readFeatureToggles(f)
521				require.NoError(t, err)
522				err = cfg.ReadUnifiedAlertingSettings(f)
523				require.NoError(t, err)
524				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
525				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, true)
526				assert.NotNil(t, AlertingEnabled)
527				assert.Equal(t, *AlertingEnabled, false)
528			},
529		},
530		{
531			desc:                   "when legacy alerting is enabled and unified is invalid (or not defined) [OSS]",
532			legacyAlertingEnabled:  "true",
533			unifiedAlertingEnabled: "",
534			isEnterprise:           false,
535			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
536				err := readAlertingSettings(f)
537				require.NoError(t, err)
538				err = cfg.readFeatureToggles(f)
539				require.NoError(t, err)
540				err = cfg.ReadUnifiedAlertingSettings(f)
541				require.NoError(t, err)
542				assert.Nil(t, cfg.UnifiedAlerting.Enabled)
543				assert.NotNil(t, AlertingEnabled)
544				assert.Equal(t, *AlertingEnabled, true)
545			},
546		},
547		{
548			desc:                   "when legacy alerting is enabled and unified is invalid (or not defined) [Enterprise]",
549			legacyAlertingEnabled:  "true",
550			unifiedAlertingEnabled: "",
551			isEnterprise:           true,
552			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
553				err := readAlertingSettings(f)
554				require.NoError(t, err)
555				err = cfg.readFeatureToggles(f)
556				require.NoError(t, err)
557				err = cfg.ReadUnifiedAlertingSettings(f)
558				require.NoError(t, err)
559				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
560				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
561				assert.NotNil(t, AlertingEnabled)
562				assert.Equal(t, *AlertingEnabled, true)
563			},
564		},
565		{
566			desc:                   "when legacy alerting is disabled and unified is invalid (or not defined) [OSS]",
567			legacyAlertingEnabled:  "false",
568			unifiedAlertingEnabled: "invalid",
569			isEnterprise:           false,
570			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
571				err := readAlertingSettings(f)
572				require.NoError(t, err)
573				err = cfg.readFeatureToggles(f)
574				require.NoError(t, err)
575				err = cfg.ReadUnifiedAlertingSettings(f)
576				require.NoError(t, err)
577				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
578				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, true)
579				assert.NotNil(t, AlertingEnabled)
580				assert.Equal(t, *AlertingEnabled, false)
581			},
582		},
583		{
584			desc:                   "when legacy alerting is disabled and unified is invalid (or not defined) [Enterprise]",
585			legacyAlertingEnabled:  "false",
586			unifiedAlertingEnabled: "invalid",
587			isEnterprise:           true,
588			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
589				err := readAlertingSettings(f)
590				require.NoError(t, err)
591				err = cfg.readFeatureToggles(f)
592				require.NoError(t, err)
593				err = cfg.ReadUnifiedAlertingSettings(f)
594				require.NoError(t, err)
595				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
596				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
597				assert.NotNil(t, AlertingEnabled)
598				assert.Equal(t, *AlertingEnabled, false)
599			},
600		},
601		{
602			desc:                   "when both are invalid (or not defined) [OSS]",
603			legacyAlertingEnabled:  "invalid",
604			unifiedAlertingEnabled: "invalid",
605			isEnterprise:           false,
606			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
607				err := readAlertingSettings(f)
608				require.NoError(t, err)
609				err = cfg.readFeatureToggles(f)
610				require.NoError(t, err)
611				err = cfg.ReadUnifiedAlertingSettings(f)
612				require.NoError(t, err)
613				assert.Nil(t, cfg.UnifiedAlerting.Enabled)
614				assert.Nil(t, AlertingEnabled)
615			},
616		},
617		{
618			desc:                   "when both are invalid (or not defined) [Enterprise]",
619			legacyAlertingEnabled:  "invalid",
620			unifiedAlertingEnabled: "invalid",
621			isEnterprise:           true,
622			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
623				err := readAlertingSettings(f)
624				require.NoError(t, err)
625				err = cfg.readFeatureToggles(f)
626				require.NoError(t, err)
627				err = cfg.ReadUnifiedAlertingSettings(f)
628				require.NoError(t, err)
629				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
630				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
631				assert.NotNil(t, AlertingEnabled)
632				assert.Equal(t, *AlertingEnabled, true)
633			},
634		},
635		{
636			desc:                   "when both are false",
637			legacyAlertingEnabled:  "false",
638			unifiedAlertingEnabled: "false",
639			isEnterprise:           anyBoolean(),
640			verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
641				err := readAlertingSettings(f)
642				require.NoError(t, err)
643				err = cfg.readFeatureToggles(f)
644				require.NoError(t, err)
645				err = cfg.ReadUnifiedAlertingSettings(f)
646				require.NoError(t, err)
647				assert.NotNil(t, cfg.UnifiedAlerting.Enabled)
648				assert.Equal(t, *cfg.UnifiedAlerting.Enabled, false)
649				assert.NotNil(t, AlertingEnabled)
650				assert.Equal(t, *AlertingEnabled, false)
651			},
652		},
653	}
654
655	var isEnterpriseOld = IsEnterprise
656	t.Cleanup(func() {
657		IsEnterprise = isEnterpriseOld
658	})
659
660	for _, tc := range testCases {
661		t.Run(tc.desc, func(t *testing.T) {
662			IsEnterprise = tc.isEnterprise
663			t.Cleanup(func() {
664				AlertingEnabled = nil
665			})
666
667			f := ini.Empty()
668			cfg := NewCfg()
669			unifiedAlertingSec, err := f.NewSection("unified_alerting")
670			require.NoError(t, err)
671			_, err = unifiedAlertingSec.NewKey("enabled", tc.unifiedAlertingEnabled)
672			require.NoError(t, err)
673
674			alertingSec, err := f.NewSection("alerting")
675			require.NoError(t, err)
676			_, err = alertingSec.NewKey("enabled", tc.legacyAlertingEnabled)
677			require.NoError(t, err)
678
679			tc.verifyCfg(t, *cfg, f)
680		})
681	}
682}
683