mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-22 03:02:35 +00:00
test: Add error path tests for renderWebhookURL and UpdateAllowedPrivateCIDRs
Add comprehensive error handling tests for two pure functions: renderWebhookURL (8 new test cases): - Empty/whitespace URL template validation - Invalid template syntax (unclosed braces, undefined functions) - Template producing empty URL - Missing scheme or host in rendered URL UpdateAllowedPrivateCIDRs (expanded from 8 to 29 cases): - Invalid IP addresses (garbage, out of range, malformed) - Invalid CIDR notation (prefix too large, negative, non-numeric) - Malformed strings (double slash, invalid IP with valid prefix) - Success cases for valid IPv4/IPv6 CIDRs and bare IPs
This commit is contained in:
parent
d548287105
commit
2d75350dfa
2 changed files with 249 additions and 15 deletions
|
|
@ -507,6 +507,122 @@ func TestRenderWebhookURL_InvalidTemplate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRenderWebhookURL_ErrorPaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
urlTemplate string
|
||||
data WebhookPayloadData
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "empty URL template",
|
||||
urlTemplate: "",
|
||||
data: WebhookPayloadData{},
|
||||
wantErr: "webhook URL cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "whitespace-only URL template",
|
||||
urlTemplate: " \t\n ",
|
||||
data: WebhookPayloadData{},
|
||||
wantErr: "webhook URL cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "invalid template syntax",
|
||||
urlTemplate: "https://example.com/{{.Unclosed",
|
||||
data: WebhookPayloadData{},
|
||||
wantErr: "invalid webhook URL template",
|
||||
},
|
||||
{
|
||||
name: "template execution error - undefined function",
|
||||
urlTemplate: "https://example.com/{{undefined_func .Message}}",
|
||||
data: WebhookPayloadData{Message: "test"},
|
||||
wantErr: "invalid webhook URL template",
|
||||
},
|
||||
{
|
||||
name: "template produces empty URL",
|
||||
urlTemplate: "{{if false}}https://example.com{{end}}",
|
||||
data: WebhookPayloadData{},
|
||||
wantErr: "webhook URL template produced empty URL",
|
||||
},
|
||||
{
|
||||
name: "template renders to missing scheme",
|
||||
urlTemplate: "{{.Message}}/path",
|
||||
data: WebhookPayloadData{Message: "example.com"},
|
||||
wantErr: "missing scheme or host",
|
||||
},
|
||||
{
|
||||
name: "template renders to missing host",
|
||||
urlTemplate: "{{.Message}}://",
|
||||
data: WebhookPayloadData{Message: "https"},
|
||||
wantErr: "missing scheme or host",
|
||||
},
|
||||
{
|
||||
name: "template renders to relative path",
|
||||
urlTemplate: "/{{.Message}}/webhook",
|
||||
data: WebhookPayloadData{Message: "api"},
|
||||
wantErr: "missing scheme or host",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := renderWebhookURL(tt.urlTemplate, tt.data)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, got nil (result: %q)", tt.wantErr, result)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Fatalf("expected error containing %q, got: %v", tt.wantErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderWebhookURL_SuccessCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
urlTemplate string
|
||||
data WebhookPayloadData
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "static URL - no template",
|
||||
urlTemplate: "https://example.com/webhook",
|
||||
data: WebhookPayloadData{},
|
||||
want: "https://example.com/webhook",
|
||||
},
|
||||
{
|
||||
name: "URL with whitespace trimmed",
|
||||
urlTemplate: " https://example.com/webhook ",
|
||||
data: WebhookPayloadData{},
|
||||
want: "https://example.com/webhook",
|
||||
},
|
||||
{
|
||||
name: "URL with template variable in path",
|
||||
urlTemplate: "https://example.com/{{.ResourceType}}/alert",
|
||||
data: WebhookPayloadData{ResourceType: "vm"},
|
||||
want: "https://example.com/vm/alert",
|
||||
},
|
||||
{
|
||||
name: "URL with urlquery encoding",
|
||||
urlTemplate: "https://example.com?msg={{urlquery .Message}}",
|
||||
data: WebhookPayloadData{Message: "hello world"},
|
||||
want: "https://example.com?msg=hello+world",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := renderWebhookURL(tt.urlTemplate, tt.data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result != tt.want {
|
||||
t.Fatalf("expected %q, got %q", tt.want, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendTestNotificationApprise(t *testing.T) {
|
||||
nm := NewNotificationManager("")
|
||||
nm.SetEmailConfig(EmailConfig{Enabled: false})
|
||||
|
|
|
|||
|
|
@ -2,64 +2,182 @@ package notifications
|
|||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpdateAllowedPrivateCIDRs(t *testing.T) {
|
||||
nm := NewNotificationManager("")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cidrs string
|
||||
wantErr bool
|
||||
wantErr string // empty string means no error expected
|
||||
}{
|
||||
// Success cases
|
||||
{
|
||||
name: "empty string clears allowlist",
|
||||
cidrs: "",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "single valid CIDR",
|
||||
cidrs: "192.168.1.0/24",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "multiple valid CIDRs",
|
||||
cidrs: "192.168.1.0/24,10.0.0.0/8",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "CIDR with spaces",
|
||||
cidrs: "192.168.1.0/24, 10.0.0.0/8",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "bare IPv4 address",
|
||||
cidrs: "192.168.1.1",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "bare IPv6 address",
|
||||
cidrs: "fe80::1",
|
||||
wantErr: false,
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "invalid CIDR",
|
||||
name: "valid IPv6 CIDR",
|
||||
cidrs: "fe80::/10",
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "loopback IPv6",
|
||||
cidrs: "::1",
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "multiple valid CIDRs with mixed IP versions",
|
||||
cidrs: "192.168.1.0/24, 10.0.0.0/8, fe80::/10",
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "mixed bare IPs and CIDRs",
|
||||
cidrs: "192.168.1.1, 10.0.0.0/8",
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "whitespace handling",
|
||||
cidrs: " 192.168.1.0/24 , 10.0.0.1 ",
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "empty entries skipped",
|
||||
cidrs: "192.168.1.0/24,,10.0.0.1",
|
||||
wantErr: "",
|
||||
},
|
||||
|
||||
// Error cases - invalid IP addresses (bare IPs without CIDR notation)
|
||||
{
|
||||
name: "invalid IP address - garbage text",
|
||||
cidrs: "not-a-cidr",
|
||||
wantErr: true,
|
||||
wantErr: "invalid IP address: not-a-cidr",
|
||||
},
|
||||
{
|
||||
name: "invalid IP address",
|
||||
name: "invalid IP address - out of range octets",
|
||||
cidrs: "999.999.999.999",
|
||||
wantErr: true,
|
||||
wantErr: "invalid IP address: 999.999.999.999",
|
||||
},
|
||||
{
|
||||
name: "invalid IP address in list",
|
||||
cidrs: "192.168.1.0/24, invalid, 10.0.0.1",
|
||||
wantErr: "invalid IP address: invalid",
|
||||
},
|
||||
{
|
||||
name: "IP with too many octets",
|
||||
cidrs: "192.168.1.1.1",
|
||||
wantErr: "invalid IP address: 192.168.1.1.1",
|
||||
},
|
||||
{
|
||||
name: "IP with negative octet",
|
||||
cidrs: "192.168.-1.0",
|
||||
wantErr: "invalid IP address: 192.168.-1.0",
|
||||
},
|
||||
{
|
||||
name: "IP with octet out of range",
|
||||
cidrs: "192.168.256.0",
|
||||
wantErr: "invalid IP address: 192.168.256.0",
|
||||
},
|
||||
|
||||
// Error cases - invalid CIDR notation
|
||||
{
|
||||
name: "CIDR prefix too large for IPv4",
|
||||
cidrs: "192.168.1.0/33",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/33",
|
||||
},
|
||||
{
|
||||
name: "CIDR prefix too large for IPv6",
|
||||
cidrs: "fe80::/129",
|
||||
wantErr: "invalid CIDR range fe80::/129",
|
||||
},
|
||||
{
|
||||
name: "CIDR prefix way too large",
|
||||
cidrs: "192.168.1.0/999",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/999",
|
||||
},
|
||||
{
|
||||
name: "CIDR with negative prefix",
|
||||
cidrs: "192.168.1.0/-1",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/-1",
|
||||
},
|
||||
{
|
||||
name: "CIDR with non-numeric prefix",
|
||||
cidrs: "192.168.1.0/abc",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/abc",
|
||||
},
|
||||
{
|
||||
name: "CIDR with empty prefix",
|
||||
cidrs: "192.168.1.0/",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/",
|
||||
},
|
||||
{
|
||||
name: "CIDR with floating point prefix",
|
||||
cidrs: "192.168.1.0/24.5",
|
||||
wantErr: "invalid CIDR range 192.168.1.0/24.5",
|
||||
},
|
||||
|
||||
// Error cases - malformed strings
|
||||
{
|
||||
name: "double slash in CIDR",
|
||||
cidrs: "192.168.1.0//24",
|
||||
wantErr: "invalid CIDR range 192.168.1.0//24",
|
||||
},
|
||||
{
|
||||
name: "CIDR with invalid IP part",
|
||||
cidrs: "999.999.999.999/24",
|
||||
wantErr: "invalid CIDR range 999.999.999.999/24",
|
||||
},
|
||||
{
|
||||
name: "valid CIDR followed by invalid CIDR",
|
||||
cidrs: "192.168.1.0/24, bad/cidr",
|
||||
wantErr: "invalid CIDR range bad/cidr",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
nm := NewNotificationManager("")
|
||||
defer nm.Stop()
|
||||
|
||||
err := nm.UpdateAllowedPrivateCIDRs(tt.cidrs)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("UpdateAllowedPrivateCIDRs() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
if tt.wantErr == "" {
|
||||
if err != nil {
|
||||
t.Errorf("UpdateAllowedPrivateCIDRs() unexpected error = %v", err)
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Errorf("UpdateAllowedPrivateCIDRs() expected error containing %q, got nil", tt.wantErr)
|
||||
} else if !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Errorf("UpdateAllowedPrivateCIDRs() error = %v, want error containing %q", err, tt.wantErr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue