diff --git a/plugins/slack-notification-plugin/slack/client.go b/plugins/slack-notification-plugin/slack/client.go new file mode 100644 index 00000000..3fca4173 --- /dev/null +++ b/plugins/slack-notification-plugin/slack/client.go @@ -0,0 +1,34 @@ +package slack + +import ( + "github.com/go-resty/resty/v2" +) + +type SlackClient interface { + Publish(message Message) error +} + +type slackClient struct { + client *resty.Client + config SlackConfig +} + +func (s *slackClient) Publish(message Message) error { + messageJson, err := message.JSON() + if err != nil { + return err + } + path := s.config.url + _, err = s.client.R(). + SetBody(messageJson). + SetHeader("Content-Type", "application/json"). + Post(path) + return err +} + +func NewSlackClient(client *resty.Client) SlackClient { + return &slackClient{ + client: client, + config: NewSlackConfig(), + } +} diff --git a/plugins/slack-notification-plugin/slack/client_test.go b/plugins/slack-notification-plugin/slack/client_test.go new file mode 100644 index 00000000..d4877fe6 --- /dev/null +++ b/plugins/slack-notification-plugin/slack/client_test.go @@ -0,0 +1,102 @@ +package slack + +import ( + "errors" + "io/ioutil" + "net/http" + "testing" + + "github.com/go-resty/resty/v2" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" +) + +type slackClientTest struct { + client SlackClient +} + +func (context *slackClientTest) setUp(t *testing.T) { + httpClient := resty.New() + httpmock.ActivateNonDefault(httpClient.GetClient()) + context.client = NewSlackClient(httpClient) + assert.NotNil(t, context.client) +} + +func (context *slackClientTest) tearDown() { + httpmock.DeactivateAndReset() +} + +func newNotificationServiceTestContext() *slackClientTest { + return &slackClientTest{} +} + +func TestSlackClient_Publish(t *testing.T) { + ctx := newNotificationServiceTestContext() + ctx.setUp(t) + defer ctx.tearDown() + + config := NewSlackConfig() + message := MessageMock{} + message.On("JSON").Return("message sent", nil) + + httpmock.RegisterResponder( + "POST", + config.url, + func(req *http.Request) (*http.Response, error) { + body, err := ioutil.ReadAll(req.Body) + assert.NoError(t, err) + assert.Equal(t, "message sent", string(body)) + + contentType := req.Header.Get("Content-type") + assert.Equal(t, "application/json", contentType) + + response := httpmock.NewStringResponse(200, "") + return response, nil + }, + ) + err := ctx.client.Publish(&message) + assert.NoError(t, err) + assert.Equal(t, 1, httpmock.GetTotalCallCount()) +} + +func TestSlackClient_PublishErrorJSON(t *testing.T) { + ctx := newNotificationServiceTestContext() + ctx.setUp(t) + defer ctx.tearDown() + + message := MessageMock{} + message.On("JSON").Return("", errors.New("JSON unmarshal error")).Once() + + err := ctx.client.Publish(&message) + assert.Error(t, err) + assert.Equal(t, 0, httpmock.GetTotalCallCount()) +} + +func TestSlackClient_PublishErrorRequest(t *testing.T) { + ctx := newNotificationServiceTestContext() + ctx.setUp(t) + defer ctx.tearDown() + + config := NewSlackConfig() + message := MessageMock{} + message.On("JSON").Return("message sent", nil) + + httpmock.RegisterResponder( + "POST", + config.url, + func(req *http.Request) (*http.Response, error) { + body, err := ioutil.ReadAll(req.Body) + assert.NoError(t, err) + assert.Equal(t, "message sent", string(body)) + + contentType := req.Header.Get("Content-type") + assert.Equal(t, "application/json", contentType) + + response := httpmock.NewStringResponse(503, "") + return response, errors.New("internal server error") + }, + ) + err := ctx.client.Publish(&message) + assert.Error(t, err) + assert.Equal(t, 1, httpmock.GetTotalCallCount()) +} diff --git a/plugins/slack-notification-plugin/slack/config.go b/plugins/slack-notification-plugin/slack/config.go new file mode 100644 index 00000000..da21e17f --- /dev/null +++ b/plugins/slack-notification-plugin/slack/config.go @@ -0,0 +1,18 @@ +package slack + +import "github.com/spf13/viper" + +type SlackConfig struct { + url string +} + +func NewSlackConfig() SlackConfig { + fang := viper.New() + fang.AutomaticEnv() + fang.SetEnvPrefix("SLACK_PLUGIN") + fang.SetDefault("URL", "https://hooks.slack.com/services") + config := SlackConfig{ + url: fang.GetString("URL"), + } + return config +} diff --git a/plugins/slack-notification-plugin/slack/message.go b/plugins/slack-notification-plugin/slack/message.go new file mode 100644 index 00000000..60f70b16 --- /dev/null +++ b/plugins/slack-notification-plugin/slack/message.go @@ -0,0 +1,5 @@ +package slack + +type Message interface { + JSON() (string, error) +} diff --git a/plugins/slack-notification-plugin/slack/message_mock.go b/plugins/slack-notification-plugin/slack/message_mock.go new file mode 100644 index 00000000..20b3267f --- /dev/null +++ b/plugins/slack-notification-plugin/slack/message_mock.go @@ -0,0 +1,14 @@ +package slack + +import ( + "github.com/stretchr/testify/mock" +) + +type MessageMock struct { + mock.Mock +} + +func (m *MessageMock) JSON() (string, error) { + args := m.Called() + return args.Get(0).(string), args.Error(1) +}