package notifications import ( "strings" "testing" ) func TestGetEmailProviders(t *testing.T) { providers := GetEmailProviders() if len(providers) == 0 { t.Fatal("GetEmailProviders() returned empty slice") } // Track seen names to ensure uniqueness seenNames := make(map[string]bool) for i, p := range providers { t.Run(p.Name, func(t *testing.T) { // Name must be non-empty if p.Name == "" { t.Errorf("Provider %d has empty Name", i) } // Name must be unique if seenNames[p.Name] { t.Errorf("Duplicate provider name: %q", p.Name) } seenNames[p.Name] = true // Instructions must be non-empty if p.Instructions == "" { t.Errorf("Provider %q has empty Instructions", p.Name) } // Port must be valid (except custom which can be 0) if p.SMTPHost != "" && (p.SMTPPort < 0 || p.SMTPPort > 65535) { t.Errorf("Provider %q has invalid SMTPPort: %d", p.Name, p.SMTPPort) } // Non-custom providers should have a host if p.Name != "Custom SMTP Server" && p.SMTPHost == "" { t.Errorf("Provider %q has empty SMTPHost", p.Name) } }) } } func TestGetEmailProviders_KnownProviders(t *testing.T) { providers := GetEmailProviders() // Expected providers that should exist expectedProviders := []string{ "Gmail / Google Workspace", "SendGrid", "Mailgun", "Amazon SES (US East)", "Microsoft 365 / Outlook", "Custom SMTP Server", } providerMap := make(map[string]EmailProvider) for _, p := range providers { providerMap[p.Name] = p } for _, name := range expectedProviders { if _, exists := providerMap[name]; !exists { t.Errorf("Expected provider %q not found", name) } } } func TestGetEmailProviders_GmailSettings(t *testing.T) { providers := GetEmailProviders() var gmail *EmailProvider for i := range providers { if providers[i].Name == "Gmail / Google Workspace" { gmail = &providers[i] break } } if gmail == nil { t.Fatal("Gmail provider not found") } if gmail.SMTPHost != "smtp.gmail.com" { t.Errorf("Gmail SMTPHost = %q, want smtp.gmail.com", gmail.SMTPHost) } if gmail.SMTPPort != 587 { t.Errorf("Gmail SMTPPort = %d, want 587", gmail.SMTPPort) } if !gmail.StartTLS { t.Error("Gmail StartTLS should be true") } if !gmail.AuthRequired { t.Error("Gmail AuthRequired should be true") } } func TestGetEmailProviders_SendGridSettings(t *testing.T) { providers := GetEmailProviders() var sendgrid *EmailProvider for i := range providers { if providers[i].Name == "SendGrid" { sendgrid = &providers[i] break } } if sendgrid == nil { t.Fatal("SendGrid provider not found") } if sendgrid.SMTPHost != "smtp.sendgrid.net" { t.Errorf("SendGrid SMTPHost = %q, want smtp.sendgrid.net", sendgrid.SMTPHost) } if sendgrid.SMTPPort != 587 { t.Errorf("SendGrid SMTPPort = %d, want 587", sendgrid.SMTPPort) } // Verify instructions mention apikey username if !strings.Contains(sendgrid.Instructions, "apikey") { t.Error("SendGrid instructions should mention 'apikey' as username") } } func TestGetEmailProviders_CustomServerSettings(t *testing.T) { providers := GetEmailProviders() var custom *EmailProvider for i := range providers { if providers[i].Name == "Custom SMTP Server" { custom = &providers[i] break } } if custom == nil { t.Fatal("Custom SMTP Server provider not found") } // Custom server should have empty host (user fills it in) if custom.SMTPHost != "" { t.Errorf("Custom SMTP Server SMTPHost = %q, want empty", custom.SMTPHost) } } func TestGetWebhookTemplates(t *testing.T) { templates := GetWebhookTemplates() if len(templates) == 0 { t.Fatal("GetWebhookTemplates() returned empty slice") } // Track seen services to ensure uniqueness seenServices := make(map[string]bool) for i, tmpl := range templates { t.Run(tmpl.Name, func(t *testing.T) { // Service must be non-empty if tmpl.Service == "" { t.Errorf("Template %d has empty Service", i) } // Service must be unique if seenServices[tmpl.Service] { t.Errorf("Duplicate template service: %q", tmpl.Service) } seenServices[tmpl.Service] = true // Name must be non-empty if tmpl.Name == "" { t.Errorf("Template %q has empty Name", tmpl.Service) } // Method must be valid HTTP method validMethods := map[string]bool{"GET": true, "POST": true, "PUT": true, "PATCH": true, "DELETE": true} if !validMethods[tmpl.Method] { t.Errorf("Template %q has invalid Method: %q", tmpl.Service, tmpl.Method) } // Instructions must be non-empty if tmpl.Instructions == "" { t.Errorf("Template %q has empty Instructions", tmpl.Service) } // PayloadTemplate must be non-empty if tmpl.PayloadTemplate == "" { t.Errorf("Template %q has empty PayloadTemplate", tmpl.Service) } // Headers must include Content-Type if _, hasContentType := tmpl.Headers["Content-Type"]; !hasContentType { t.Errorf("Template %q missing Content-Type header", tmpl.Service) } }) } } func TestGetWebhookTemplates_KnownServices(t *testing.T) { templates := GetWebhookTemplates() // Expected services that should exist expectedServices := []string{ "discord", "telegram", "slack", "teams", "pagerduty", "generic", } serviceMap := make(map[string]WebhookTemplate) for _, tmpl := range templates { serviceMap[tmpl.Service] = tmpl } for _, service := range expectedServices { if _, exists := serviceMap[service]; !exists { t.Errorf("Expected service %q not found", service) } } } func TestGetWebhookTemplates_DiscordSettings(t *testing.T) { templates := GetWebhookTemplates() var discord *WebhookTemplate for i := range templates { if templates[i].Service == "discord" { discord = &templates[i] break } } if discord == nil { t.Fatal("Discord template not found") } if !strings.Contains(discord.URLPattern, "discord.com") { t.Errorf("Discord URLPattern should contain discord.com, got %q", discord.URLPattern) } if discord.Method != "POST" { t.Errorf("Discord Method = %q, want POST", discord.Method) } if discord.Headers["Content-Type"] != "application/json" { t.Errorf("Discord Content-Type = %q, want application/json", discord.Headers["Content-Type"]) } // Verify payload contains Discord-specific fields if !strings.Contains(discord.PayloadTemplate, "embeds") { t.Error("Discord PayloadTemplate should contain 'embeds'") } } func TestGetWebhookTemplates_SlackSettings(t *testing.T) { templates := GetWebhookTemplates() var slack *WebhookTemplate for i := range templates { if templates[i].Service == "slack" { slack = &templates[i] break } } if slack == nil { t.Fatal("Slack template not found") } if !strings.Contains(slack.URLPattern, "hooks.slack.com") { t.Errorf("Slack URLPattern should contain hooks.slack.com, got %q", slack.URLPattern) } if slack.Method != "POST" { t.Errorf("Slack Method = %q, want POST", slack.Method) } // Verify payload contains Slack-specific fields if !strings.Contains(slack.PayloadTemplate, "blocks") { t.Error("Slack PayloadTemplate should contain 'blocks'") } } func TestGetWebhookTemplates_TelegramSettings(t *testing.T) { templates := GetWebhookTemplates() var telegram *WebhookTemplate for i := range templates { if templates[i].Service == "telegram" { telegram = &templates[i] break } } if telegram == nil { t.Fatal("Telegram template not found") } if !strings.Contains(telegram.URLPattern, "api.telegram.org") { t.Errorf("Telegram URLPattern should contain api.telegram.org, got %q", telegram.URLPattern) } if telegram.Method != "POST" { t.Errorf("Telegram Method = %q, want POST", telegram.Method) } // Verify payload contains Telegram-specific fields if !strings.Contains(telegram.PayloadTemplate, "chat_id") { t.Error("Telegram PayloadTemplate should contain 'chat_id'") } if !strings.Contains(telegram.PayloadTemplate, "parse_mode") { t.Error("Telegram PayloadTemplate should contain 'parse_mode'") } } func TestGetWebhookTemplates_PagerDutySettings(t *testing.T) { templates := GetWebhookTemplates() var pagerduty *WebhookTemplate for i := range templates { if templates[i].Service == "pagerduty" { pagerduty = &templates[i] break } } if pagerduty == nil { t.Fatal("PagerDuty template not found") } if !strings.Contains(pagerduty.URLPattern, "pagerduty.com") { t.Errorf("PagerDuty URLPattern should contain pagerduty.com, got %q", pagerduty.URLPattern) } if pagerduty.Method != "POST" { t.Errorf("PagerDuty Method = %q, want POST", pagerduty.Method) } // PagerDuty requires specific Accept header if pagerduty.Headers["Accept"] == "" { t.Error("PagerDuty should have Accept header") } // Verify payload contains PagerDuty-specific fields if !strings.Contains(pagerduty.PayloadTemplate, "routing_key") { t.Error("PagerDuty PayloadTemplate should contain 'routing_key'") } if !strings.Contains(pagerduty.PayloadTemplate, "event_action") { t.Error("PagerDuty PayloadTemplate should contain 'event_action'") } } func TestGetWebhookTemplates_GenericSettings(t *testing.T) { templates := GetWebhookTemplates() var generic *WebhookTemplate for i := range templates { if templates[i].Service == "generic" { generic = &templates[i] break } } if generic == nil { t.Fatal("Generic template not found") } // Generic should have empty URL pattern (user fills it in) if generic.URLPattern != "" { t.Errorf("Generic URLPattern = %q, want empty", generic.URLPattern) } if generic.Method != "POST" { t.Errorf("Generic Method = %q, want POST", generic.Method) } } func TestGetWebhookTemplates_PayloadTemplatesHaveRequiredFields(t *testing.T) { templates := GetWebhookTemplates() // All templates should include these alert fields in their payload requiredFields := []string{ "{{.Level}}", "{{.ResourceName}}", "{{.Node}}", "{{.Type}}", } for _, tmpl := range templates { t.Run(tmpl.Service, func(t *testing.T) { for _, field := range requiredFields { // Check for field or its variations (e.g., {{.Level | title}}) fieldBase := strings.TrimPrefix(strings.TrimSuffix(field, "}}"), "{{.") if !strings.Contains(tmpl.PayloadTemplate, "."+fieldBase) { t.Errorf("Template %q PayloadTemplate missing field %s", tmpl.Service, field) } } }) } } func TestEmailProviderConfig_Fields(t *testing.T) { cfg := EmailProviderConfig{ Provider: "gmail", ReplyTo: "reply@example.com", MaxRetries: 3, RetryDelay: 5, RateLimit: 60, StartTLS: true, SkipTLSVerify: false, AuthRequired: true, } if cfg.Provider != "gmail" { t.Errorf("Provider = %q, want gmail", cfg.Provider) } if cfg.ReplyTo != "reply@example.com" { t.Errorf("ReplyTo = %q, want reply@example.com", cfg.ReplyTo) } if cfg.MaxRetries != 3 { t.Errorf("MaxRetries = %d, want 3", cfg.MaxRetries) } if cfg.RetryDelay != 5 { t.Errorf("RetryDelay = %d, want 5", cfg.RetryDelay) } if cfg.RateLimit != 60 { t.Errorf("RateLimit = %d, want 60", cfg.RateLimit) } if !cfg.StartTLS { t.Error("StartTLS should be true") } if cfg.SkipTLSVerify { t.Error("SkipTLSVerify should be false") } if !cfg.AuthRequired { t.Error("AuthRequired should be true") } } func TestWebhookTemplate_Fields(t *testing.T) { tmpl := WebhookTemplate{ Service: "test", Name: "Test Webhook", URLPattern: "https://example.com/webhook", Method: "POST", Headers: map[string]string{"Content-Type": "application/json"}, PayloadTemplate: `{"message": "test"}`, Instructions: "Test instructions", } if tmpl.Service != "test" { t.Errorf("Service = %q, want test", tmpl.Service) } if tmpl.Name != "Test Webhook" { t.Errorf("Name = %q, want Test Webhook", tmpl.Name) } if tmpl.URLPattern != "https://example.com/webhook" { t.Errorf("URLPattern = %q, want https://example.com/webhook", tmpl.URLPattern) } if tmpl.Method != "POST" { t.Errorf("Method = %q, want POST", tmpl.Method) } if tmpl.Headers["Content-Type"] != "application/json" { t.Errorf("Headers[Content-Type] = %q, want application/json", tmpl.Headers["Content-Type"]) } if tmpl.PayloadTemplate != `{"message": "test"}` { t.Errorf("PayloadTemplate mismatch") } if tmpl.Instructions != "Test instructions" { t.Errorf("Instructions = %q, want Test instructions", tmpl.Instructions) } } func TestEmailProvider_Fields(t *testing.T) { provider := EmailProvider{ Name: "Test Provider", SMTPHost: "smtp.test.com", SMTPPort: 587, TLS: false, StartTLS: true, AuthRequired: true, Instructions: "Test instructions", } if provider.Name != "Test Provider" { t.Errorf("Name = %q, want Test Provider", provider.Name) } if provider.SMTPHost != "smtp.test.com" { t.Errorf("SMTPHost = %q, want smtp.test.com", provider.SMTPHost) } if provider.SMTPPort != 587 { t.Errorf("SMTPPort = %d, want 587", provider.SMTPPort) } if provider.TLS { t.Error("TLS should be false") } if !provider.StartTLS { t.Error("StartTLS should be true") } if !provider.AuthRequired { t.Error("AuthRequired should be true") } if provider.Instructions != "Test instructions" { t.Errorf("Instructions = %q, want Test instructions", provider.Instructions) } }