Skip to content

Commit

Permalink
Refactor test code
Browse files Browse the repository at this point in the history
  • Loading branch information
mszostok committed Jan 29, 2024
1 parent 1bc5e66 commit 316a195
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 412 deletions.
2 changes: 1 addition & 1 deletion test/botkubex/botkube_cli_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func Install(t *testing.T, params InstallParams) error {

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
return cmd.Run()
}

func Uninstall(t *testing.T, binaryPath string) {
Expand Down
2 changes: 1 addition & 1 deletion test/commplatform/discord_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (d *DiscordTester) TesterUserID() string {
return d.testerUserID
}

func (d *DiscordTester) Channel() Channel {
func (d *DiscordTester) FirstChannel() Channel {
return d.channel
}

Expand Down
2 changes: 1 addition & 1 deletion test/commplatform/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type BotDriver interface {
WaitForInteractiveMessagePosted(userID, channelID string, limitMessages int, assertFn MessageAssertion) error
WaitForMessagePostedWithFileUpload(userID, channelID string, assertFn FileUploadAssertion) error
WaitForMessagePostedWithAttachment(userID, channel string, limitMessages int, expInput ExpAttachmentInput) error
Channel() Channel
FirstChannel() Channel
SecondChannel() Channel
ThirdChannel() Channel
BotName() string
Expand Down
4 changes: 2 additions & 2 deletions test/commplatform/slack_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (s *SlackTester) TesterUserID() string {
return s.testerUserID
}

func (s *SlackTester) Channel() Channel {
func (s *SlackTester) FirstChannel() Channel {
return s.channel
}

Expand Down Expand Up @@ -204,7 +204,7 @@ func (s *SlackTester) WaitForLastMessageEqual(userID, channelID, expectedMsg str
// AssertEquals checks if message is equal to expected message
func (s *SlackTester) AssertEquals(expectedMsg string) MessageAssertion {
return func(msg string) (bool, int, string) {
msg = formatx.RemoveHyperlinks(msg) // normalize the message URLs
msg = formatx.RemoveHyperlinks(msg) // normalize the message URLs
//msg = strings.NewReplacer("<https", "https", ">\n", "\n").Replace(msg)
msg = strings.ReplaceAll(msg, slackInteractiveElementsMsgSuffix, "") // remove interactive elements suffix
if !strings.EqualFold(expectedMsg, msg) {
Expand Down
221 changes: 87 additions & 134 deletions test/commplatform/teams_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ type TeamsConfig struct {
BotTesterAppID string
BotTesterAppPassword string

OrganizationTenantID string
OrganizationTeamID string
OrganizationTenantID string
OrganizationTeamID string
OrganizationGeneralThreadID string `envconfig:"default=19:[email protected]"`
}

type TeamsChannel struct {
Expand Down Expand Up @@ -89,13 +90,15 @@ func (s *TeamsTester) ReplaceBotNamePlaceholder(msg *interactive.CoreMessage, cl
}

func NewTeamsTester(teamsCfg TeamsConfig, apiKey *string) (*TeamsTester, error) {
teamsCli, err := msteamsx.New(teamsCfg.BotTesterAppID, teamsCfg.BotTesterAppPassword, teamsCfg.OrganizationTenantID)
teamsCli, err := msteamsx.NewClient(teamsCfg.BotTesterName, teamsCfg.BotTesterAppID, teamsCfg.BotTesterAppPassword, teamsCfg.OrganizationTenantID)
if err != nil {
return nil, err
}

pubSubClient, err := pubsub.New(context.Background(), false)

if err != nil {
return nil, err
}
return &TeamsTester{
cli: teamsCli,
cfg: teamsCfg,
Expand All @@ -106,16 +109,6 @@ func NewTeamsTester(teamsCfg TeamsConfig, apiKey *string) (*TeamsTester, error)
}, nil
}

// Shutdown performs the shutdown of the dispatcher.
func (s *TeamsTester) Shutdown() error {
//s.log.Info("Shutting down event dispatcher...")
err := s.pubSubClient.Instance.Close()
if err != nil {
return errx.Wrap(err, "while closing pub/sub instance")
}
return nil
}

// AgentEvent is the event being sent by Agent either as a new notification or executor response.
// This is used in processor, passed through Pub/Sub.
type AgentEvent struct {
Expand Down Expand Up @@ -147,12 +140,15 @@ func (s *TeamsTester) InitUsers(t *testing.T) {
}

func (s *TeamsTester) InitChannels(t *testing.T) []func() {
channels, err := s.cli.GetChannels(context.Background(), s.cfg.OrganizationTeamID)
assert.NoError(t, err)
for _, i := range channels {
err := s.cli.DeleteChannel(context.Background(), s.cfg.OrganizationTeamID, i)
assert.NoError(t, err)
}
t.Helper()

//channels, err := s.cli.GetChannels(context.Background(), s.cfg.OrganizationTeamID)
//assert.NoError(t, err)
//for _, i := range channels {
// err := s.cli.DeleteChannel(context.Background(), s.cfg.OrganizationTeamID, i)
// assert.NoError(t, err)
//}

firstChannel, cleanupFirstChannelFn := s.CreateChannel(t, "first")
s.firstChannel = firstChannel

Expand Down Expand Up @@ -185,7 +181,7 @@ func (s *TeamsTester) TesterUserID() string {
return s.cfg.BotTesterName
}

func (s *TeamsTester) Channel() Channel {
func (s *TeamsTester) FirstChannel() Channel {
return s.firstChannel
}

Expand Down Expand Up @@ -216,7 +212,7 @@ func (s *TeamsTester) PostInitialMessage(t *testing.T, channelName string) {

func (s *TeamsTester) PostMessageToBot(t *testing.T, channel, command string) {
ctx := context.Background()
msgText := fmt.Sprintf("<at>%s</at> %s", s.cfg.BotDevName, command)
msgText := fmt.Sprintf("%s %s", s.BotName(), command)
activity := schema.Activity{
Type: schema.Message,
ServiceURL: serviceURL,
Expand All @@ -230,7 +226,7 @@ func (s *TeamsTester) PostMessageToBot(t *testing.T, channel, command string) {
"id": channel,
},
"team": map[string]string{
"id": "19:[email protected]",
"id": s.cfg.OrganizationGeneralThreadID,
},
},
From: schema.ChannelAccount{
Expand All @@ -239,7 +235,6 @@ func (s *TeamsTester) PostMessageToBot(t *testing.T, channel, command string) {
},
}

// message
err := s.cli.SendMessage(ctx, channel, msgText)
assert.NoError(t, err)
err = s.publishBotActivityIntoPubSub(t, ctx, activity)
Expand All @@ -250,52 +245,13 @@ func (s *TeamsTester) InviteBotToChannel(t *testing.T, channelID string) {
t.Logf("No need to invite bot for channel %q since bot is added in Team level...", channelID)
}

// FIXME: Valid ones

func (s *TeamsTester) WaitForMessagePostedRecentlyEqual(userID, channelID, expectedMsg string) error {
// TODO: unify with InteractivePosted
msg := api.NewPlaintextMessage(expectedMsg, false)
_, card, _ := s.renderer.RenderCoreMessageCardAndOptions(interactive.CoreMessage{Message: msg}, s.cfg.BotDevName)
card.MsTeams.Entities = nil

expMsg, err := json.Marshal(card)
if err != nil {
return err
}
opts := jsondiff.DefaultConsoleOptions()
opts.SkipMatches = true
return s.WaitForInteractiveMessagePosted(userID, channelID, s.cfg.RecentMessagesLimit, func(msg string) (bool, int, string) {
gotMsg := strings.NewReplacer(`<at id=\"0\">`, "", "<at>", "", "</at>", "").Replace(msg)
ok, msgDiff := jsondiff.Compare(expMsg, []byte(gotMsg), ptr.FromType(opts))
if ok != jsondiff.FullMatch {
return false, 1, msgDiff
}

return true, 0, ""
})
}

func (s *TeamsTester) WaitForLastMessageContains(userID, channelID, expectedMsgSubstring string) error {
return s.WaitForInteractiveMessagePosted(userID, channelID, 1, func(msg string) (bool, int, string) {
msg, expectedMsgSubstring = NormalizeTeamsWhitespacesInMessages(msg, expectedMsgSubstring)
return strings.Contains(msg, expectedMsgSubstring), 0, ""
})
}

// NormalizeTeamsWhitespacesInMessages normalizes messages, as the Teams renderer uses different line breaks in order to make the message
// more readable. It's hard to come up with a single message that matches all our communication platforms so
// this makes sure that we're normalizing the message to a single line break.
//
// We can consider enchantment in the future, and replace the expectedMsg string with api.Message to allow using dedicated MD renderers in each platform.
func NormalizeTeamsWhitespacesInMessages(got, exp string) (string, string) {
got = strings.ReplaceAll(got, "\n\n", "\n")
got = strings.ReplaceAll(got, "\n\n\n", "\n")

exp = strings.ReplaceAll(exp, "\n\n", "\n")
exp = strings.ReplaceAll(exp, "\n\n\n", "\n")
return got, exp
}

func (s *TeamsTester) WaitForLastMessageEqual(userID, channelID, expectedMsg string) error {
limitMessages := 1
if len(strings.Split(expectedMsg, "\n")) > lineLimitToShowFilter {
Expand All @@ -305,27 +261,18 @@ func (s *TeamsTester) WaitForLastMessageEqual(userID, channelID, expectedMsg str
return s.WaitForInteractiveMessagePosted(userID, channelID, limitMessages, s.AssertEquals(expectedMsg))
}

// AssertEquals checks if message is equal to expected message.
func (s *TeamsTester) AssertEquals(expectedMsg string) MessageAssertion {
return func(msg string) (bool, int, string) {
msg, expectedMsg = NormalizeTeamsWhitespacesInMessages(msg, expectedMsg)

if !strings.EqualFold(expectedMsg, msg) {
count := diff.CountMatchBlock(expectedMsg, msg)
msgDiff := diff.Diff(expectedMsg, msg)
return false, count, msgDiff
}
return true, 0, ""
}
}

func (s *TeamsTester) WaitForMessagePosted(userID, channelID string, limitMessages int, assertFn MessageAssertion) error {
return s.WaitForInteractiveMessagePosted(userID, channelID, limitMessages, assertFn)
}

func (s *TeamsTester) WaitForMessagePostedRecentlyEqual(userID, channelID, expectedMsg string) error {
msg := api.NewPlaintextMessage(expectedMsg, false)
return s.waitForAdaptiveCardMessage(userID, channelID, s.cfg.RecentMessagesLimit, interactive.CoreMessage{Message: msg})
}

func (s *TeamsTester) WaitForInteractiveMessagePosted(userID, channelID string, limitMessages int, assertFn MessageAssertion) error {
var (
fetchedMessages []msteamsx.MsTeamsMessage
fetchedMessages []msteamsx.Message
diffMessage string
lastErr error
)
Expand Down Expand Up @@ -374,14 +321,8 @@ func (s *TeamsTester) WaitForInteractiveMessagePosted(userID, channelID string,
return nil
}

// FIXME: Valid ones -- end

func (s *TeamsTester) WaitForMessagePostedWithFileUpload(_, _ string, _ FileUploadAssertion) error {
return errors.New("not implemented")
}

func (s *TeamsTester) WaitForMessagePostedWithAttachment(userID, channelID string, limitMessages int, expAttachment ExpAttachmentInput) error {
// for now we don't compare times
// for now, we don't compare times
expAttachment.Message.Timestamp = time.Time{}

_, card, _ := s.renderer.RenderCoreMessageCardAndOptions(interactive.CoreMessage{Message: expAttachment.Message}, s.cfg.BotDevName)
Expand All @@ -391,34 +332,12 @@ func (s *TeamsTester) WaitForMessagePostedWithAttachment(userID, channelID strin
if err != nil {
return err
}
opts := jsondiff.DefaultConsoleOptions()
opts.SkipMatches = true
return s.WaitForInteractiveMessagePosted(userID, channelID, limitMessages, func(msg string) (bool, int, string) {
gotMsg := strings.NewReplacer(`<at id=\"0\">`, "", "</at>", "", "<at>", "").Replace(msg)
gotMsg, err := filterDatesObjects(gotMsg)
gotMsg, err := filterDatesObjects(msg)
if err != nil {
return false, 1, err.Error()
}
ok, msgDiff := jsondiff.Compare(expMsg, []byte(gotMsg), ptr.FromType(opts))
switch ok {
// SupersetMatch is used as sometimes we sent more details than is returned by Teams API, e.g.:
// we sent:
// {
// "type": "TableColumnDefinition",
// "width": 1,
// "horizontalCellContentAlignment": "left",
// "verticalCellContentAlignment": "bottom"
// }
// while API returns:
// {
// "verticalCellContentAlignment": "bottom",
// "width": 1
// }
case jsondiff.FullMatch, jsondiff.SupersetMatch:
return true, 0, ""
default:
return false, 1, msgDiff
}
return s.assertJSONEqual(expMsg, gotMsg)
})
}

Expand All @@ -434,6 +353,10 @@ func (s *TeamsTester) WaitForLastInteractiveMessagePostedEqualWithCustomRender(_
return errors.New("not implemented")
}

func (s *TeamsTester) WaitForMessagePostedWithFileUpload(_, _ string, _ FileUploadAssertion) error {
return errors.New("not implemented")
}

func (s *TeamsTester) SetTimeout(timeout time.Duration) {
s.cfg.MessageWaitTimeout = timeout
}
Expand Down Expand Up @@ -464,7 +387,33 @@ func (s *TeamsTester) CreateChannel(t *testing.T, prefix string) (Channel, func(
return &TeamsChannel{id: channelID, name: channelName}, cleanupFn
}

// private
// AssertEquals checks if message is equal to expected message.
func (s *TeamsTester) AssertEquals(expectedMsg string) MessageAssertion {
return func(msg string) (bool, int, string) {
msg, expectedMsg = NormalizeTeamsWhitespacesInMessages(msg, expectedMsg)

if !strings.EqualFold(expectedMsg, msg) {
count := diff.CountMatchBlock(expectedMsg, msg)
msgDiff := diff.Diff(expectedMsg, msg)
return false, count, msgDiff
}
return true, 0, ""
}
}

// NormalizeTeamsWhitespacesInMessages normalizes messages, as the Teams renderer uses different line breaks in order to make the message
// more readable. It's hard to come up with a single message that matches all our communication platforms so
// this makes sure that we're normalizing the message to a single line break.
//
// We can consider enchantment in the future, and replace the expectedMsg string with api.Message to allow using dedicated MD renderers in each platform.
func NormalizeTeamsWhitespacesInMessages(got, exp string) (string, string) {
got = strings.ReplaceAll(got, "\n\n", "\n")
got = strings.ReplaceAll(got, "\n\n\n", "\n")

exp = strings.ReplaceAll(exp, "\n\n", "\n")
exp = strings.ReplaceAll(exp, "\n\n\n", "\n")
return got, exp
}

func (s *TeamsTester) waitForAdaptiveCardMessage(userID, channelID string, limitMessages int, msg interactive.CoreMessage) error {
_, card, _ := s.renderer.RenderCoreMessageCardAndOptions(msg, s.cfg.BotDevName)
Expand All @@ -474,34 +423,38 @@ func (s *TeamsTester) waitForAdaptiveCardMessage(userID, channelID string, limit
if err != nil {
return err
}
opts := jsondiff.DefaultConsoleOptions()
opts.SkipMatches = true
return s.WaitForInteractiveMessagePosted(userID, channelID, limitMessages, func(msg string) (bool, int, string) {
gotMsg := strings.NewReplacer(`<at id=\"0\">`, "", "</at>", "", "<at>", "").Replace(msg)

ok, msgDiff := jsondiff.Compare(expMsg, []byte(gotMsg), ptr.FromType(opts))
switch ok {
// SupersetMatch is used as sometimes we sent more details than is returned by Teams API, e.g.:
// we send:
// {
// "type": "TableColumnDefinition",
// "width": 1,
// "horizontalCellContentAlignment": "left",
// "verticalCellContentAlignment": "bottom"
// }
// while API returns:
// {
// "verticalCellContentAlignment": "bottom",
// "width": 1
// }
case jsondiff.FullMatch, jsondiff.SupersetMatch:
return true, 0, ""
default:
return false, 1, msgDiff
}
return s.assertJSONEqual(expMsg, msg)
})
}

func (s *TeamsTester) assertJSONEqual(exp []byte, got string) (bool, int, string) {
opts := jsondiff.DefaultConsoleOptions()
opts.SkipMatches = true
gotMsg := strings.NewReplacer(`<at id=\"0\">`, "", "<at>", "", "</at>", "").Replace(got)

diffType, diffMsg := jsondiff.Compare(exp, []byte(gotMsg), ptr.FromType(opts))
switch diffType {
// SupersetMatch is used as sometimes we sent more details than is returned by Teams API, e.g.:
// we sent:
// {
// "type": "TableColumnDefinition",
// "width": 1,
// "horizontalCellContentAlignment": "left",
// "verticalCellContentAlignment": "bottom"
// }
// while API returns:
// {
// "verticalCellContentAlignment": "bottom",
// "width": 1
// }
case jsondiff.FullMatch, jsondiff.SupersetMatch:
return true, 0, ""
default:
return false, 1, diffMsg
}
}

func filterDatesObjects(adaptiveCard string) (string, error) {
var event map[string]any
err := json.Unmarshal([]byte(adaptiveCard), &event)
Expand Down
Loading

0 comments on commit 316a195

Please sign in to comment.