Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utility function for validating slack integrations #1114

Merged
merged 1 commit into from May 5, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Expand Up @@ -363,6 +363,7 @@ workflows:
- test:
context:
- Gruntwork Admin
- Slack Token For Test
requires:
- setup
filters:
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Expand Up @@ -48,6 +48,8 @@ require (
k8s.io/client-go v0.20.6
)

require github.com/slack-go/slack v0.10.3

require (
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
Expand All @@ -72,10 +74,11 @@ require (
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gnostic v0.4.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
Expand Down
7 changes: 6 additions & 1 deletion go.sum
Expand Up @@ -354,6 +354,7 @@ github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZp
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
Expand Down Expand Up @@ -419,8 +420,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-containerregistry v0.6.0 h1:niQ+8XD//kKgArIFwDVBXsWVWbde16LPdHMyNwSC8h4=
github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -461,6 +463,7 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
Expand Down Expand Up @@ -739,6 +742,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/slack-go/slack v0.10.3 h1:kKYwlKY73AfSrtAk9UHWCXXfitudkDztNI9GYBviLxw=
github.com/slack-go/slack v0.10.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
Expand Down
2 changes: 2 additions & 0 deletions modules/slack/doc.go
@@ -0,0 +1,2 @@
// Package slack contains routines useful for testing slack integrations.
package slack
98 changes: 98 additions & 0 deletions modules/slack/validate.go
@@ -0,0 +1,98 @@
package slack

import (
"fmt"
"strconv"
"strings"
"time"

"github.com/gruntwork-io/terratest/modules/testing"
"github.com/slack-go/slack"
)

// ValidateExpectedSlackMessage validates whether a message containing the expected text was posted in the given channel
// ID, looking back historyLimit messages up to the given duration. For example, if you set (15*time.Minute) as the
// lookBack parameter with historyLimit set to 50, then this will look back the last 50 messages, up to 15 minutes ago.
// This expects a slack token to be provided. This returns MessageNotFoundErr when there is no match.
// For the purposes of matching, this only checks the following blocks:
// - Section block text
// - Header block text
// All other blocks are ignored in the validation.
// NOTE: This only looks for bot posted messages.
func ValidateExpectedSlackMessageE(
t testing.TestingT,
token,
channelID,
expectedText string,
historyLimit int,
lookBack time.Duration,
) error {
lookBackTime := time.Now().Add(-1 * lookBack)
slackClt := slack.New(token)
params := slack.GetConversationHistoryParameters{
ChannelID: channelID,
Limit: historyLimit,
Oldest: strconv.FormatInt(lookBackTime.Unix(), 10),
}

resp, err := slackClt.GetConversationHistory(&params)
if err != nil {
return err
}

for _, msg := range resp.Messages {
if checkMessageContainsText(msg.Msg, expectedText) {
return nil
}

if msg.SubMessage != nil {
if checkMessageContainsText(*msg.SubMessage, expectedText) {
return nil
}
}
}
return fmt.Errorf("still no message")
}

func checkMessageContainsText(msg slack.Msg, expectedText string) bool {
// If this message is not a bot message, ignore.
if msg.Type != slack.MsgSubTypeBotMessage && msg.BotID == "" {
return false
}

// Check message text
if strings.Contains(msg.Text, expectedText) {
return true
}

// Check attachments
for _, attachment := range msg.Attachments {
if strings.Contains(attachment.Text, expectedText) {
return true
}
}

// Check blocks
for _, block := range msg.Blocks.BlockSet {
switch block.BlockType() {
case slack.MBTSection:
sectionBlk := block.(*slack.SectionBlock)
if sectionBlk.Text != nil && strings.Contains(sectionBlk.Text.Text, expectedText) {
return true
}
case slack.MBTHeader:
headerBlk := block.(*slack.HeaderBlock)
if headerBlk.Text != nil && strings.Contains(headerBlk.Text.Text, expectedText) {
return true
}
}
}

return false
}

type MessageNotFoundErr struct{}

func (err MessageNotFoundErr) Error() string {
return "Could not find the expected text in any of the messages posted in the given channel."
}
51 changes: 51 additions & 0 deletions modules/slack/validate_test.go
@@ -0,0 +1,51 @@
package slack

import (
"fmt"
"os"
"testing"
"time"

"github.com/slack-go/slack"
"github.com/stretchr/testify/require"

"github.com/gruntwork-io/terratest/modules/environment"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/retry"
)

const (
slackTokenEnv = "SLACK_TOKEN_FOR_TEST"
slackChannelIDEnv = "SLACK_CHANNEL_ID_FOR_TEST"
)

func TestValidateSlackMessage(t *testing.T) {
t.Parallel()

environment.RequireEnvVar(t, slackTokenEnv)
environment.RequireEnvVar(t, slackChannelIDEnv)

token := os.Getenv(slackTokenEnv)
channelID := os.Getenv(slackChannelIDEnv)

uniqueID := random.UniqueId()
msgTxt := fmt.Sprintf("Test message from terratest: %s", uniqueID)

slackClt := slack.New(token)

_, _, err := slackClt.PostMessage(
channelID,
slack.MsgOptionText(msgTxt, false),
)
require.NoError(t, err)

retry.DoWithRetry(
t,
"wait for slack message",
6, 10*time.Second,
func() (string, error) {
err := ValidateExpectedSlackMessageE(t, token, channelID, msgTxt, 10, 5*time.Minute)
return "", err
},
)
}