Files
team/server/channels/app/mention_parser_standard_test.go
Jesse HallamandGitHub edf2075381 MM-68149: Upgrade to Go 1.26.2 (#36418)
* MM-68149: upgrade to Go 1.26.2

Update go directive in go.mod and .go-version.

* MM-68149: replace pointer helpers with Go 1.26 new()

Go 1.26 extends the built-in new() to accept an initial value expression,
making typed-pointer helpers like model.NewPointer(x), bToP(x), and boolPtr(x)
redundant. Replace every call site with new(x) and remove the now-unused
helper functions and their //go:fix inline directives.

* MM-68149: apply go fix for reflect API and format-string changes

- reflect.Ptr → reflect.Pointer (renamed in Go 1.18, deprecated alias removed in 1.26)
- reflect range-over-struct: for i := 0; i < t.NumField(); i++ → for field := range t.Fields()
  and the equivalent for Methods() and interface types
- Fix format-string concatenation and variadic-arg mismatches flagged by go vet

* MM-68149: update JPEG fixtures and test infrastructure for Go 1.26 encoder

Go 1.26 ships a new image/jpeg encoder that produces slightly different output.
Regenerate all JPEG fixture files and switch the comparison helpers from
byte-equality to pixel-level comparison with a small per-channel tolerance,
so minor encoder drift across patch versions is handled automatically.

Add -update-fixtures flag to make it easy to regenerate fixtures after future
major Go upgrades. Document the update procedure in tests/README.md.

* MM-68149: CI check that go fix ./... produces no changes

* Fix real bugs flagged by CodeRabbit review

- group.go: set newGroup.MemberCount not group.MemberCount (member count
  was populated on the wrong variable and lost before publish/return)
- file_test.go: guard compareImage(GetFilePreview) on the preview slice
  length, not the thumbnail slice length (copy-paste error)
- config_test.go: remove duplicate MinimumLength assignment

* fixup! Fix real bugs flagged by CodeRabbit review
2026-05-12 15:59:12 +00:00

438 lines
11 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/assert"
)
func TestIsKeywordMultibyte(t *testing.T) {
mainHelper.Parallel(t)
id1 := model.NewId()
for name, tc := range map[string]struct {
Message string
Attachments []*model.MessageAttachment
Keywords map[string][]string
Groups map[string]*model.Group
Expected *MentionResults
}{
"MultibyteCharacter": {
Message: "My name is 萌",
Keywords: map[string][]string{"萌": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"MultibyteCharacterWithNoUser": {
Message: "My name is 萌",
Keywords: map[string][]string{"萌": {}},
Expected: &MentionResults{
Mentions: nil,
},
},
"MultibyteCharacterAtBeginningOfSentence": {
Message: "이메일을 보내다.",
Keywords: map[string][]string{"이메일": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"MultibyteCharacterAtBeginningOfSentenceWithNoUser": {
Message: "이메일을 보내다.",
Keywords: map[string][]string{"이메일": {}},
Expected: &MentionResults{
Mentions: nil,
},
},
"MultibyteCharacterInPartOfSentence": {
Message: "我爱吃番茄炒饭",
Keywords: map[string][]string{"番茄": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"MultibyteCharacterInPartOfSentenceWithNoUser": {
Message: "我爱吃番茄炒饭",
Keywords: map[string][]string{"番茄": {}},
Expected: &MentionResults{
Mentions: nil,
},
},
"MultibyteCharacterAtEndOfSentence": {
Message: "こんにちは、世界",
Keywords: map[string][]string{"世界": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"MultibyteCharacterAtEndOfSentenceWithNoUser": {
Message: "こんにちは、世界",
Keywords: map[string][]string{"世界": {}},
Expected: &MentionResults{
Mentions: nil,
},
},
"MultibyteCharacterTwiceInSentence": {
Message: "石橋さんが石橋を渡る",
Keywords: map[string][]string{"石橋": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"MultibyteCharacterTwiceInSentenceWithNoUser": {
Message: "石橋さんが石橋を渡る",
Keywords: map[string][]string{"石橋": {}},
Expected: &MentionResults{
Mentions: nil,
},
},
} {
t.Run(name, func(t *testing.T) {
post := &model.Post{
Message: tc.Message,
Props: model.StringInterface{
model.PostPropsAttachments: tc.Attachments,
},
}
m := getExplicitMentions(post, mapsToMentionKeywords(tc.Keywords, tc.Groups))
assert.EqualValues(t, tc.Expected, m)
})
}
}
func TestCheckForMentionUsers(t *testing.T) {
mainHelper.Parallel(t)
id1 := model.NewId()
id2 := model.NewId()
for name, tc := range map[string]struct {
Word string
Attachments []*model.MessageAttachment
Keywords map[string][]string
Expected *MentionResults
}{
"Nobody": {
Word: "nothing",
Keywords: map[string][]string{},
Expected: &MentionResults{},
},
"UppercaseUser1": {
Word: "@User",
Keywords: map[string][]string{"@user": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"LowercaseUser1": {
Word: "@user",
Keywords: map[string][]string{"@user": {id1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id1: KeywordMention,
},
},
},
"LowercaseUser2": {
Word: "@user2",
Keywords: map[string][]string{"@user2": {id2}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id2: KeywordMention,
},
},
},
"UppercaseUser2": {
Word: "@UsEr2",
Keywords: map[string][]string{"@user2": {id2}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
id2: KeywordMention,
},
},
},
"HereMention": {
Word: "@here",
Expected: &MentionResults{
HereMentioned: true,
},
},
"ChannelMention": {
Word: "@channel",
Expected: &MentionResults{
ChannelMentioned: true,
},
},
"AllMention": {
Word: "@all",
Expected: &MentionResults{
AllMentioned: true,
},
},
"UppercaseHere": {
Word: "@HeRe",
Expected: &MentionResults{
HereMentioned: true,
},
},
"UppercaseChannel": {
Word: "@ChaNNel",
Expected: &MentionResults{
ChannelMentioned: true,
},
},
"UppercaseAll": {
Word: "@ALL",
Expected: &MentionResults{
AllMentioned: true,
},
},
} {
t.Run(name, func(t *testing.T) {
p := makeStandardMentionParser(mapsToMentionKeywords(tc.Keywords, nil))
p.checkForMention(tc.Word)
assert.EqualValues(t, tc.Expected, p.Results())
})
}
}
func TestCheckForMentionGroups(t *testing.T) {
mainHelper.Parallel(t)
groupID1 := model.NewId()
groupID2 := model.NewId()
for name, tc := range map[string]struct {
Word string
Groups map[string]*model.Group
Expected *MentionResults
}{
"No groups": {
Word: "nothing",
Groups: map[string]*model.Group{},
Expected: &MentionResults{},
},
"No matching groups": {
Word: "nothing",
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{},
},
"matching group with no @": {
Word: "engineering",
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{},
},
"matching group with preceding @": {
Word: "@engineering",
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
GroupMentions: map[string]MentionType{
groupID1: GroupMention,
},
},
},
"matching upper case group with preceding @": {
Word: "@Engineering",
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
GroupMentions: map[string]MentionType{
groupID1: GroupMention,
},
},
},
} {
t.Run(name, func(t *testing.T) {
p := makeStandardMentionParser(mapsToMentionKeywords(nil, tc.Groups))
p.checkForMention(tc.Word)
mr := p.Results()
assert.EqualValues(t, tc.Expected, mr)
})
}
}
func TestProcessText(t *testing.T) {
mainHelper.Parallel(t)
userID1 := model.NewId()
groupID1 := model.NewId()
groupID2 := model.NewId()
for name, tc := range map[string]struct {
Text string
Keywords map[string][]string
Groups map[string]*model.Group
Expected *MentionResults
}{
"Mention user in text": {
Text: "hello user @user1",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
},
},
"Mention user after ending a sentence with full stop": {
Text: "hello user.@user1",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
},
},
"Mention user after hyphen": {
Text: "hello user-@user1",
Keywords: map[string][]string{"@user1": {userID1}},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
},
},
"Mention user after colon": {
Text: "hello user:@user1",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
},
},
"Mention here after colon": {
Text: "hello all:@here",
Keywords: map[string][]string{},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
HereMentioned: true,
},
},
"Mention all after hyphen": {
Text: "hello all-@all",
Keywords: map[string][]string{},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
AllMentioned: true,
},
},
"Mention channel after full stop": {
Text: "hello channel.@channel",
Keywords: map[string][]string{},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
ChannelMentioned: true,
},
},
"Mention other potential users or system calls": {
Text: "hello @potentialuser and @otherpotentialuser",
Keywords: map[string][]string{},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
OtherPotentialMentions: []string{"potentialuser", "otherpotentialuser"},
},
},
"Mention a real user and another potential user": {
Text: "@user1, you can use @systembot to get help",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
OtherPotentialMentions: []string{"systembot"},
},
},
"Mention a group": {
Text: "@engineering",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
GroupMentions: map[string]MentionType{groupID1: GroupMention},
},
},
"Mention a real user and another potential user and a group": {
Text: "@engineering @user1, you can use @systembot to get help from",
Keywords: map[string][]string{"@user1": {userID1}},
Groups: map[string]*model.Group{
groupID1: {Id: groupID1, Name: new("engineering")},
groupID2: {Id: groupID2, Name: new("developers")},
},
Expected: &MentionResults{
Mentions: map[string]MentionType{
userID1: KeywordMention,
},
GroupMentions: map[string]MentionType{groupID1: GroupMention},
OtherPotentialMentions: []string{"systembot"},
},
},
} {
t.Run(name, func(t *testing.T) {
p := makeStandardMentionParser(mapsToMentionKeywords(tc.Keywords, tc.Groups))
p.ProcessText(tc.Text)
assert.EqualValues(t, tc.Expected, p.Results())
})
}
}