1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-04 03:11:55 +02:00
goreleaser/pkg/config/config_slack_test.go
fredbi 905a1640f1
feat(announce): added Slack notification options (#2988)
* feat(announce): added Slack notification options

This feature adds support for specifying a richer content in Slack
announcements. We may now specify "blocks" and "attachments" to produce
better-looking announcement messages.

* fixes #2986

The goreleaser configuration only exposes the top-level structures and does not
check the validity of the Slack API internal structures. This way, we do
not inject hard dependencies on changes in the Slack API.

Notice: untyped config parsing introduces a little hack to have yaml and
JSON marshaling work together properly. This hack won't be necessary
with yaml.v3.

How this has been tested?
-------------------------

* Added unit tests for the config parsing
* Added a (skipped) e2e test.
  For now, this requires a valid Slack webhook, so I've been able to test this manually.

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

* added more unit tests

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

* removed yaml.v2 hack

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2022-03-30 09:42:59 -03:00

247 lines
5.2 KiB
Go

package config
import (
"bytes"
"encoding/json"
"errors"
"io"
"testing"
"github.com/stretchr/testify/require"
)
func TestUnmarshalSlackBlocks(t *testing.T) {
t.Parallel()
t.Run("valid blocks", func(t *testing.T) {
t.Parallel()
prop, err := LoadReader(goodBlocksSlackConf())
require.NoError(t, err)
expectedBlocks := []SlackBlock{
{
Internal: map[string]interface{}{
"type": "header",
"text": map[string]interface{}{
"type": "plain_text",
"text": "{{ .Version }}",
},
},
},
{
Internal: map[string]interface{}{
"text": map[string]interface{}{
"type": "mrkdwn",
"text": "Heading\n=======\n\n**Bold**\n",
},
"type": "section",
},
},
}
// assert Unmarshal from YAML
require.Equal(t, expectedBlocks, prop.Announce.Slack.Blocks)
jazon, err := json.Marshal(prop.Announce.Slack.Blocks)
require.NoError(t, err)
var untyped []SlackBlock
require.NoError(t, json.Unmarshal(jazon, &untyped))
// assert that JSON Marshal didn't alter the struct
require.Equal(t, expectedBlocks, prop.Announce.Slack.Blocks)
})
t.Run("invalid blocks", func(t *testing.T) {
t.Parallel()
_, err := LoadReader(badBlocksSlackConf())
require.Error(t, err)
})
}
func TestUnmarshalSlackAttachments(t *testing.T) {
t.Parallel()
t.Run("valid attachments", func(t *testing.T) {
t.Parallel()
prop, err := LoadReader(goodAttachmentsSlackConf())
require.NoError(t, err)
expectedAttachments := []SlackAttachment{
{
Internal: map[string]interface{}{
"color": "#46a64f",
"fields": []interface{}{
map[string]interface{}{
"short": false,
"title": "field 1",
"value": "value 1",
},
},
"footer": "a footer",
"mrkdwn_in": []interface{}{
"text",
},
"pretext": "optional",
"text": "another",
"title": "my_title",
},
},
}
// assert Unmarshal from YAML
require.Equal(t, expectedAttachments, prop.Announce.Slack.Attachments)
jazon, err := json.Marshal(prop.Announce.Slack.Attachments)
require.NoError(t, err)
var untyped []SlackAttachment
require.NoError(t, json.Unmarshal(jazon, &untyped))
// assert that JSON Marshal didn't alter the struct
require.Equal(t, expectedAttachments, prop.Announce.Slack.Attachments)
})
t.Run("invalid attachments", func(t *testing.T) {
t.Parallel()
_, err := LoadReader(badAttachmentsSlackConf())
require.Error(t, err)
})
}
func TestUnmarshalYAMLSlackBlocks(t *testing.T) {
// func (a *SlackAttachment) UnmarshalYAML(unmarshal func(interface{}) error) error {
t.Parallel()
const testError = "testError"
erf := func(_ interface{}) error {
return errors.New(testError)
}
t.Run("SlackBlock.UnmarshalYAML error case", func(t *testing.T) {
t.Parallel()
var block SlackBlock
err := block.UnmarshalYAML(erf)
require.Error(t, err)
require.ErrorContains(t, err, testError)
})
t.Run("SlackAttachment.UnmarshalYAML error case", func(t *testing.T) {
t.Parallel()
var attachment SlackAttachment
err := attachment.UnmarshalYAML(erf)
require.Error(t, err)
require.ErrorContains(t, err, testError)
})
}
func goodBlocksSlackConf() io.Reader {
const conf = `
announce:
slack:
enabled: true
username: my_user
message_template: fallback
channel: my_channel
blocks:
- type: header
text:
type: plain_text
text: '{{ .Version }}'
- type: section
text:
type: mrkdwn
text: |
Heading
=======
**Bold**
`
buf := bytes.NewBufferString(conf)
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
}
func badBlocksSlackConf() io.Reader {
const conf = `
announce:
slack:
enabled: true
username: my_user
message_template: fallback
channel: my_channel
blocks:
type: header
text:
type: plain_text
text: '{{ .Version }}'
type: section
text:
type: mrkdwn
text: |
**Bold**
`
buf := bytes.NewBufferString(conf)
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
}
func goodAttachmentsSlackConf() io.Reader {
const conf = `
announce:
slack:
enabled: true
username: my_user
message_template: fallback
channel: my_channel
attachments:
- mrkdwn_in: ["text"]
color: '#46a64f'
pretext: optional
title: my_title
text: another
fields:
- title: field 1
value: value 1
short: false
footer: a footer
`
buf := bytes.NewBufferString(conf)
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
}
func badAttachmentsSlackConf() io.Reader {
const conf = `
announce:
slack:
enabled: true
username: my_user
message_template: fallback
channel: my_channel
attachments:
key:
mrkdwn_in: ["text"]
color: #46a64f
pretext: optional
title: my_title
text: another
fields:
- title: field 1
value: value 1
short: false
footer: a footer
`
buf := bytes.NewBufferString(conf)
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
}