Add tests for create and then update silences (#3899)

This commit adds a unit test for the postSilencesHandler to
create and then update a silence. It shows that changing the
ID of an existing silence returns 404 Not Found, and removing
the ID of an existing silence re-creates that silence with
a different ID.

Signed-off-by: George Robinson <george.robinson@grafana.com>
This commit is contained in:
George Robinson 2024-06-25 11:58:45 +01:00 committed by GitHub
parent ffd7681fb4
commit b676fc4d2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 19 deletions

View File

@ -15,6 +15,7 @@ package v2
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -24,6 +25,7 @@ import (
"time" "time"
"github.com/go-openapi/runtime" "github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -269,32 +271,108 @@ func TestPostSilencesHandler(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
silence, silenceBytes := createSilence(t, tc.sid, "silenceCreator", tc.start, tc.end)
api := API{ api := API{
uptime: time.Now(), uptime: time.Now(),
silences: silences, silences: silences,
logger: log.NewNopLogger(), logger: log.NewNopLogger(),
} }
r, err := http.NewRequest("POST", "/api/v2/silence/${tc.sid}", bytes.NewReader(silenceBytes)) sil := createSilence(t, tc.sid, "silenceCreator", tc.start, tc.end)
require.NoError(t, err)
w := httptest.NewRecorder() w := httptest.NewRecorder()
p := runtime.TextProducer() postSilences(t, w, api.postSilencesHandler, sil)
responder := api.postSilencesHandler(silence_ops.PostSilencesParams{
HTTPRequest: r,
Silence: &silence,
})
responder.WriteResponse(w, p)
body, _ := io.ReadAll(w.Result().Body) body, _ := io.ReadAll(w.Result().Body)
require.Equal(t, tc.expectedCode, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body))) require.Equal(t, tc.expectedCode, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body)))
}) })
} }
}) })
} }
func TestPostSilencesHandlerMissingIdCreatesSilence(t *testing.T) {
now := time.Now()
silences := newSilences(t)
api := API{
uptime: time.Now(),
silences: silences,
logger: log.NewNopLogger(),
}
// Create a new silence. It should be assigned a random UUID.
sil := createSilence(t, "", "silenceCreator", now.Add(time.Hour), now.Add(time.Hour*2))
w := httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusOK, w.Code)
// Get the silences from the API.
w = httptest.NewRecorder()
getSilences(t, w, api.getSilencesHandler)
require.Equal(t, http.StatusOK, w.Code)
var resp []open_api_models.GettableSilence
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Len(t, resp, 1)
// Change the ID. It should return 404 Not Found.
sil = open_api_models.PostableSilence{
ID: "unknownID",
Silence: resp[0].Silence,
}
w = httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusNotFound, w.Code)
// Remove the ID. It should duplicate the silence with a different UUID.
sil = open_api_models.PostableSilence{
ID: "",
Silence: resp[0].Silence,
}
w = httptest.NewRecorder()
postSilences(t, w, api.postSilencesHandler, sil)
require.Equal(t, http.StatusOK, w.Code)
// Get the silences from the API. There should now be 2 silences.
w = httptest.NewRecorder()
getSilences(t, w, api.getSilencesHandler)
require.Equal(t, http.StatusOK, w.Code)
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Len(t, resp, 2)
require.NotEqual(t, resp[0].ID, resp[1].ID)
}
func getSilences(
t *testing.T,
w *httptest.ResponseRecorder,
handlerFunc func(params silence_ops.GetSilencesParams) middleware.Responder,
) {
r, err := http.NewRequest("GET", "/api/v2/silences", nil)
require.NoError(t, err)
p := runtime.TextProducer()
responder := handlerFunc(silence_ops.GetSilencesParams{
HTTPRequest: r,
Filter: nil,
})
responder.WriteResponse(w, p)
}
func postSilences(
t *testing.T,
w *httptest.ResponseRecorder,
handlerFunc func(params silence_ops.PostSilencesParams) middleware.Responder,
sil open_api_models.PostableSilence,
) {
b, err := json.Marshal(sil)
require.NoError(t, err)
r, err := http.NewRequest("POST", "/api/v2/silences", bytes.NewReader(b))
require.NoError(t, err)
p := runtime.TextProducer()
responder := handlerFunc(silence_ops.PostSilencesParams{
HTTPRequest: r,
Silence: &sil,
})
responder.WriteResponse(w, p)
}
func TestCheckSilenceMatchesFilterLabels(t *testing.T) { func TestCheckSilenceMatchesFilterLabels(t *testing.T) {
type test struct { type test struct {
silenceMatchers []*silencepb.Matcher silenceMatchers []*silencepb.Matcher

View File

@ -14,19 +14,17 @@
package v2 package v2
import ( import (
"encoding/json"
"testing" "testing"
"time" "time"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
"github.com/stretchr/testify/require"
open_api_models "github.com/prometheus/alertmanager/api/v2/models" open_api_models "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/silence/silencepb"
) )
func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (open_api_models.PostableSilence, []byte) { func createSilence(t *testing.T, ID, creator string, start, ends time.Time) open_api_models.PostableSilence {
t.Helper() t.Helper()
comment := "test" comment := "test"
@ -46,10 +44,7 @@ func createSilence(t *testing.T, ID, creator string, start, ends time.Time) (ope
Comment: &comment, Comment: &comment,
}, },
} }
b, err := json.Marshal(&sil) return sil
require.NoError(t, err)
return sil, b
} }
func createSilenceMatcher(t *testing.T, name, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher { func createSilenceMatcher(t *testing.T, name, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher {