package notifier import ( "errors" "io/ioutil" "path/filepath" "testing" api "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPersistTemplates(t *testing.T) { tc := []struct { name string templates map[string]string existingTemplates map[string]string expectedPaths []string expectedError error expectedChange bool }{ { name: "With valid templates file names, it persists successfully", templates: map[string]string{"email.template": "a perfectly fine template"}, expectedChange: true, expectedError: nil, expectedPaths: []string{"email.template"}, }, { name: "With a invalid filename, it fails", templates: map[string]string{"adirectory/email.template": "a perfectly fine template"}, expectedError: errors.New("template file name 'adirectory/email.template' is not valid"), }, { name: "with a template that has the same name but different content to an existing one", existingTemplates: map[string]string{"email.template": "a perfectly fine template"}, templates: map[string]string{"email.template": "a completely different content"}, expectedChange: true, expectedError: nil, expectedPaths: []string{"email.template"}, }, { name: "with a template that has the same name and the same content as an existing one", existingTemplates: map[string]string{"email.template": "a perfectly fine template"}, templates: map[string]string{"email.template": "a perfectly fine template"}, expectedChange: false, expectedError: nil, expectedPaths: []string{"email.template"}, }, { name: "with two new template files, it changes the template tree", existingTemplates: map[string]string{"email.template": "a perfectly fine template"}, templates: map[string]string{"slack.template": "a perfectly fine template", "webhook.template": "a webhook template"}, expectedChange: true, expectedError: nil, expectedPaths: []string{"slack.template", "webhook.template"}, }, { name: "when we remove a template file from the list, it changes the template tree", existingTemplates: map[string]string{"slack.template": "a perfectly fine template", "webhook.template": "a webhook template"}, templates: map[string]string{"slack.template": "a perfectly fine template"}, expectedChange: true, expectedError: nil, expectedPaths: []string{"slack.template"}, }, } for _, tt := range tc { t.Run(tt.name, func(t *testing.T) { dir := t.TempDir() // Write "existing files" for name, content := range tt.existingTemplates { err := ioutil.WriteFile(filepath.Join(dir, name), []byte(content), 0644) require.NoError(t, err) } c := &api.PostableUserConfig{TemplateFiles: tt.templates} paths, changed, persistErr := PersistTemplates(c, dir) files := map[string]string{} readFiles, err := ioutil.ReadDir(dir) require.NoError(t, err) for _, f := range readFiles { if f.IsDir() || f.Name() == "" { continue } // Safe to disable, this is a test. // nolint:gosec content, err := ioutil.ReadFile(filepath.Join(dir, f.Name())) // nolint:gosec require.NoError(t, err) files[f.Name()] = string(content) } // Given we use a temporary directory in tests, we need to prepend the expected paths with it. for i, p := range tt.expectedPaths { tt.expectedPaths[i] = filepath.Join(dir, p) } require.Equal(t, tt.expectedError, persistErr) require.ElementsMatch(t, tt.expectedPaths, paths) require.Equal(t, tt.expectedChange, changed) if tt.expectedError == nil { require.Equal(t, tt.templates, files) } }) } } func TestLoad(t *testing.T) { tc := []struct { name string rawConfig string expectedTemplates map[string]string expectedError error }{ { name: "with a valid config and template", rawConfig: ` { "alertmanager_config": { "global": { "smtp_from": "noreply@grafana.net" }, "route": { "receiver": "email" }, "receivers": [ { "name": "email" } ] }, "template_files": { "email.template": "something with a pretty good content" } } `, expectedTemplates: map[string]string{"email.template": "something with a pretty good content"}, }, { name: "with an empty configuration, it is not valid.", rawConfig: "{}", expectedError: errors.New("unable to parse Alertmanager configuration: no route provided in config"), }, } for _, tt := range tc { t.Run(tt.name, func(t *testing.T) { c, err := Load([]byte(tt.rawConfig)) if tt.expectedError != nil { assert.Nil(t, c) assert.Equal(t, tt.expectedError.Error(), err.Error()) } else { require.NoError(t, err) assert.NotNil(t, c.TemplateFiles) assert.Equal(t, tt.expectedTemplates, c.TemplateFiles) } }) } }