mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* MM-13015 Add safety valve setting to disable post metadata * Remove setting from client config since it's no longer needed
1105 lines
30 KiB
Go
1105 lines
30 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dyatlov/go-opengraph/opengraph"
|
|
"github.com/mattermost/mattermost-server/model"
|
|
"github.com/mattermost/mattermost-server/utils/testutils"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPreparePostListForClient(t *testing.T) {
|
|
// Most of this logic is covered by TestPreparePostForClient, so this just tests handling of multiple posts
|
|
|
|
th := Setup().InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ExperimentalSettings.DisablePostMetadata = false
|
|
})
|
|
|
|
postList := model.NewPostList()
|
|
for i := 0; i < 5; i++ {
|
|
postList.AddPost(&model.Post{})
|
|
}
|
|
|
|
clientPostList := th.App.PreparePostListForClient(postList)
|
|
|
|
t.Run("doesn't mutate provided post list", func(t *testing.T) {
|
|
assert.NotEqual(t, clientPostList, postList, "should've returned a new post list")
|
|
assert.NotEqual(t, clientPostList.Posts, postList.Posts, "should've returned a new PostList.Posts")
|
|
assert.Equal(t, clientPostList.Order, postList.Order, "should've returned the existing PostList.Order")
|
|
|
|
for id, originalPost := range postList.Posts {
|
|
assert.NotEqual(t, clientPostList.Posts[id], originalPost, "should've returned new post objects")
|
|
assert.Equal(t, clientPostList.Posts[id].Id, originalPost.Id, "should've returned the same posts")
|
|
}
|
|
})
|
|
|
|
t.Run("adds metadata to each post", func(t *testing.T) {
|
|
for _, clientPost := range clientPostList.Posts {
|
|
assert.NotNil(t, clientPost.Metadata, "should've populated metadata for each post")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPreparePostForClient(t *testing.T) {
|
|
setup := func() *TestHelper {
|
|
th := Setup().InitBasic()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.ImageProxyType = ""
|
|
*cfg.ServiceSettings.ImageProxyURL = ""
|
|
*cfg.ServiceSettings.ImageProxyOptions = ""
|
|
*cfg.ExperimentalSettings.DisablePostMetadata = false
|
|
})
|
|
|
|
return th
|
|
}
|
|
|
|
t.Run("no metadata needed", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
message := model.NewId()
|
|
post := &model.Post{
|
|
Message: message,
|
|
}
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("doesn't mutate provided post", func(t *testing.T) {
|
|
assert.NotEqual(t, clientPost, post, "should've returned a new post")
|
|
|
|
assert.Equal(t, message, post.Message, "shouldn't have mutated post.Message")
|
|
assert.Equal(t, (*model.PostMetadata)(nil), post.Metadata, "shouldn't have mutated post.Metadata")
|
|
})
|
|
|
|
t.Run("populates all fields", func(t *testing.T) {
|
|
assert.Equal(t, message, clientPost.Message, "shouldn't have changed Message")
|
|
assert.NotEqual(t, nil, clientPost.Metadata, "should've populated Metadata")
|
|
assert.Len(t, clientPost.Metadata.Embeds, 0, "should've populated Embeds")
|
|
assert.Len(t, clientPost.Metadata.Reactions, 0, "should've populated Reactions")
|
|
assert.Len(t, clientPost.Metadata.Files, 0, "should've populated Files")
|
|
assert.Len(t, clientPost.Metadata.Emojis, 0, "should've populated Emojis")
|
|
assert.Len(t, clientPost.Metadata.Images, 0, "should've populated Images")
|
|
})
|
|
})
|
|
|
|
t.Run("metadata already set", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
assert.False(t, clientPost == post, "should've returned a new post")
|
|
assert.Equal(t, clientPost, post, "shouldn't have changed any metadata")
|
|
})
|
|
|
|
t.Run("reactions", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
reaction1 := th.AddReactionToPost(post, th.BasicUser, "smile")
|
|
reaction2 := th.AddReactionToPost(post, th.BasicUser2, "smile")
|
|
reaction3 := th.AddReactionToPost(post, th.BasicUser2, "ice_cream")
|
|
post.HasReactions = true
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
assert.Len(t, clientPost.Metadata.Reactions, 3, "should've populated Reactions")
|
|
assert.Equal(t, reaction1, clientPost.Metadata.Reactions[0], "first reaction is incorrect")
|
|
assert.Equal(t, reaction2, clientPost.Metadata.Reactions[1], "second reaction is incorrect")
|
|
assert.Equal(t, reaction3, clientPost.Metadata.Reactions[2], "third reaction is incorrect")
|
|
})
|
|
|
|
t.Run("files", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
fileInfo, err := th.App.DoUploadFile(time.Now(), th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, "test.txt", []byte("test"))
|
|
require.Nil(t, err)
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
FileIds: []string{fileInfo.Id},
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
fileInfo.PostId = post.Id
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
assert.Equal(t, []*model.FileInfo{fileInfo}, clientPost.Metadata.Files, "should've populated Files")
|
|
})
|
|
|
|
t.Run("emojis without custom emojis enabled", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableCustomEmoji = false
|
|
})
|
|
|
|
emoji := th.CreateEmoji()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: ":" + emoji.Name + ": :taco:",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: ":" + emoji.Name + ":",
|
|
},
|
|
},
|
|
},
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
th.AddReactionToPost(post, th.BasicUser, "smile")
|
|
th.AddReactionToPost(post, th.BasicUser, "angry")
|
|
th.AddReactionToPost(post, th.BasicUser2, "angry")
|
|
post.HasReactions = true
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("populates emojis", func(t *testing.T) {
|
|
assert.ElementsMatch(t, []*model.Emoji{}, clientPost.Metadata.Emojis, "should've populated empty Emojis")
|
|
})
|
|
|
|
t.Run("populates reaction counts", func(t *testing.T) {
|
|
reactions := clientPost.Metadata.Reactions
|
|
assert.Len(t, reactions, 3, "should've populated Reactions")
|
|
})
|
|
})
|
|
|
|
t.Run("emojis with custom emojis enabled", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableCustomEmoji = true
|
|
})
|
|
|
|
emoji1 := th.CreateEmoji()
|
|
emoji2 := th.CreateEmoji()
|
|
emoji3 := th.CreateEmoji()
|
|
emoji4 := th.CreateEmoji()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: ":" + emoji3.Name + ": :taco:",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: ":" + emoji4.Name + ":",
|
|
},
|
|
},
|
|
},
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
th.AddReactionToPost(post, th.BasicUser, emoji1.Name)
|
|
th.AddReactionToPost(post, th.BasicUser, emoji2.Name)
|
|
th.AddReactionToPost(post, th.BasicUser2, emoji2.Name)
|
|
th.AddReactionToPost(post, th.BasicUser2, "angry")
|
|
post.HasReactions = true
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("pupulates emojis", func(t *testing.T) {
|
|
assert.ElementsMatch(t, []*model.Emoji{emoji1, emoji2, emoji3, emoji4}, clientPost.Metadata.Emojis, "should've populated post.Emojis")
|
|
})
|
|
|
|
t.Run("populates reaction counts", func(t *testing.T) {
|
|
reactions := clientPost.Metadata.Reactions
|
|
assert.Len(t, reactions, 4, "should've populated Reactions")
|
|
})
|
|
})
|
|
|
|
t.Run("markdown image dimensions", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "This is  and ",
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("populates image dimensions", func(t *testing.T) {
|
|
imageDimensions := clientPost.Metadata.Images
|
|
assert.Len(t, imageDimensions, 2)
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 1068,
|
|
Height: 552,
|
|
}, imageDimensions["https://github.com/hmhealey/test-files/raw/master/logoVertical.png"])
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 501,
|
|
Height: 501,
|
|
}, imageDimensions["https://github.com/hmhealey/test-files/raw/master/icon.png"])
|
|
})
|
|
})
|
|
|
|
t.Run("proxy linked images", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
testProxyLinkedImage(t, th, false)
|
|
})
|
|
|
|
t.Run("proxy opengraph images", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
testProxyOpenGraphImage(t, th, false)
|
|
})
|
|
|
|
t.Run("image embed", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: `This is our logo: https://github.com/hmhealey/test-files/raw/master/logoVertical.png
|
|
And this is our icon: https://github.com/hmhealey/test-files/raw/master/icon.png`,
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
// Reminder that only the first link gets an embed and dimensions
|
|
|
|
t.Run("populates embeds", func(t *testing.T) {
|
|
assert.ElementsMatch(t, []*model.PostEmbed{
|
|
{
|
|
Type: model.POST_EMBED_IMAGE,
|
|
URL: "https://github.com/hmhealey/test-files/raw/master/logoVertical.png",
|
|
},
|
|
}, clientPost.Metadata.Embeds)
|
|
})
|
|
|
|
t.Run("populates image dimensions", func(t *testing.T) {
|
|
imageDimensions := clientPost.Metadata.Images
|
|
assert.Len(t, imageDimensions, 1)
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 1068,
|
|
Height: 552,
|
|
}, imageDimensions["https://github.com/hmhealey/test-files/raw/master/logoVertical.png"])
|
|
})
|
|
})
|
|
|
|
t.Run("opengraph embed", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: `This is our web page: https://github.com/hmhealey/test-files`,
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("populates embeds", func(t *testing.T) {
|
|
assert.ElementsMatch(t, []*model.PostEmbed{
|
|
{
|
|
Type: model.POST_EMBED_OPENGRAPH,
|
|
URL: "https://github.com/hmhealey/test-files",
|
|
Data: &opengraph.OpenGraph{
|
|
Description: "Contribute to hmhealey/test-files development by creating an account on GitHub.",
|
|
SiteName: "GitHub",
|
|
Title: "hmhealey/test-files",
|
|
Type: "object",
|
|
URL: "https://github.com/hmhealey/test-files",
|
|
Images: []*opengraph.Image{
|
|
{
|
|
URL: "https://avatars1.githubusercontent.com/u/3277310?s=400&v=4",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, clientPost.Metadata.Embeds)
|
|
})
|
|
|
|
t.Run("populates image dimensions", func(t *testing.T) {
|
|
imageDimensions := clientPost.Metadata.Images
|
|
assert.Len(t, imageDimensions, 1)
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 420,
|
|
Height: 420,
|
|
}, imageDimensions["https://avatars1.githubusercontent.com/u/3277310?s=400&v=4"])
|
|
})
|
|
})
|
|
|
|
t.Run("message attachment embed", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Props: map[string]interface{}{
|
|
"attachments": []interface{}{
|
|
map[string]interface{}{
|
|
"text": "",
|
|
},
|
|
},
|
|
},
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
t.Run("populates embeds", func(t *testing.T) {
|
|
assert.ElementsMatch(t, []*model.PostEmbed{
|
|
{
|
|
Type: model.POST_EMBED_MESSAGE_ATTACHMENT,
|
|
},
|
|
}, clientPost.Metadata.Embeds)
|
|
})
|
|
|
|
t.Run("populates image dimensions", func(t *testing.T) {
|
|
imageDimensions := clientPost.Metadata.Images
|
|
assert.Len(t, imageDimensions, 1)
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 501,
|
|
Height: 501,
|
|
}, imageDimensions["https://github.com/hmhealey/test-files/raw/master/icon.png"])
|
|
})
|
|
})
|
|
|
|
t.Run("when disabled", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ExperimentalSettings.DisablePostMetadata = true
|
|
})
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
post = th.App.PreparePostForClient(post)
|
|
|
|
assert.Nil(t, post.Metadata)
|
|
})
|
|
}
|
|
|
|
func TestPreparePostForClientWithImageProxy(t *testing.T) {
|
|
setup := func() *TestHelper {
|
|
th := Setup().InitBasic()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
|
|
*cfg.ServiceSettings.ImageProxyType = "atmos/camo"
|
|
*cfg.ServiceSettings.ImageProxyURL = "https://127.0.0.1"
|
|
*cfg.ServiceSettings.ImageProxyOptions = "foo"
|
|
*cfg.ExperimentalSettings.DisablePostMetadata = false
|
|
})
|
|
|
|
return th
|
|
}
|
|
|
|
t.Run("proxy linked images", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
testProxyLinkedImage(t, th, true)
|
|
})
|
|
|
|
t.Run("proxy opengraph images", func(t *testing.T) {
|
|
th := setup()
|
|
defer th.TearDown()
|
|
|
|
testProxyOpenGraphImage(t, th, true)
|
|
})
|
|
}
|
|
|
|
func testProxyLinkedImage(t *testing.T, th *TestHelper, shouldProxy bool) {
|
|
postTemplate := ""
|
|
imageURL := "http://mydomain.com/myimage"
|
|
proxiedImageURL := "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765"
|
|
|
|
post := &model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: fmt.Sprintf(postTemplate, imageURL),
|
|
}
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
if shouldProxy {
|
|
assert.Equal(t, post.Message, fmt.Sprintf(postTemplate, imageURL), "should not have mutated original post")
|
|
assert.Equal(t, clientPost.Message, fmt.Sprintf(postTemplate, proxiedImageURL), "should've replaced linked image URLs")
|
|
} else {
|
|
assert.Equal(t, clientPost.Message, fmt.Sprintf(postTemplate, imageURL), "shouldn't have replaced linked image URLs")
|
|
}
|
|
}
|
|
|
|
func testProxyOpenGraphImage(t *testing.T, th *TestHelper, shouldProxy bool) {
|
|
post, err := th.App.CreatePost(&model.Post{
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: `This is our web page: https://github.com/hmhealey/test-files`,
|
|
}, th.BasicChannel, false)
|
|
require.Nil(t, err)
|
|
|
|
clientPost := th.App.PreparePostForClient(post)
|
|
|
|
image := &opengraph.Image{}
|
|
if shouldProxy {
|
|
image.SecureURL = "https://127.0.0.1/b2ef6ef4890a0107aa80ba33b3011fd51f668303/68747470733a2f2f61766174617273312e67697468756275736572636f6e74656e742e636f6d2f752f333237373331303f733d34303026763d34"
|
|
} else {
|
|
image.URL = "https://avatars1.githubusercontent.com/u/3277310?s=400&v=4"
|
|
}
|
|
|
|
assert.ElementsMatch(t, []*model.PostEmbed{
|
|
{
|
|
Type: model.POST_EMBED_OPENGRAPH,
|
|
URL: "https://github.com/hmhealey/test-files",
|
|
Data: &opengraph.OpenGraph{
|
|
Description: "Contribute to hmhealey/test-files development by creating an account on GitHub.",
|
|
SiteName: "GitHub",
|
|
Title: "hmhealey/test-files",
|
|
Type: "object",
|
|
URL: "https://github.com/hmhealey/test-files",
|
|
Images: []*opengraph.Image{image},
|
|
},
|
|
},
|
|
}, clientPost.Metadata.Embeds)
|
|
}
|
|
|
|
func TestGetEmojiNamesForString(t *testing.T) {
|
|
testCases := []struct {
|
|
Description string
|
|
Input string
|
|
Expected []string
|
|
}{
|
|
{
|
|
Description: "no emojis",
|
|
Input: "this is a string",
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Description: "one emoji",
|
|
Input: "this is an :emoji1: string",
|
|
Expected: []string{"emoji1"},
|
|
},
|
|
{
|
|
Description: "two emojis",
|
|
Input: "this is a :emoji3: :emoji2: string",
|
|
Expected: []string{"emoji3", "emoji2"},
|
|
},
|
|
{
|
|
Description: "punctuation around emojis",
|
|
Input: ":emoji3:/:emoji1: (:emoji2:)",
|
|
Expected: []string{"emoji3", "emoji1", "emoji2"},
|
|
},
|
|
{
|
|
Description: "adjacent emojis",
|
|
Input: ":emoji3::emoji1:",
|
|
Expected: []string{"emoji3", "emoji1"},
|
|
},
|
|
{
|
|
Description: "duplicate emojis",
|
|
Input: ":emoji1: :emoji1: :emoji1::emoji2::emoji2: :emoji1:",
|
|
Expected: []string{"emoji1", "emoji1", "emoji1", "emoji2", "emoji2", "emoji1"},
|
|
},
|
|
{
|
|
Description: "fake emojis",
|
|
Input: "these don't exist :tomato: :potato: :rotato:",
|
|
Expected: []string{"tomato", "potato", "rotato"},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
emojis := getEmojiNamesForString(testCase.Input)
|
|
assert.ElementsMatch(t, emojis, testCase.Expected, "received incorrect emoji names")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetEmojiNamesForPost(t *testing.T) {
|
|
testCases := []struct {
|
|
Description string
|
|
Post *model.Post
|
|
Reactions []*model.Reaction
|
|
Expected []string
|
|
}{
|
|
{
|
|
Description: "no emojis",
|
|
Post: &model.Post{
|
|
Message: "this is a post",
|
|
},
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Description: "in post message",
|
|
Post: &model.Post{
|
|
Message: "this is :emoji:",
|
|
},
|
|
Expected: []string{"emoji"},
|
|
},
|
|
{
|
|
Description: "in reactions",
|
|
Post: &model.Post{},
|
|
Reactions: []*model.Reaction{
|
|
{
|
|
EmojiName: "emoji1",
|
|
},
|
|
{
|
|
EmojiName: "emoji2",
|
|
},
|
|
},
|
|
Expected: []string{"emoji1", "emoji2"},
|
|
},
|
|
{
|
|
Description: "in message attachments",
|
|
Post: &model.Post{
|
|
Message: "this is a post",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: ":emoji1:",
|
|
Pretext: ":emoji2:",
|
|
},
|
|
{
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: ":emoji3:",
|
|
},
|
|
{
|
|
Value: ":emoji4:",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"emoji1", "emoji2", "emoji3", "emoji4"},
|
|
},
|
|
{
|
|
Description: "with duplicates",
|
|
Post: &model.Post{
|
|
Message: "this is :emoji1",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: ":emoji2:",
|
|
Pretext: ":emoji2:",
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: ":emoji3:",
|
|
},
|
|
{
|
|
Value: ":emoji1:",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"emoji1", "emoji2", "emoji3"},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
emojis := getEmojiNamesForPost(testCase.Post, testCase.Reactions)
|
|
assert.ElementsMatch(t, emojis, testCase.Expected, "received incorrect emoji names")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetCustomEmojisForPost(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableCustomEmoji = true
|
|
})
|
|
|
|
emojis := []*model.Emoji{
|
|
th.CreateEmoji(),
|
|
th.CreateEmoji(),
|
|
th.CreateEmoji(),
|
|
th.CreateEmoji(),
|
|
th.CreateEmoji(),
|
|
th.CreateEmoji(),
|
|
}
|
|
|
|
t.Run("from different parts of the post", func(t *testing.T) {
|
|
reactions := []*model.Reaction{
|
|
{
|
|
UserId: th.BasicUser.Id,
|
|
EmojiName: emojis[0].Name,
|
|
},
|
|
}
|
|
|
|
post := &model.Post{
|
|
Message: ":" + emojis[1].Name + ":",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Pretext: ":" + emojis[2].Name + ":",
|
|
Text: ":" + emojis[3].Name + ":",
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: ":" + emojis[4].Name + ":",
|
|
},
|
|
{
|
|
Value: ":" + emojis[5].Name + ":",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
emojisForPost, err := th.App.getCustomEmojisForPost(post, reactions)
|
|
assert.Nil(t, err, "failed to get emojis for post")
|
|
assert.ElementsMatch(t, emojisForPost, emojis, "received incorrect emojis")
|
|
})
|
|
|
|
t.Run("with emojis that don't exist", func(t *testing.T) {
|
|
post := &model.Post{
|
|
Message: ":secret: :" + emojis[0].Name + ":",
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: ":imaginary:",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
emojisForPost, err := th.App.getCustomEmojisForPost(post, nil)
|
|
assert.Nil(t, err, "failed to get emojis for post")
|
|
assert.ElementsMatch(t, emojisForPost, []*model.Emoji{emojis[0]}, "received incorrect emojis")
|
|
})
|
|
|
|
t.Run("with no emojis", func(t *testing.T) {
|
|
post := &model.Post{
|
|
Message: "this post is boring",
|
|
Props: map[string]interface{}{},
|
|
}
|
|
|
|
emojisForPost, err := th.App.getCustomEmojisForPost(post, nil)
|
|
assert.Nil(t, err, "failed to get emojis for post")
|
|
assert.ElementsMatch(t, emojisForPost, []*model.Emoji{}, "should have received no emojis")
|
|
})
|
|
}
|
|
|
|
func TestGetFirstLinkAndImages(t *testing.T) {
|
|
for name, testCase := range map[string]struct {
|
|
Input string
|
|
ExpectedFirstLink string
|
|
ExpectedImages []string
|
|
}{
|
|
"no links or images": {
|
|
Input: "this is a string",
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{},
|
|
},
|
|
"http link": {
|
|
Input: "this is a http://example.com",
|
|
ExpectedFirstLink: "http://example.com",
|
|
ExpectedImages: []string{},
|
|
},
|
|
"www link": {
|
|
Input: "this is a www.example.com",
|
|
ExpectedFirstLink: "http://www.example.com",
|
|
ExpectedImages: []string{},
|
|
},
|
|
"image": {
|
|
Input: "this is a ",
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{"http://example.com/logo"},
|
|
},
|
|
"multiple images": {
|
|
Input: "this is a  and  and ",
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{"http://example.com/logo", "http://example.com/logo2", "http://example.com/logo3"},
|
|
},
|
|
"multiple images with duplicate": {
|
|
Input: "this is a  and  and ",
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{"http://example.com/logo", "http://example.com/logo2", "http://example.com/logo2"},
|
|
},
|
|
"reference image": {
|
|
Input: `this is a ![our logo][logo]
|
|
|
|
[logo]: http://example.com/logo`,
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{"http://example.com/logo"},
|
|
},
|
|
"image and link": {
|
|
Input: "this is a https://example.com and ",
|
|
ExpectedFirstLink: "https://example.com",
|
|
ExpectedImages: []string{"https://example.com/logo"},
|
|
},
|
|
"markdown links (not returned)": {
|
|
Input: `this is a [our page](http://example.com) and [another page][]
|
|
|
|
[another page]: http://www.exaple.com/another_page`,
|
|
ExpectedFirstLink: "",
|
|
ExpectedImages: []string{},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
firstLink, images := getFirstLinkAndImages(testCase.Input)
|
|
|
|
assert.Equal(t, firstLink, testCase.ExpectedFirstLink)
|
|
assert.Equal(t, images, testCase.ExpectedImages)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetImagesInMessageAttachments(t *testing.T) {
|
|
for _, test := range []struct {
|
|
Name string
|
|
Post *model.Post
|
|
Expected []string
|
|
}{
|
|
{
|
|
Name: "no attachments",
|
|
Post: &model.Post{},
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Name: "empty attachments",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{},
|
|
},
|
|
},
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Name: "attachment with no fields that can contain images",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Title: "This is the title",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Name: "images in text",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: " and ",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/logo", "https://example.com/icon"},
|
|
},
|
|
{
|
|
Name: "images in pretext",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Pretext: " and ",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/logo1", "https://example.com/icon1"},
|
|
},
|
|
{
|
|
Name: "images in fields",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: " and ",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/logo2", "https://example.com/icon2"},
|
|
},
|
|
{
|
|
Name: "image in author_icon",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
AuthorIcon: "https://example.com/icon2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/icon2"},
|
|
},
|
|
{
|
|
Name: "image in image_url",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
ImageURL: "https://example.com/image",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/image"},
|
|
},
|
|
{
|
|
Name: "image in thumb_url",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
ThumbURL: "https://example.com/image",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/image"},
|
|
},
|
|
{
|
|
Name: "image in footer_icon",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
FooterIcon: "https://example.com/image",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/image"},
|
|
},
|
|
{
|
|
Name: "images in multiple fields",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: "",
|
|
},
|
|
{
|
|
Value: "",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/logo", "https://example.com/icon"},
|
|
},
|
|
{
|
|
Name: "non-string field",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: 77,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{},
|
|
},
|
|
{
|
|
Name: "images in multiple locations",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: "",
|
|
Pretext: "",
|
|
Fields: []*model.SlackAttachmentField{
|
|
{
|
|
Value: "",
|
|
},
|
|
{
|
|
Value: "",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/text", "https://example.com/pretext", "https://example.com/field1", "https://example.com/field2"},
|
|
},
|
|
{
|
|
Name: "multiple attachments",
|
|
Post: &model.Post{
|
|
Props: map[string]interface{}{
|
|
"attachments": []*model.SlackAttachment{
|
|
{
|
|
Text: "",
|
|
},
|
|
{
|
|
Text: "",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []string{"https://example.com/logo", "https://example.com/icon"},
|
|
},
|
|
} {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
images := getImagesInMessageAttachments(test.Post)
|
|
|
|
assert.ElementsMatch(t, images, test.Expected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseLinkMetadata(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
defer th.TearDown()
|
|
|
|
imageURL := "http://example.com/test.png"
|
|
file, err := testutils.ReadTestFile("test.png")
|
|
require.Nil(t, err)
|
|
|
|
ogURL := "https://example.com/hello"
|
|
html := `
|
|
<html>
|
|
<head>
|
|
<meta property="og:title" content="Hello, World!">
|
|
<meta property="og:type" content="object">
|
|
<meta property="og:url" content="` + ogURL + `">
|
|
</head>
|
|
</html>`
|
|
|
|
makeImageReader := func() io.Reader {
|
|
return bytes.NewReader(file)
|
|
}
|
|
|
|
makeOpenGraphReader := func() io.Reader {
|
|
return strings.NewReader(html)
|
|
}
|
|
|
|
t.Run("image", func(t *testing.T) {
|
|
og, dimensions, err := th.App.parseLinkMetadata(imageURL, makeImageReader(), "image/png")
|
|
assert.Nil(t, err)
|
|
|
|
assert.Nil(t, og)
|
|
assert.Equal(t, &model.PostImage{
|
|
Width: 408,
|
|
Height: 336,
|
|
}, dimensions)
|
|
})
|
|
|
|
t.Run("malformed image", func(t *testing.T) {
|
|
og, dimensions, err := th.App.parseLinkMetadata(imageURL, makeOpenGraphReader(), "image/png")
|
|
assert.NotNil(t, err)
|
|
|
|
assert.Nil(t, og)
|
|
assert.Nil(t, dimensions)
|
|
})
|
|
|
|
t.Run("opengraph", func(t *testing.T) {
|
|
og, dimensions, err := th.App.parseLinkMetadata(ogURL, makeOpenGraphReader(), "text/html; charset=utf-8")
|
|
assert.Nil(t, err)
|
|
|
|
assert.NotNil(t, og)
|
|
assert.Equal(t, og.Title, "Hello, World!")
|
|
assert.Equal(t, og.Type, "object")
|
|
assert.Equal(t, og.URL, ogURL)
|
|
assert.Nil(t, dimensions)
|
|
})
|
|
|
|
t.Run("malformed opengraph", func(t *testing.T) {
|
|
og, dimensions, err := th.App.parseLinkMetadata(ogURL, makeImageReader(), "text/html; charset=utf-8")
|
|
assert.Nil(t, err)
|
|
|
|
assert.Nil(t, og)
|
|
assert.Nil(t, dimensions)
|
|
})
|
|
|
|
t.Run("neither", func(t *testing.T) {
|
|
og, dimensions, err := th.App.parseLinkMetadata("http://example.com/test.wad", strings.NewReader("garbage"), "application/x-doom")
|
|
assert.Nil(t, err)
|
|
|
|
assert.Nil(t, og)
|
|
assert.Nil(t, dimensions)
|
|
})
|
|
}
|
|
|
|
func TestParseImages(t *testing.T) {
|
|
for name, testCase := range map[string]struct {
|
|
FileName string
|
|
ExpectedWidth int
|
|
ExpectedHeight int
|
|
ExpectError bool
|
|
}{
|
|
"png": {
|
|
FileName: "test.png",
|
|
ExpectedWidth: 408,
|
|
ExpectedHeight: 336,
|
|
},
|
|
"animated gif": {
|
|
FileName: "testgif.gif",
|
|
ExpectedWidth: 118,
|
|
ExpectedHeight: 118,
|
|
},
|
|
"not an image": {
|
|
FileName: "README.md",
|
|
ExpectError: true,
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
file, err := testutils.ReadTestFile(testCase.FileName)
|
|
require.Nil(t, err)
|
|
|
|
dimensions, err := parseImages(bytes.NewReader(file))
|
|
if testCase.ExpectError {
|
|
require.NotNil(t, err)
|
|
} else {
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, dimensions)
|
|
require.Equal(t, testCase.ExpectedWidth, dimensions.Width)
|
|
require.Equal(t, testCase.ExpectedHeight, dimensions.Height)
|
|
}
|
|
})
|
|
}
|
|
}
|