Files
Reloader/internal/pkg/alerting/alerter_test.go
T

242 lines
6.2 KiB
Go

package alerting
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stakater/Reloader/internal/pkg/config"
)
// testServer creates a test HTTP server that captures the request body.
// Returns the server and a function to retrieve the captured body.
func testServer(t *testing.T) (*httptest.Server, func() []byte) {
t.Helper()
var body []byte
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("Expected POST request, got %s", r.Method)
}
if r.Header.Get("Content-Type") != "application/json" {
t.Errorf("Expected Content-Type application/json, got %s", r.Header.Get("Content-Type"))
}
body, _ = io.ReadAll(r.Body)
w.WriteHeader(http.StatusOK)
}))
return server, func() []byte { return body }
}
// testAlertMessage returns a standard AlertMessage for testing.
func testAlertMessage() AlertMessage {
return AlertMessage{
WorkloadKind: "Deployment",
WorkloadName: "nginx",
WorkloadNamespace: "default",
ResourceKind: "ConfigMap",
ResourceName: "nginx-config",
ResourceNamespace: "default",
Timestamp: time.Now(),
}
}
func TestNewAlerter(t *testing.T) {
tests := []struct {
name string
setup func(*config.Config)
wantType string
}{
{
name: "disabled",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = false
},
wantType: "*alerting.NoOpAlerter",
},
{
name: "no webhook URL",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = ""
},
wantType: "*alerting.NoOpAlerter",
},
{
name: "slack",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = "http://example.com/webhook"
cfg.Alerting.Sink = "slack"
},
wantType: "*alerting.SlackAlerter",
},
{
name: "teams",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = "http://example.com/webhook"
cfg.Alerting.Sink = "teams"
},
wantType: "*alerting.TeamsAlerter",
},
{
name: "gchat",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = "http://example.com/webhook"
cfg.Alerting.Sink = "gchat"
},
wantType: "*alerting.GChatAlerter",
},
{
name: "raw",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = "http://example.com/webhook"
cfg.Alerting.Sink = "raw"
},
wantType: "*alerting.RawAlerter",
},
{
name: "empty sink defaults to raw",
setup: func(cfg *config.Config) {
cfg.Alerting.Enabled = true
cfg.Alerting.WebhookURL = "http://example.com/webhook"
cfg.Alerting.Sink = ""
},
wantType: "*alerting.RawAlerter",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := config.NewDefault()
tt.setup(cfg)
alerter := NewAlerter(cfg)
gotType := getTypeName(alerter)
if gotType != tt.wantType {
t.Errorf("NewAlerter() type = %s, want %s", gotType, tt.wantType)
}
})
}
}
func getTypeName(a Alerter) string {
switch a.(type) {
case *NoOpAlerter:
return "*alerting.NoOpAlerter"
case *SlackAlerter:
return "*alerting.SlackAlerter"
case *TeamsAlerter:
return "*alerting.TeamsAlerter"
case *GChatAlerter:
return "*alerting.GChatAlerter"
case *RawAlerter:
return "*alerting.RawAlerter"
default:
return "unknown"
}
}
func TestNoOpAlerter_Send(t *testing.T) {
alerter := &NoOpAlerter{}
if err := alerter.Send(context.Background(), AlertMessage{}); err != nil {
t.Errorf("NoOpAlerter.Send() error = %v, want nil", err)
}
}
func TestAlerter_Send(t *testing.T) {
tests := []struct {
name string
newAlert func(url string) Alerter
validate func(t *testing.T, body []byte)
}{
{
name: "slack",
newAlert: func(url string) Alerter { return NewSlackAlerter(url, "", "Test Cluster") },
validate: func(t *testing.T, body []byte) {
var msg slackMessage
if err := json.Unmarshal(body, &msg); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if msg.Text == "" {
t.Error("Expected non-empty text")
}
},
},
{
name: "teams",
newAlert: func(url string) Alerter { return NewTeamsAlerter(url, "", "") },
validate: func(t *testing.T, body []byte) {
var msg teamsMessage
if err := json.Unmarshal(body, &msg); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if msg.Type != "MessageCard" {
t.Errorf("@type = %s, want MessageCard", msg.Type)
}
},
},
{
name: "gchat",
newAlert: func(url string) Alerter { return NewGChatAlerter(url, "", "") },
validate: func(t *testing.T, body []byte) {
var msg gchatMessage
if err := json.Unmarshal(body, &msg); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if len(msg.Cards) != 1 {
t.Errorf("cards = %d, want 1", len(msg.Cards))
}
},
},
{
name: "raw",
newAlert: func(url string) Alerter { return NewRawAlerter(url, "", "custom-info") },
validate: func(t *testing.T, body []byte) {
var msg rawMessage
if err := json.Unmarshal(body, &msg); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if msg.Event != "reload" {
t.Errorf("event = %s, want reload", msg.Event)
}
if msg.WorkloadName != "nginx" {
t.Errorf("workloadName = %s, want nginx", msg.WorkloadName)
}
if msg.Additional != "custom-info" {
t.Errorf("additional = %s, want custom-info", msg.Additional)
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server, getBody := testServer(t)
defer server.Close()
alerter := tt.newAlert(server.URL)
if err := alerter.Send(context.Background(), testAlertMessage()); err != nil {
t.Fatalf("Send() error = %v", err)
}
tt.validate(t, getBody())
})
}
}
func TestAlerter_WebhookError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer server.Close()
alerter := NewRawAlerter(server.URL, "", "")
if err := alerter.Send(context.Background(), AlertMessage{}); err == nil {
t.Error("Expected error for non-2xx response")
}
}