mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add tests for Slack Notifier Image Upload (#49669)
Tests that file bytes are correctly read from storage when an image is available without a URL. Tests that not found and success cases are both handled.
This commit is contained in:
@@ -7,10 +7,12 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -24,17 +26,45 @@ import (
|
||||
func TestSlackNotifier(t *testing.T) {
|
||||
tmpl := templateForTests(t)
|
||||
|
||||
// Create our temporary image file.
|
||||
f, err := os.CreateTemp("", "ngalert-images-example*.png")
|
||||
if err != nil {
|
||||
panic("Temp file error!")
|
||||
}
|
||||
defer func() { _ = os.Remove(f.Name()) }()
|
||||
|
||||
fakeImageStore := &fakeImageStore{
|
||||
Images: []*models.Image{
|
||||
{
|
||||
Token: "test-with-url",
|
||||
URL: "https://www.example.com/image.jpg",
|
||||
},
|
||||
{
|
||||
Token: "test-with-path-not-found",
|
||||
Path: "usr/home/nouser/noway.jpg",
|
||||
},
|
||||
{
|
||||
Token: "test-with-path-found",
|
||||
Path: f.Name(), // Has the full path because of how CreateTemp works.
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, _ = f.Write([]byte("test image"))
|
||||
_ = f.Close()
|
||||
|
||||
externalURL, err := url.Parse("http://localhost")
|
||||
require.NoError(t, err)
|
||||
tmpl.ExternalURL = externalURL
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
settings string
|
||||
alerts []*types.Alert
|
||||
expMsg *slackMessage
|
||||
expInitError string
|
||||
expMsgError error
|
||||
name string
|
||||
settings string
|
||||
alerts []*types.Alert
|
||||
expMsg *slackMessage
|
||||
expInitError string
|
||||
expMsgError error
|
||||
expWebhookURL string
|
||||
}{
|
||||
{
|
||||
name: "Correct config with one alert",
|
||||
@@ -106,6 +136,114 @@ func TestSlackNotifier(t *testing.T) {
|
||||
},
|
||||
expMsgError: nil,
|
||||
},
|
||||
{
|
||||
name: "Image URL in alert appears in slack message",
|
||||
settings: `{
|
||||
"token": "1234",
|
||||
"recipient": "#testchannel",
|
||||
"icon_emoji": ":emoji:"
|
||||
}`,
|
||||
alerts: []*types.Alert{
|
||||
{
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
||||
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-with-url"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsg: &slackMessage{
|
||||
Channel: "#testchannel",
|
||||
Username: "Grafana",
|
||||
IconEmoji: ":emoji:",
|
||||
Attachments: []attachment{
|
||||
{
|
||||
Title: "[FIRING:1] (val1)",
|
||||
TitleLink: "http://localhost/alerting/list",
|
||||
Text: "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
|
||||
Fallback: "[FIRING:1] (val1)",
|
||||
Fields: nil,
|
||||
Footer: "Grafana v" + setting.BuildVersion,
|
||||
FooterIcon: "https://grafana.com/assets/img/fav32.png",
|
||||
Color: "#D63232",
|
||||
Ts: 0,
|
||||
ImageURL: "https://www.example.com/image.jpg",
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsgError: nil,
|
||||
},
|
||||
{
|
||||
name: "Image URL with path but no file creates message with no error",
|
||||
settings: `{
|
||||
"token": "1234",
|
||||
"image_upload_url": "https://www.webhook.com",
|
||||
"recipient": "#testchannel",
|
||||
"icon_emoji": ":emoji:"
|
||||
}`,
|
||||
alerts: []*types.Alert{
|
||||
{
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
||||
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-with-path-not-found"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsg: &slackMessage{
|
||||
Channel: "#testchannel",
|
||||
Username: "Grafana",
|
||||
IconEmoji: ":emoji:",
|
||||
Attachments: []attachment{
|
||||
{
|
||||
Title: "[FIRING:1] (val1)",
|
||||
TitleLink: "http://localhost/alerting/list",
|
||||
Text: "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
|
||||
Fallback: "[FIRING:1] (val1)",
|
||||
Fields: nil,
|
||||
Footer: "Grafana v" + setting.BuildVersion,
|
||||
FooterIcon: "https://grafana.com/assets/img/fav32.png",
|
||||
Color: "#D63232",
|
||||
Ts: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsgError: nil,
|
||||
},
|
||||
{
|
||||
name: "Image URL with path and file creates message and uploads image",
|
||||
settings: `{
|
||||
"token": "1234",
|
||||
"recipient": "#testchannel",
|
||||
"icon_emoji": ":emoji:"
|
||||
}`,
|
||||
alerts: []*types.Alert{
|
||||
{
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
||||
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-with-path-found"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsg: &slackMessage{
|
||||
Channel: "#testchannel",
|
||||
Username: "Grafana",
|
||||
IconEmoji: ":emoji:",
|
||||
Attachments: []attachment{
|
||||
{
|
||||
Title: "[FIRING:1] (val1)",
|
||||
TitleLink: "http://localhost/alerting/list",
|
||||
Text: "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
|
||||
Fallback: "[FIRING:1] (val1)",
|
||||
Fields: nil,
|
||||
Footer: "Grafana v" + setting.BuildVersion,
|
||||
FooterIcon: "https://grafana.com/assets/img/fav32.png",
|
||||
Color: "#D63232",
|
||||
Ts: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsgError: nil,
|
||||
expWebhookURL: SlackImageAPIEndpoint,
|
||||
},
|
||||
{
|
||||
name: "Correct config with multiple alerts and template",
|
||||
settings: `{
|
||||
@@ -196,6 +334,43 @@ func TestSlackNotifier(t *testing.T) {
|
||||
},
|
||||
expMsgError: nil,
|
||||
},
|
||||
{
|
||||
name: "Custom image upload URL",
|
||||
settings: `{
|
||||
"token": "1234",
|
||||
"recipient": "#testchannel",
|
||||
"icon_emoji": ":emoji:",
|
||||
"imageUploadUrl": "https://custom-domain.upload"
|
||||
}`,
|
||||
alerts: []*types.Alert{
|
||||
{
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
||||
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-with-path-found"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsg: &slackMessage{
|
||||
Channel: "#testchannel",
|
||||
Username: "Grafana",
|
||||
IconEmoji: ":emoji:",
|
||||
Attachments: []attachment{
|
||||
{
|
||||
Title: "[FIRING:1] (val1)",
|
||||
TitleLink: "http://localhost/alerting/list",
|
||||
Text: "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
|
||||
Fallback: "[FIRING:1] (val1)",
|
||||
Fields: nil,
|
||||
Footer: "Grafana v" + setting.BuildVersion,
|
||||
FooterIcon: "https://grafana.com/assets/img/fav32.png",
|
||||
Color: "#D63232",
|
||||
Ts: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
expMsgError: nil,
|
||||
expWebhookURL: "https://custom-domain.upload",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -206,6 +381,8 @@ func TestSlackNotifier(t *testing.T) {
|
||||
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
notificationService := mockNotificationService()
|
||||
|
||||
fc := FactoryConfig{
|
||||
Config: &NotificationChannelConfig{
|
||||
Name: "slack_testing",
|
||||
@@ -213,8 +390,9 @@ func TestSlackNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
SecureSettings: secureSettings,
|
||||
},
|
||||
ImageStore: &UnavailableImageStore{},
|
||||
NotificationService: mockNotificationService(),
|
||||
ImageStore: fakeImageStore,
|
||||
// TODO: allow changing the associated values for different tests.
|
||||
NotificationService: notificationService,
|
||||
DecryptFunc: decryptFn,
|
||||
}
|
||||
|
||||
@@ -271,6 +449,10 @@ func TestSlackNotifier(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, string(expBody), body)
|
||||
|
||||
// If we should have sent to the webhook, the mock notification service
|
||||
// will have a record of it.
|
||||
require.Equal(t, c.expWebhookURL, notificationService.Webhook.Url)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
// mockTimeNow replaces function timeNow to return constant time.
|
||||
@@ -48,3 +49,16 @@ func (ns *notificationServiceMock) SendEmailCommandHandler(ctx context.Context,
|
||||
}
|
||||
|
||||
func mockNotificationService() *notificationServiceMock { return ¬ificationServiceMock{} }
|
||||
|
||||
type fakeImageStore struct {
|
||||
Images []*ngmodels.Image
|
||||
}
|
||||
|
||||
func (f *fakeImageStore) GetImage(ctx context.Context, token string) (*ngmodels.Image, error) {
|
||||
for _, img := range f.Images {
|
||||
if img.Token == token {
|
||||
return img, nil
|
||||
}
|
||||
}
|
||||
return nil, ngmodels.ErrImageNotFound
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user