summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/notification.go206
-rw-r--r--app/notification_test.go524
2 files changed, 338 insertions, 392 deletions
diff --git a/app/notification.go b/app/notification.go
index 62aad4c28..7223fb3aa 100644
--- a/app/notification.go
+++ b/app/notification.go
@@ -10,7 +10,6 @@ import (
"net/http"
"net/url"
"path/filepath"
- "regexp"
"sort"
"strings"
"time"
@@ -20,6 +19,7 @@ import (
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
+ "github.com/mattermost/mattermost-server/utils/markdown"
"github.com/nicksnyder/go-i18n/i18n"
)
@@ -71,8 +71,8 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
} else {
keywords := a.GetMentionKeywordsInChannel(profileMap, post.Type != model.POST_HEADER_CHANGE && post.Type != model.POST_PURPOSE_CHANGE)
- var potentialOtherMentions []string
- mentionedUserIds, potentialOtherMentions, hereNotification, channelNotification, allNotification = GetExplicitMentions(post.Message, keywords)
+ m := GetExplicitMentions(post.Message, keywords)
+ mentionedUserIds, hereNotification, channelNotification, allNotification = m.MentionedUserIds, m.HereMentioned, m.ChannelMentioned, m.AllMentioned
// get users that have comment thread mentions enabled
if len(post.RootId) > 0 && parentPostList != nil {
@@ -89,8 +89,8 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
delete(mentionedUserIds, post.UserId)
}
- if len(potentialOtherMentions) > 0 {
- if result := <-a.Srv.Store.User().GetProfilesByUsernames(potentialOtherMentions, team.Id); result.Err == nil {
+ if len(m.OtherPotentialMentions) > 0 {
+ if result := <-a.Srv.Store.User().GetProfilesByUsernames(m.OtherPotentialMentions, team.Id); result.Err == nil {
outOfChannelMentions := result.Data.([]*model.User)
if channel.Type != model.CHANNEL_GROUP {
a.Go(func() {
@@ -788,125 +788,133 @@ func (a *App) sendOutOfChannelMentions(sender *model.User, post *model.Post, cha
return nil
}
+type ExplicitMentions struct {
+ // MentionedUserIds contains a key for each user mentioned by keyword.
+ MentionedUserIds map[string]bool
+
+ // OtherPotentialMentions contains a list of strings that looked like mentions, but didn't have
+ // a corresponding keyword.
+ OtherPotentialMentions []string
+
+ // HereMentioned is true if the message contained @here.
+ HereMentioned bool
+
+ // AllMentioned is true if the message contained @all.
+ AllMentioned bool
+
+ // ChannelMentioned is true if the message contained @channel.
+ ChannelMentioned bool
+}
+
// Given a message and a map mapping mention keywords to the users who use them, returns a map of mentioned
// users and a slice of potential mention users not in the channel and whether or not @here was mentioned.
-func GetExplicitMentions(message string, keywords map[string][]string) (map[string]bool, []string, bool, bool, bool) {
- mentioned := make(map[string]bool)
- potentialOthersMentioned := make([]string, 0)
+func GetExplicitMentions(message string, keywords map[string][]string) *ExplicitMentions {
+ ret := &ExplicitMentions{
+ MentionedUserIds: make(map[string]bool),
+ }
systemMentions := map[string]bool{"@here": true, "@channel": true, "@all": true}
- hereMentioned := false
- allMentioned := false
- channelMentioned := false
addMentionedUsers := func(ids []string) {
for _, id := range ids {
- mentioned[id] = true
+ ret.MentionedUserIds[id] = true
}
}
- message = removeCodeFromMessage(message)
-
- for _, word := range strings.FieldsFunc(message, func(c rune) bool {
- // Split on any whitespace or punctuation that can't be part of an at mention or emoji pattern
- return !(c == ':' || c == '.' || c == '-' || c == '_' || c == '@' || unicode.IsLetter(c) || unicode.IsNumber(c))
- }) {
- isMention := false
-
- // skip word with format ':word:' with an assumption that it is an emoji format only
- if word[0] == ':' && word[len(word)-1] == ':' {
- continue
- }
-
- if word == "@here" {
- hereMentioned = true
- }
+ processText := func(text string) {
+ for _, word := range strings.FieldsFunc(text, func(c rune) bool {
+ // Split on any whitespace or punctuation that can't be part of an at mention or emoji pattern
+ return !(c == ':' || c == '.' || c == '-' || c == '_' || c == '@' || unicode.IsLetter(c) || unicode.IsNumber(c))
+ }) {
+ isMention := false
- if word == "@channel" {
- channelMentioned = true
- }
-
- if word == "@all" {
- allMentioned = true
- }
-
- // Non-case-sensitive check for regular keys
- if ids, match := keywords[strings.ToLower(word)]; match {
- addMentionedUsers(ids)
- isMention = true
- }
+ // skip word with format ':word:' with an assumption that it is an emoji format only
+ if word[0] == ':' && word[len(word)-1] == ':' {
+ continue
+ }
- // Case-sensitive check for first name
- if ids, match := keywords[word]; match {
- addMentionedUsers(ids)
- isMention = true
- }
+ if word == "@here" {
+ ret.HereMentioned = true
+ }
- if isMention {
- continue
- }
+ if word == "@channel" {
+ ret.ChannelMentioned = true
+ }
- if strings.ContainsAny(word, ".-:") {
- // This word contains a character that may be the end of a sentence, so split further
- splitWords := strings.FieldsFunc(word, func(c rune) bool {
- return c == '.' || c == '-' || c == ':'
- })
+ if word == "@all" {
+ ret.AllMentioned = true
+ }
- for _, splitWord := range splitWords {
- if splitWord == "@here" {
- hereMentioned = true
- }
+ // Non-case-sensitive check for regular keys
+ if ids, match := keywords[strings.ToLower(word)]; match {
+ addMentionedUsers(ids)
+ isMention = true
+ }
- if splitWord == "@all" {
- allMentioned = true
- }
+ // Case-sensitive check for first name
+ if ids, match := keywords[word]; match {
+ addMentionedUsers(ids)
+ isMention = true
+ }
- if splitWord == "@channel" {
- channelMentioned = true
- }
+ if isMention {
+ continue
+ }
- // Non-case-sensitive check for regular keys
- if ids, match := keywords[strings.ToLower(splitWord)]; match {
- addMentionedUsers(ids)
- }
+ if strings.ContainsAny(word, ".-:") {
+ // This word contains a character that may be the end of a sentence, so split further
+ splitWords := strings.FieldsFunc(word, func(c rune) bool {
+ return c == '.' || c == '-' || c == ':'
+ })
- // Case-sensitive check for first name
- if ids, match := keywords[splitWord]; match {
- addMentionedUsers(ids)
- } else if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
- username := splitWord[1:]
- potentialOthersMentioned = append(potentialOthersMentioned, username)
+ for _, splitWord := range splitWords {
+ if splitWord == "@here" {
+ ret.HereMentioned = true
+ }
+
+ if splitWord == "@all" {
+ ret.AllMentioned = true
+ }
+
+ if splitWord == "@channel" {
+ ret.ChannelMentioned = true
+ }
+
+ // Non-case-sensitive check for regular keys
+ if ids, match := keywords[strings.ToLower(splitWord)]; match {
+ addMentionedUsers(ids)
+ }
+
+ // Case-sensitive check for first name
+ if ids, match := keywords[splitWord]; match {
+ addMentionedUsers(ids)
+ } else if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
+ username := splitWord[1:]
+ ret.OtherPotentialMentions = append(ret.OtherPotentialMentions, username)
+ }
}
}
- }
- if _, ok := systemMentions[word]; !ok && strings.HasPrefix(word, "@") {
- username := word[1:]
- potentialOthersMentioned = append(potentialOthersMentioned, username)
+ if _, ok := systemMentions[word]; !ok && strings.HasPrefix(word, "@") {
+ username := word[1:]
+ ret.OtherPotentialMentions = append(ret.OtherPotentialMentions, username)
+ }
}
}
- return mentioned, potentialOthersMentioned, hereMentioned, channelMentioned, allMentioned
-}
-
-// Matches a line containing only ``` and a potential language definition, any number of lines not containing ```,
-// and then either a line containing only ``` or the end of the text
-var codeBlockPattern = regexp.MustCompile("(?m)^[^\\S\n]*[\\`~]{3}.*$[\\s\\S]+?(^[^\\S\n]*[`~]{3}$|\\z)")
-
-// Matches a backquote, either some text or any number of non-empty lines, and then a final backquote
-var inlineCodePattern = regexp.MustCompile("(?m)\\`+(?:.+?|.*?\n(.*?\\S.*?\n)*.*?)\\`+")
-
-// Strips pre-formatted text and code blocks from a Markdown string by replacing them with whitespace
-func removeCodeFromMessage(message string) string {
- if strings.Contains(message, "```") || strings.Contains(message, "~~~") {
- message = codeBlockPattern.ReplaceAllString(message, "")
- }
-
- // Replace with a space to prevent cases like "user`code`name" from turning into "username"
- if strings.Contains(message, "`") {
- message = inlineCodePattern.ReplaceAllString(message, " ")
- }
+ buf := ""
+ markdown.Inspect(message, func(node interface{}) bool {
+ text, ok := node.(*markdown.Text)
+ if !ok {
+ processText(buf)
+ buf = ""
+ return true
+ }
+ buf += text.Text
+ return false
+ })
+ processText(buf)
- return message
+ return ret
}
// Given a map of user IDs to profiles, returns a list of mention
diff --git a/app/notification_test.go b/app/notification_test.go
index c5d0f8478..11f4df685 100644
--- a/app/notification_test.go
+++ b/app/notification_test.go
@@ -7,6 +7,8 @@ import (
"strings"
"testing"
+ "github.com/stretchr/testify/assert"
+
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -82,147 +84,229 @@ func TestGetExplicitMentions(t *testing.T) {
id2 := model.NewId()
id3 := model.NewId()
- // not mentioning anybody
- message := "this is a message"
- keywords := map[string][]string{}
- if mentions, potential, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 0 || len(potential) != 0 {
- t.Fatal("shouldn't have mentioned anybody or have any potencial mentions")
- }
-
- // mentioning a user that doesn't exist
- message = "this is a message for @user"
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 0 {
- t.Fatal("shouldn't have mentioned user that doesn't exist")
- }
-
- // mentioning one person
- keywords = map[string][]string{"@user": {id1}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] {
- t.Fatal("should've mentioned @user")
- }
-
- // mentioning one person without an @mention
- message = "this is a message for @user"
- keywords = map[string][]string{"this": {id1}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] {
- t.Fatal("should've mentioned this")
- }
-
- // mentioning multiple people with one word
- message = "this is a message for @user"
- keywords = map[string][]string{"@user": {id1, id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] {
- t.Fatal("should've mentioned two users with @user")
- }
-
- // mentioning only one of multiple people
- keywords = map[string][]string{"@user": {id1}, "@mention": {id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] {
- t.Fatal("should've mentioned @user and not @mention")
- }
-
- // mentioning multiple people with multiple words
- message = "this is an @mention for @user"
- keywords = map[string][]string{"@user": {id1}, "@mention": {id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] {
- t.Fatal("should've mentioned two users with @user and @mention")
- }
-
- // mentioning @channel (not a special case, but it's good to double check)
- message = "this is an message for @channel"
- keywords = map[string][]string{"@channel": {id1, id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] {
- t.Fatal("should've mentioned two users with @channel")
- }
-
- // mentioning @all (not a special case, but it's good to double check)
- message = "this is an message for @all"
- keywords = map[string][]string{"@all": {id1, id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] {
- t.Fatal("should've mentioned two users with @all")
- }
-
- // mentioning user.period without mentioning user (PLT-3222)
- message = "user.period doesn't complicate things at all by including periods in their username"
- keywords = map[string][]string{"user.period": {id1}, "user": {id2}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] {
- t.Fatal("should've mentioned user.period and not user")
- }
-
- // mentioning a potential out of channel user
- message = "this is an message for @potential and @user"
- keywords = map[string][]string{"@user": {id1}}
- if mentions, potential, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || len(potential) != 1 {
- t.Fatal("should've mentioned user and have a potential not in channel")
- }
-
- // words in inline code shouldn't trigger mentions
- message = "`this shouldn't mention @channel at all`"
- keywords = map[string][]string{}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 0 {
- t.Fatal("@channel in inline code shouldn't cause a mention")
- }
-
- // words in code blocks shouldn't trigger mentions
- message = "```\nthis shouldn't mention @channel at all\n```"
- keywords = map[string][]string{}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 0 {
- t.Fatal("@channel in code block shouldn't cause a mention")
- }
-
- // Markdown-formatted text that isn't code should trigger mentions
- message = "*@aaa @bbb @ccc*"
- keywords = map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 3 || !mentions[id1] || !mentions[id2] || !mentions[id3] {
- t.Fatal("should've mentioned all 3 users", mentions)
- }
-
- message = "**@aaa @bbb @ccc**"
- keywords = map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 3 || !mentions[id1] || !mentions[id2] || !mentions[id3] {
- t.Fatal("should've mentioned all 3 users")
- }
-
- message = "~~@aaa @bbb @ccc~~"
- keywords = map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 3 || !mentions[id1] || !mentions[id2] || !mentions[id3] {
- t.Fatal("should've mentioned all 3 users")
- }
-
- message = "### @aaa"
- keywords = map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] || mentions[id3] {
- t.Fatal("should've only mentioned aaa")
- }
-
- message = "> @aaa"
- keywords = map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] || mentions[id3] {
- t.Fatal("should've only mentioned aaa")
- }
-
- message = ":smile:"
- keywords = map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) == 1 || mentions[id1] {
- t.Fatal("should not mentioned smile")
- }
-
- message = "smile"
- keywords = map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] || mentions[id3] {
- t.Fatal("should've only mentioned smile")
- }
-
- message = ":smile"
- keywords = map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] || mentions[id3] {
- t.Fatal("should've only mentioned smile")
- }
-
- message = "smile:"
- keywords = map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}
- if mentions, _, _, _, _ := GetExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] || mentions[id3] {
- t.Fatal("should've only mentioned smile")
+ for name, tc := range map[string]struct {
+ Message string
+ Keywords map[string][]string
+ Expected *ExplicitMentions
+ }{
+ "Nobody": {
+ Message: "this is a message",
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{},
+ },
+ "NonexistentUser": {
+ Message: "this is a message for @user",
+ Expected: &ExplicitMentions{
+ OtherPotentialMentions: []string{"user"},
+ },
+ },
+ "OnePerson": {
+ Message: "this is a message for @user",
+ Keywords: map[string][]string{"@user": {id1}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "OnePersonWithoutAtMention": {
+ Message: "this is a message for @user",
+ Keywords: map[string][]string{"this": {id1}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ OtherPotentialMentions: []string{"user"},
+ },
+ },
+ "MultiplePeopleWithOneWord": {
+ Message: "this is a message for @user",
+ Keywords: map[string][]string{"@user": {id1, id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ },
+ },
+ },
+ "OneOfMultiplePeople": {
+ Message: "this is a message for @user",
+ Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "MultiplePeopleWithMultipleWords": {
+ Message: "this is an @mention for @user",
+ Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ },
+ },
+ },
+ "Channel": {
+ Message: "this is an message for @channel",
+ Keywords: map[string][]string{"@channel": {id1, id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ },
+ ChannelMentioned: true,
+ },
+ },
+ "All": {
+ Message: "this is an message for @all",
+ Keywords: map[string][]string{"@all": {id1, id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ },
+ AllMentioned: true,
+ },
+ },
+ "UserWithPeriod": {
+ Message: "user.period doesn't complicate things at all by including periods in their username",
+ Keywords: map[string][]string{"user.period": {id1}, "user": {id2}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "PotentialOutOfChannelUser": {
+ Message: "this is an message for @potential and @user",
+ Keywords: map[string][]string{"@user": {id1}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ OtherPotentialMentions: []string{"potential"},
+ },
+ },
+ "InlineCode": {
+ Message: "`this shouldn't mention @channel at all`",
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{},
+ },
+ "FencedCodeBlock": {
+ Message: "```\nthis shouldn't mention @channel at all\n```",
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{},
+ },
+ "Emphasis": {
+ Message: "*@aaa @bbb @ccc*",
+ Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ id3: true,
+ },
+ },
+ },
+ "StrongEmphasis": {
+ Message: "**@aaa @bbb @ccc**",
+ Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ id3: true,
+ },
+ },
+ },
+ "Strikethrough": {
+ Message: "~~@aaa @bbb @ccc~~",
+ Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ id2: true,
+ id3: true,
+ },
+ },
+ },
+ "Heading": {
+ Message: "### @aaa",
+ Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "BlockQuote": {
+ Message: "> @aaa",
+ Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "Emoji": {
+ Message: ":smile:",
+ Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}},
+ Expected: &ExplicitMentions{},
+ },
+ "NotEmoji": {
+ Message: "smile",
+ Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "UnclosedEmoji": {
+ Message: ":smile",
+ Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "UnopenedEmoji": {
+ Message: "smile:",
+ Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}},
+ Expected: &ExplicitMentions{
+ MentionedUserIds: map[string]bool{
+ id1: true,
+ },
+ },
+ },
+ "IndentedCodeBlock": {
+ Message: " this shouldn't mention @channel at all",
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{},
+ },
+ "LinkTitle": {
+ Message: `[foo](this "shouldn't mention @channel at all")`,
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{},
+ },
+ "MalformedInlineCode": {
+ Message: "`this should mention @channel``",
+ Keywords: map[string][]string{},
+ Expected: &ExplicitMentions{
+ ChannelMentioned: true,
+ },
+ },
+ } {
+ t.Run(name, func(t *testing.T) {
+ m := GetExplicitMentions(tc.Message, tc.Keywords)
+ if tc.Expected.MentionedUserIds == nil {
+ tc.Expected.MentionedUserIds = make(map[string]bool)
+ }
+ assert.EqualValues(t, tc.Expected, m)
+ })
}
}
@@ -268,170 +352,24 @@ func TestGetExplicitMentionsAtHere(t *testing.T) {
}
for message, shouldMention := range cases {
- if _, _, hereMentioned, _, _ := GetExplicitMentions(message, nil); hereMentioned && !shouldMention {
+ if m := GetExplicitMentions(message, nil); m.HereMentioned && !shouldMention {
t.Fatalf("shouldn't have mentioned @here with \"%v\"", message)
- } else if !hereMentioned && shouldMention {
- t.Fatalf("should've have mentioned @here with \"%v\"", message)
+ } else if !m.HereMentioned && shouldMention {
+ t.Fatalf("should've mentioned @here with \"%v\"", message)
}
}
// mentioning @here and someone
id := model.NewId()
- if mentions, potential, hereMentioned, _, _ := GetExplicitMentions("@here @user @potential", map[string][]string{"@user": {id}}); !hereMentioned {
+ if m := GetExplicitMentions("@here @user @potential", map[string][]string{"@user": {id}}); !m.HereMentioned {
t.Fatal("should've mentioned @here with \"@here @user\"")
- } else if len(mentions) != 1 || !mentions[id] {
+ } else if len(m.MentionedUserIds) != 1 || !m.MentionedUserIds[id] {
t.Fatal("should've mentioned @user with \"@here @user\"")
- } else if len(potential) > 1 {
+ } else if len(m.OtherPotentialMentions) > 1 {
t.Fatal("should've potential mentions for @potential")
}
}
-func TestRemoveCodeFromMessage(t *testing.T) {
- input := "this is regular text"
- expected := input
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with\n```\na code block\n```\nin it"
- expected = "this is text with\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with\n```javascript\na JS code block\n```\nin it"
- expected = "this is text with\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with\n```java script?\na JS code block\n```\nin it"
- expected = "this is text with\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with an empty\n```\n\n\n\n```\nin it"
- expected = "this is text with an empty\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with\n```\ntwo\n```\ncode\n```\nblocks\n```\nin it"
- expected = "this is text with\n\ncode\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with indented\n ```\ncode\n ```\nin it"
- expected = "this is text with indented\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text ending with\n```\nan unfinished code block"
- expected = "this is text ending with\n"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `code` in a sentence"
- expected = "this is in a sentence"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `two` things of `code` in a sentence"
- expected = "this is things of in a sentence"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `code with spaces` in a sentence"
- expected = "this is in a sentence"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `code\nacross multiple` lines"
- expected = "this is lines"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `code\non\nmany\ndifferent` lines"
- expected = "this is lines"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `\ncode on its own line\n` across multiple lines"
- expected = "this is across multiple lines"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `\n some more code \n` across multiple lines"
- expected = "this is across multiple lines"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `\ncode` on its own line"
- expected = "this is on its own line"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `code\n` on its own line"
- expected = "this is on its own line"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is *italics mixed with `code in a way that has the code` take precedence*"
- expected = "this is *italics mixed with take precedence*"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is code within a wo` `rd for some reason"
- expected = "this is code within a wo rd for some reason"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `not\n\ncode` because it has a blank line"
- expected = input
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is `not\n \ncode` because it has a line with only whitespace"
- expected = input
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is just `` two backquotes"
- expected = input
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "these are ``multiple backquotes`` around code"
- expected = "these are around code"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-
- input = "this is text with\n~~~\na code block\n~~~\nin it"
- expected = "this is text with\n\nin it"
- if actual := removeCodeFromMessage(input); actual != expected {
- t.Fatalf("received incorrect output\n\nGot:\n%v\n\nExpected:\n%v\n", actual, expected)
- }
-}
-
func TestGetMentionKeywords(t *testing.T) {
th := Setup()
defer th.TearDown()