Alerting: Add integration tests for mute timings provisioning API (#79810)
This commit is contained in:
@@ -6,12 +6,18 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/timeinterval"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
@@ -371,6 +377,235 @@ func TestIntegrationProvisioning(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestMuteTimings(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
createUser(t, store, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleAdmin),
|
||||
Password: "admin",
|
||||
Login: "admin",
|
||||
})
|
||||
|
||||
apiClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
|
||||
|
||||
t.Run("default config should return empty list", func(t *testing.T) {
|
||||
mt, status, body := apiClient.GetAllMuteTimingsWithStatus(t)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Empty(t, mt)
|
||||
})
|
||||
|
||||
emptyMuteTiming := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Empty Mute Timing",
|
||||
TimeIntervals: []timeinterval.TimeInterval{},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("should create a new mute timing without any intervals", func(t *testing.T) {
|
||||
mt, status, body := apiClient.CreateMuteTimingWithStatus(t, emptyMuteTiming)
|
||||
requireStatusCode(t, http.StatusCreated, status, body)
|
||||
require.Equal(t, emptyMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt.Provenance)
|
||||
})
|
||||
|
||||
anotherMuteTiming := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Not Empty Mute Timing",
|
||||
TimeIntervals: []timeinterval.TimeInterval{
|
||||
{
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 10,
|
||||
EndMinute: 45,
|
||||
},
|
||||
},
|
||||
Weekdays: []timeinterval.WeekdayRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 0,
|
||||
End: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 4,
|
||||
End: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("should create a new mute timing with some settings", func(t *testing.T) {
|
||||
mt, status, body := apiClient.CreateMuteTimingWithStatus(t, anotherMuteTiming)
|
||||
requireStatusCode(t, http.StatusCreated, status, body)
|
||||
require.Equal(t, anotherMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, models.ProvenanceAPI, mt.Provenance)
|
||||
})
|
||||
|
||||
t.Run("should return mute timing by name", func(t *testing.T) {
|
||||
mt, status, body := apiClient.GetMuteTimingByNameWithStatus(t, emptyMuteTiming.Name)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Equal(t, emptyMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, "", mt.Provenance) // TODO this is a bug
|
||||
|
||||
mt, status, body = apiClient.GetMuteTimingByNameWithStatus(t, anotherMuteTiming.Name)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Equal(t, anotherMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
require.EqualValues(t, "", mt.Provenance) // TODO this is a bug
|
||||
})
|
||||
|
||||
t.Run("should return NotFound if mute timing does not exist", func(t *testing.T) {
|
||||
_, status, body := apiClient.GetMuteTimingByNameWithStatus(t, "some-missing-timing")
|
||||
requireStatusCode(t, http.StatusNotFound, status, body)
|
||||
})
|
||||
|
||||
t.Run("should return all mute timings", func(t *testing.T) {
|
||||
mt, status, body := apiClient.GetAllMuteTimingsWithStatus(t)
|
||||
requireStatusCode(t, http.StatusOK, status, body)
|
||||
require.Len(t, mt, 2)
|
||||
|
||||
slices.SortFunc(mt, func(a, b definitions.MuteTimeInterval) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
require.Equal(t, emptyMuteTiming.MuteTimeInterval, mt[0].MuteTimeInterval)
|
||||
require.EqualValues(t, "", mt[0].Provenance) // TODO this is a bug
|
||||
|
||||
require.Equal(t, anotherMuteTiming.MuteTimeInterval, mt[1].MuteTimeInterval)
|
||||
require.EqualValues(t, "", mt[1].Provenance) // TODO this is a bug
|
||||
})
|
||||
|
||||
t.Run("should get BadRequest if creates a new mute timing with the same name", func(t *testing.T) {
|
||||
m := anotherMuteTiming
|
||||
m.TimeIntervals = nil
|
||||
_, status, body := apiClient.CreateMuteTimingWithStatus(t, m)
|
||||
t.Log(body)
|
||||
requireStatusCode(t, http.StatusBadRequest, status, body)
|
||||
var validationError map[string]any
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &validationError))
|
||||
assert.Contains(t, validationError, "message")
|
||||
if t.Failed() {
|
||||
t.Fatalf("response: %s", body)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should get BadRequest if creates an invalid mute timing", func(t *testing.T) {
|
||||
m := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Invalid",
|
||||
TimeIntervals: []timeinterval.TimeInterval{
|
||||
{
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 20000,
|
||||
EndMinute: 90000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, status, body := apiClient.CreateMuteTimingWithStatus(t, m)
|
||||
t.Log(body)
|
||||
requireStatusCode(t, http.StatusBadRequest, status, body)
|
||||
var validationError map[string]any
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &validationError))
|
||||
assert.Contains(t, validationError, "message")
|
||||
if t.Failed() {
|
||||
t.Fatalf("response: %s", body)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should update existing mute timing", func(t *testing.T) {
|
||||
anotherMuteTiming.TimeIntervals = []timeinterval.TimeInterval{
|
||||
{
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 36,
|
||||
EndMinute: 49,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mt, status, body := apiClient.UpdateMuteTimingWithStatus(t, anotherMuteTiming)
|
||||
requireStatusCode(t, http.StatusAccepted, status, body)
|
||||
require.Equal(t, anotherMuteTiming.MuteTimeInterval, mt.MuteTimeInterval)
|
||||
})
|
||||
|
||||
t.Run("should fail to update existing mute timing with invalid one", func(t *testing.T) {
|
||||
mt := anotherMuteTiming
|
||||
mt.TimeIntervals = []timeinterval.TimeInterval{
|
||||
{
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 360000,
|
||||
EndMinute: 490000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, status, body := apiClient.UpdateMuteTimingWithStatus(t, mt)
|
||||
|
||||
requireStatusCode(t, http.StatusBadRequest, status, body)
|
||||
var validationError map[string]any
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &validationError))
|
||||
assert.Contains(t, validationError, "message")
|
||||
if t.Failed() {
|
||||
t.Fatalf("response: %s", body)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should get NotFound if updates mute timing that does not exist", func(t *testing.T) {
|
||||
mt := definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: config.MuteTimeInterval{
|
||||
Name: "Missing Mute Timing",
|
||||
},
|
||||
}
|
||||
_, status, body := apiClient.UpdateMuteTimingWithStatus(t, mt)
|
||||
requireStatusCode(t, http.StatusNotFound, status, body)
|
||||
})
|
||||
|
||||
t.Run("should delete unused mute timing", func(t *testing.T) {
|
||||
status, body := apiClient.DeleteMuteTimingWithStatus(t, emptyMuteTiming.Name)
|
||||
requireStatusCode(t, http.StatusNoContent, status, body)
|
||||
|
||||
_, status, body = apiClient.GetMuteTimingByNameWithStatus(t, emptyMuteTiming.Name)
|
||||
requireStatusCode(t, http.StatusNotFound, status, body)
|
||||
})
|
||||
|
||||
t.Run("should get BadRequest if deletes used mute-timing", func(t *testing.T) {
|
||||
route, status, response := apiClient.GetRouteWithStatus(t)
|
||||
requireStatusCode(t, http.StatusOK, status, response)
|
||||
route.Routes = append(route.Routes, &definitions.Route{
|
||||
Receiver: route.Receiver,
|
||||
ObjectMatchers: definitions.ObjectMatchers{
|
||||
{
|
||||
Name: "a",
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
MuteTimeIntervals: []string{anotherMuteTiming.Name},
|
||||
})
|
||||
status, response = apiClient.UpdateRouteWithStatus(t, route)
|
||||
requireStatusCode(t, http.StatusAccepted, status, response)
|
||||
|
||||
status, response = apiClient.DeleteMuteTimingWithStatus(t, anotherMuteTiming.Name)
|
||||
requireStatusCode(t, http.StatusInternalServerError, status, response) // TODO should be bad request
|
||||
})
|
||||
}
|
||||
|
||||
func createTestRequest(method string, url string, user string, body string) *http.Request {
|
||||
var bodyBuf io.Reader
|
||||
if body != "" {
|
||||
|
||||
Reference in New Issue
Block a user