diff --git a/pkg/services/ngalert/store/alertmanager_test.go b/pkg/services/ngalert/store/alertmanager_test.go index 20e199463af..1b0d9c93cef 100644 --- a/pkg/services/ngalert/store/alertmanager_test.go +++ b/pkg/services/ngalert/store/alertmanager_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/md5" "fmt" + "sort" "testing" "github.com/stretchr/testify/require" @@ -13,6 +14,115 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/models" ) +func TestIntegrationAlertManagerStore(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + sqlStore := db.InitTestDB(t) + store := &DBstore{ + SQLStore: sqlStore, + Logger: log.NewNopLogger(), + } + + t.Run("GetLatestAlertmanagerConfiguration for org that doesn't exist returns error", func(t *testing.T) { + _, _ = setupConfig(t, "my-config", store) + req := &models.GetLatestAlertmanagerConfigurationQuery{ + OrgID: 1234, + } + + err := store.GetLatestAlertmanagerConfiguration(context.Background(), req) + + require.ErrorIs(t, err, ErrNoAlertmanagerConfiguration) + require.Nil(t, req.Result) + }) + + t.Run("GetLatestAlertmanagerConfiguration return the right config", func(t *testing.T) { + _, configMD5 := setupConfig(t, "my-config", store) + req := &models.GetLatestAlertmanagerConfigurationQuery{ + OrgID: 1, + } + + err := store.GetLatestAlertmanagerConfiguration(context.Background(), req) + + require.NoError(t, err) + require.NotNil(t, req.Result) + require.Equal(t, "my-config", req.Result.AlertmanagerConfiguration) + require.Equal(t, configMD5, req.Result.ConfigurationHash) + }) + + t.Run("GetLatestAlertmanagerConfiguration after saving multiple times should return the latest config", func(t *testing.T) { + _, _ = setupConfig(t, "my-config1", store) + _, _ = setupConfig(t, "my-config2", store) + _, configMD5 := setupConfig(t, "my-config3", store) + req := &models.GetLatestAlertmanagerConfigurationQuery{ + OrgID: 1, + } + + err := store.GetLatestAlertmanagerConfiguration(context.Background(), req) + + require.NoError(t, err) + require.NotNil(t, req.Result) + require.Equal(t, "my-config3", req.Result.AlertmanagerConfiguration) + require.Equal(t, configMD5, req.Result.ConfigurationHash) + }) + + t.Run("GetAllLatestAlertmanagerConfiguration gets latest config for all orgs", func(t *testing.T) { + _, _ = setupConfigInOrg(t, "my-config1", 1, store) + _, _ = setupConfigInOrg(t, "my-config2", 1, store) + _, _ = setupConfigInOrg(t, "my-config3", 1, store) + _, _ = setupConfigInOrg(t, "my-config1", 2, store) + _, _ = setupConfigInOrg(t, "my-config1", 3, store) + + res, err := store.GetAllLatestAlertmanagerConfiguration(context.Background()) + + require.NoError(t, err) + require.Len(t, res, 3) + sort.Slice(res, func(i, j int) bool { + return res[i].OrgID < res[j].OrgID + }) + require.Equal(t, int64(1), res[0].OrgID) + require.Equal(t, int64(2), res[1].OrgID) + require.Equal(t, int64(3), res[2].OrgID) + require.Equal(t, "my-config3", res[0].AlertmanagerConfiguration) + require.Equal(t, "my-config1", res[1].AlertmanagerConfiguration) + require.Equal(t, "my-config1", res[2].AlertmanagerConfiguration) + }) + + t.Run("SaveAlertmanagerConfigurationWithCallback calls callback", func(t *testing.T) { + called := false + callback := func() error { called = true; return nil } + cmd := buildSaveConfigCmd(t, "my-config", 1) + + err := store.SaveAlertmanagerConfigurationWithCallback(context.Background(), &cmd, callback) + + require.NoError(t, err) + require.True(t, called) + }) + + t.Run("SaveAlertmanagerConfigurationWithCallback rolls back if callback returns error", func(t *testing.T) { + _, _ = setupConfigInOrg(t, "my-config", 1, store) + callback := func() error { return fmt.Errorf("callback failed") } + cmd := buildSaveConfigCmd(t, "my-config-changed", 1) + + err := store.SaveAlertmanagerConfigurationWithCallback(context.Background(), &cmd, callback) + + require.ErrorContains(t, err, "callback failed") + // Assert that we rolled back the transaction. + get := &models.GetLatestAlertmanagerConfigurationQuery{OrgID: 1} + err = store.GetLatestAlertmanagerConfiguration(context.Background(), get) + require.NoError(t, err) + require.Equal(t, get.Result.AlertmanagerConfiguration, "my-config") + }) + + t.Run("UpdateAlertmanagerConfiguration returns error if existing config does not exist", func(t *testing.T) { + cmd := buildSaveConfigCmd(t, "my-config", 1234) + cmd.FetchedConfigurationHash = fmt.Sprintf("%x", md5.Sum([]byte("my-config"))) + err := store.UpdateAlertmanagerConfiguration(context.Background(), &cmd) + + require.ErrorIs(t, err, ErrVersionLockedObjectNotFound) + }) +} + func TestIntegrationAlertManagerHash(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") @@ -23,16 +133,6 @@ func TestIntegrationAlertManagerHash(t *testing.T) { Logger: log.NewNopLogger(), } - t.Run("After saving the DB should return the right hash", func(t *testing.T) { - _, configMD5 := setupConfig(t, "my-config", store) - req := &models.GetLatestAlertmanagerConfigurationQuery{ - OrgID: 1, - } - err := store.GetLatestAlertmanagerConfiguration(context.Background(), req) - require.NoError(t, err) - require.Equal(t, configMD5, req.Result.ConfigurationHash) - }) - t.Run("When passing the right hash the config should be updated", func(t *testing.T) { _, configMD5 := setupConfig(t, "my-config", store) req := &models.GetLatestAlertmanagerConfigurationQuery{ @@ -176,14 +276,25 @@ func TestIntegrationAlertManagerConfigCleanup(t *testing.T) { } func setupConfig(t *testing.T, config string, store *DBstore) (string, string) { + t.Helper() + return setupConfigInOrg(t, config, 1, store) +} + +func setupConfigInOrg(t *testing.T, config string, org int64, store *DBstore) (string, string) { t.Helper() config, configMD5 := config, fmt.Sprintf("%x", md5.Sum([]byte(config))) - err := store.SaveAlertmanagerConfiguration(context.Background(), &models.SaveAlertmanagerConfigurationCmd{ - AlertmanagerConfiguration: config, - ConfigurationVersion: "v1", - Default: false, - OrgID: 1, - }) + cmd := buildSaveConfigCmd(t, config, org) + err := store.SaveAlertmanagerConfiguration(context.Background(), &cmd) require.NoError(t, err) return config, configMD5 } + +func buildSaveConfigCmd(t *testing.T, config string, org int64) models.SaveAlertmanagerConfigurationCmd { + t.Helper() + return models.SaveAlertmanagerConfigurationCmd{ + AlertmanagerConfiguration: config, + ConfigurationVersion: "v1", + Default: false, + OrgID: org, + } +}