Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 273 additions & 0 deletions pkg/services/slack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package services

import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -63,6 +64,149 @@ func TestGetTemplater_Slack(t *testing.T) {
assert.Equal(t, true, notification.Slack.NotifyBroadcast)
}

func TestGetTemplater_Slack_InvalidTemplates(t *testing.T) {
tests := []struct {
name string
notification SlackNotification
}{
{
name: "Invalid username template",
notification: SlackNotification{
Username: "{{.foo",
},
},
{
name: "Invalid icon template",
notification: SlackNotification{
Icon: "{{.foo",
},
},
{
name: "Invalid attachments template",
notification: SlackNotification{
Attachments: "{{.foo",
},
},
{
name: "Invalid blocks template",
notification: SlackNotification{
Blocks: "{{.foo",
},
},
{
name: "Invalid grouping key template",
notification: SlackNotification{
GroupingKey: "{{.foo",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := tt.notification.GetTemplater("test", template.FuncMap{})
assert.Error(t, err)
})
}
}

func TestGetTemplater_Slack_NilNotification(t *testing.T) {
n := Notification{
Slack: &SlackNotification{
Username: "{{.name}}",
},
}

templater, err := n.GetTemplater("", template.FuncMap{})
assert.NoError(t, err)

// Test with nil Slack on target notification
var notification Notification
err = templater(&notification, map[string]interface{}{
"name": "test",
})

assert.NoError(t, err)
assert.NotNil(t, notification.Slack)
assert.Equal(t, "test", notification.Slack.Username)
}

func TestGetTemplater_Slack_DeliveryPolicy(t *testing.T) {
n := Notification{
Slack: &SlackNotification{
Username: "bot",
DeliveryPolicy: slackutil.Update,
},
}

templater, err := n.GetTemplater("", template.FuncMap{})
assert.NoError(t, err)

var notification Notification
err = templater(&notification, map[string]interface{}{})

assert.NoError(t, err)
assert.Equal(t, slackutil.Update, notification.Slack.DeliveryPolicy)
}

func TestGetTemplater_Slack_TemplateExecutionError(t *testing.T) {
// Create a FuncMap with the required function
funcMap := template.FuncMap{
"required": func(msg string, val interface{}) (interface{}, error) {
if val == nil || val == "" {
return nil, fmt.Errorf("%s", msg)
}
return val, nil
},
}

tests := []struct {
name string
notification SlackNotification
}{
{
name: "Username execution error",
notification: SlackNotification{
Username: "{{.missing | required \"missing is required\"}}",
},
},
{
name: "Icon execution error",
notification: SlackNotification{
Icon: "{{.missing | required \"missing is required\"}}",
},
},
{
name: "Attachments execution error",
notification: SlackNotification{
Attachments: "{{.missing | required \"missing is required\"}}",
},
},
{
name: "Blocks execution error",
notification: SlackNotification{
Blocks: "{{.missing | required \"missing is required\"}}",
},
},
{
name: "GroupingKey execution error",
notification: SlackNotification{
GroupingKey: "{{.missing | required \"missing is required\"}}",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
templater, err := tt.notification.GetTemplater("", funcMap)
assert.NoError(t, err)

var notification Notification
err = templater(&notification, map[string]interface{}{})
assert.Error(t, err)
})
}
}

func TestBuildMessageOptionsWithNonExistTemplate(t *testing.T) {
n := Notification{}

Expand All @@ -73,6 +217,116 @@ func TestBuildMessageOptionsWithNonExistTemplate(t *testing.T) {
assert.Equal(t, slackutil.Post, sn.DeliveryPolicy)
}

func TestBuildMessageOptions_IconURL(t *testing.T) {
t.Run("Valid icon URL from notification", func(t *testing.T) {
n := Notification{
Message: "test",
Slack: &SlackNotification{
Icon: "https://example.com/icon.png",
},
}

_, opts, err := buildMessageOptions(n, SlackOptions{})
assert.NoError(t, err)
// Should have text + icon_url options
assert.GreaterOrEqual(t, len(opts), 2)
})

t.Run("Valid icon URL from options", func(t *testing.T) {
n := Notification{
Message: "test",
}

_, opts, err := buildMessageOptions(n, SlackOptions{
Icon: "http://example.com/icon.png",
})
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(opts), 2)
})

t.Run("Invalid icon - neither emoji nor URL", func(t *testing.T) {
n := Notification{
Message: "test",
Slack: &SlackNotification{
Icon: "invalid-icon",
},
}

_, opts, err := buildMessageOptions(n, SlackOptions{})
assert.NoError(t, err)
// Should have text + attachments + blocks (but no icon option because it's invalid)
// Use GreaterOrEqual to make test less fragile to implementation changes
assert.GreaterOrEqual(t, len(opts), 3)
})
}

func TestBuildMessageOptions_DisableUnfurl(t *testing.T) {
n := Notification{
Message: "test",
}

_, opts, err := buildMessageOptions(n, SlackOptions{
DisableUnfurl: true,
})
assert.NoError(t, err)
// Should have text + 2 unfurl options
assert.GreaterOrEqual(t, len(opts), 3)
}

func TestBuildMessageOptions_InvalidAttachments(t *testing.T) {
n := Notification{
Message: "test",
Slack: &SlackNotification{
Attachments: "invalid json",
},
}

_, _, err := buildMessageOptions(n, SlackOptions{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to unmarshal attachments")
}

func TestBuildMessageOptions_InvalidBlocks(t *testing.T) {
n := Notification{
Message: "test",
Slack: &SlackNotification{
Blocks: "invalid json",
},
}

_, _, err := buildMessageOptions(n, SlackOptions{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to unmarshal blocks")
}

func TestGetSigningSecret(t *testing.T) {
service := NewSlackService(SlackOptions{
Token: "test-token",
SigningSecret: "test-signing-secret",
})

slackService, ok := service.(*slackService)
assert.True(t, ok)
assert.Equal(t, "test-signing-secret", slackService.GetSigningSecret())
}

func TestNewSlackClient_CustomAPIURL(t *testing.T) {
client, err := newSlackClient(SlackOptions{
Token: "test-token",
ApiURL: "https://custom.slack.com/api/",
})
assert.NoError(t, err)
assert.NotNil(t, client)
}

func TestNewSlackClient_DefaultAPIURL(t *testing.T) {
client, err := newSlackClient(SlackOptions{
Token: "test-token",
})
assert.NoError(t, err)
assert.NotNil(t, client)
}

type chatResponseFull struct {
Channel string `json:"channel"`
Timestamp string `json:"ts"` // Regular message timestamp
Expand Down Expand Up @@ -319,3 +573,22 @@ func TestSlack_SetUsernameAndIcon(t *testing.T) {
}
})
}

func TestSlack_SendNotification_WithInvalidJSON(t *testing.T) {
service := NewSlackService(SlackOptions{
Token: "something-token",
InsecureSkipVerify: true,
})

err := service.Send(
Notification{
Message: "test",
Slack: &SlackNotification{
Attachments: "invalid json",
},
},
Destination{Recipient: "test", Service: "slack"},
)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to unmarshal")
}
Loading
Loading