mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* Prepare file upload infrastructure for Data Retention. This commit prepares the file upload infrastructure for the data retention feature that is under construction. Changes are: * Move file management code to utils to allow access to it from jobs. * From now on, store all file uploads in a top level folder which is the date of the day on which they were uploaded. This commit is based on Harrison Healey's branch, but updated to work with the latest master. * Use NewAppError
426 lines
14 KiB
Go
426 lines
14 KiB
Go
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/gif"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/platform/app"
|
|
"github.com/mattermost/platform/model"
|
|
"github.com/mattermost/platform/store"
|
|
"github.com/mattermost/platform/utils"
|
|
)
|
|
|
|
func TestGetEmoji(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
Client := th.BasicClient
|
|
|
|
EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
|
|
defer func() {
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
|
|
}()
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
|
|
|
|
emojis := []*model.Emoji{
|
|
{
|
|
CreatorId: model.NewId(),
|
|
Name: model.NewId(),
|
|
},
|
|
{
|
|
CreatorId: model.NewId(),
|
|
Name: model.NewId(),
|
|
},
|
|
{
|
|
CreatorId: model.NewId(),
|
|
Name: model.NewId(),
|
|
},
|
|
}
|
|
|
|
for i, emoji := range emojis {
|
|
emojis[i] = store.Must(app.Srv.Store.Emoji().Save(emoji)).(*model.Emoji)
|
|
}
|
|
defer func() {
|
|
for _, emoji := range emojis {
|
|
store.Must(app.Srv.Store.Emoji().Delete(emoji.Id, time.Now().Unix()))
|
|
}
|
|
}()
|
|
|
|
if returnedEmojis, err := Client.ListEmoji(); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
for _, emoji := range emojis {
|
|
found := false
|
|
|
|
for _, savedEmoji := range returnedEmojis {
|
|
if emoji.Id == savedEmoji.Id {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Fatalf("failed to get emoji with id %v", emoji.Id)
|
|
}
|
|
}
|
|
}
|
|
|
|
deleted := &model.Emoji{
|
|
CreatorId: model.NewId(),
|
|
Name: model.NewId(),
|
|
DeleteAt: 1,
|
|
}
|
|
deleted = store.Must(app.Srv.Store.Emoji().Save(deleted)).(*model.Emoji)
|
|
|
|
if returnedEmojis, err := Client.ListEmoji(); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
found := false
|
|
|
|
for _, savedEmoji := range returnedEmojis {
|
|
if deleted.Id == savedEmoji.Id {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if found {
|
|
t.Fatalf("shouldn't have gotten deleted emoji %v", deleted.Id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreateEmoji(t *testing.T) {
|
|
th := Setup().InitBasic().InitSystemAdmin()
|
|
Client := th.BasicClient
|
|
|
|
EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
|
|
defer func() {
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
|
|
}()
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = false
|
|
|
|
emoji := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
|
|
// try to create an emoji when they're disabled
|
|
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
|
t.Fatal("shouldn't be able to create an emoji when they're disabled")
|
|
}
|
|
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
|
|
|
|
// try to create a valid gif emoji when they're enabled
|
|
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
emoji = emojiResult
|
|
}
|
|
|
|
// try to create an emoji with a duplicate name
|
|
emoji2 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: emoji.Name,
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji2, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
|
t.Fatal("shouldn't be able to create an emoji with a duplicate name")
|
|
}
|
|
|
|
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
|
|
|
// try to create a valid animated gif emoji
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestAnimatedGif(t, 10, 10, 10), "image.gif"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
emoji = emojiResult
|
|
}
|
|
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
|
|
|
// try to create a valid jpeg emoji
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestJpeg(t, 10, 10), "image.jpeg"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
emoji = emojiResult
|
|
}
|
|
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
|
|
|
// try to create a valid png emoji
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestPng(t, 10, 10), "image.png"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
emoji = emojiResult
|
|
}
|
|
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
|
|
|
// try to create an emoji that's too wide
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 1000, 10), "image.gif"); err != nil {
|
|
t.Fatal("should be able to create an emoji that's too wide by resizing it")
|
|
}
|
|
|
|
// try to create an emoji that's too tall
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 1000), "image.gif"); err != nil {
|
|
t.Fatal("should be able to create an emoji that's too tall by resizing it")
|
|
}
|
|
|
|
// try to create an emoji that's too large
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji, utils.CreateTestAnimatedGif(t, 100, 100, 10000), "image.gif"); err == nil {
|
|
t.Fatal("shouldn't be able to create an emoji that's too large")
|
|
}
|
|
|
|
// try to create an emoji with data that isn't an image
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji, make([]byte, 100, 100), "image.gif"); err == nil {
|
|
t.Fatal("shouldn't be able to create an emoji with non-image data")
|
|
}
|
|
|
|
// try to create an emoji as another user
|
|
emoji = &model.Emoji{
|
|
CreatorId: th.BasicUser2.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
|
t.Fatal("shouldn't be able to create an emoji as another user")
|
|
}
|
|
}
|
|
|
|
func TestDeleteEmoji(t *testing.T) {
|
|
th := Setup().InitBasic().InitSystemAdmin()
|
|
Client := th.BasicClient
|
|
|
|
EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
|
|
defer func() {
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
|
|
}()
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = false
|
|
|
|
emoji1 := createTestEmoji(t, &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}, utils.CreateTestGif(t, 10, 10))
|
|
|
|
if _, err := Client.DeleteEmoji(emoji1.Id); err == nil {
|
|
t.Fatal("shouldn't have been able to delete an emoji when they're disabled")
|
|
}
|
|
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
|
|
|
|
if deleted, err := Client.DeleteEmoji(emoji1.Id); err != nil {
|
|
t.Fatal(err)
|
|
} else if !deleted {
|
|
t.Fatalf("should be able to delete your own emoji %v", emoji1.Id)
|
|
}
|
|
|
|
if _, err := Client.DeleteEmoji(emoji1.Id); err == nil {
|
|
t.Fatal("shouldn't be able to delete an already-deleted emoji")
|
|
}
|
|
|
|
emoji2 := createTestEmoji(t, &model.Emoji{
|
|
CreatorId: th.BasicUser2.Id,
|
|
Name: model.NewId(),
|
|
}, utils.CreateTestGif(t, 10, 10))
|
|
|
|
if _, err := Client.DeleteEmoji(emoji2.Id); err == nil {
|
|
t.Fatal("shouldn't be able to delete another user's emoji")
|
|
}
|
|
|
|
if deleted, err := th.SystemAdminClient.DeleteEmoji(emoji2.Id); err != nil {
|
|
t.Fatal(err)
|
|
} else if !deleted {
|
|
t.Fatalf("system admin should be able to delete anyone's emoji %v", emoji2.Id)
|
|
}
|
|
}
|
|
|
|
func createTestEmoji(t *testing.T, emoji *model.Emoji, imageData []byte) *model.Emoji {
|
|
emoji = store.Must(app.Srv.Store.Emoji().Save(emoji)).(*model.Emoji)
|
|
|
|
if err := utils.WriteFile(imageData, "emoji/"+emoji.Id+"/image"); err != nil {
|
|
store.Must(app.Srv.Store.Emoji().Delete(emoji.Id, time.Now().Unix()))
|
|
t.Fatalf("failed to write image: %v", err.Error())
|
|
}
|
|
|
|
return emoji
|
|
}
|
|
|
|
func TestGetEmojiImage(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
Client := th.BasicClient
|
|
|
|
EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
|
|
RestrictCustomEmojiCreation := *utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation
|
|
defer func() {
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
|
|
*utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation = RestrictCustomEmojiCreation
|
|
}()
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
|
|
*utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ALL
|
|
|
|
emoji1 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
emoji1 = Client.MustGeneric(Client.CreateEmoji(emoji1, utils.CreateTestGif(t, 10, 10), "image.gif")).(*model.Emoji)
|
|
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji1.Id)) }()
|
|
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = false
|
|
|
|
if _, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji1.Id), "", ""); err == nil {
|
|
t.Fatal("should've failed to get emoji image when disabled")
|
|
}
|
|
|
|
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
|
|
|
|
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji1.Id), "", ""); err != nil {
|
|
t.Fatal(err)
|
|
} else if resp.Header.Get("Content-Type") != "image/gif" {
|
|
t.Fatal("should've received a gif")
|
|
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
|
t.Fatalf("unable to identify received image: %v", err.Error())
|
|
} else if imageType != "gif" {
|
|
t.Fatal("should've received gif data")
|
|
}
|
|
|
|
emoji2 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
emoji2 = Client.MustGeneric(Client.CreateEmoji(emoji2, utils.CreateTestAnimatedGif(t, 10, 10, 10), "image.gif")).(*model.Emoji)
|
|
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji2.Id)) }()
|
|
|
|
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji2.Id), "", ""); err != nil {
|
|
t.Fatal(err)
|
|
} else if resp.Header.Get("Content-Type") != "image/gif" {
|
|
t.Fatal("should've received a gif")
|
|
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
|
t.Fatalf("unable to identify received image: %v", err.Error())
|
|
} else if imageType != "gif" {
|
|
t.Fatal("should've received gif data")
|
|
}
|
|
|
|
emoji3 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
emoji3 = Client.MustGeneric(Client.CreateEmoji(emoji3, utils.CreateTestJpeg(t, 10, 10), "image.jpeg")).(*model.Emoji)
|
|
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji3.Id)) }()
|
|
|
|
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji3.Id), "", ""); err != nil {
|
|
t.Fatal(err)
|
|
} else if resp.Header.Get("Content-Type") != "image/jpeg" {
|
|
t.Fatal("should've received a jpeg")
|
|
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
|
t.Fatalf("unable to identify received image: %v", err.Error())
|
|
} else if imageType != "jpeg" {
|
|
t.Fatal("should've received jpeg data")
|
|
}
|
|
|
|
emoji4 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
emoji4 = Client.MustGeneric(Client.CreateEmoji(emoji4, utils.CreateTestPng(t, 10, 10), "image.png")).(*model.Emoji)
|
|
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji4.Id)) }()
|
|
|
|
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji4.Id), "", ""); err != nil {
|
|
t.Fatal(err)
|
|
} else if resp.Header.Get("Content-Type") != "image/png" {
|
|
t.Fatal("should've received a png")
|
|
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
|
t.Fatalf("unable to identify received image: %v", err.Error())
|
|
} else if imageType != "png" {
|
|
t.Fatal("should've received png data")
|
|
}
|
|
|
|
emoji5 := &model.Emoji{
|
|
CreatorId: th.BasicUser.Id,
|
|
Name: model.NewId(),
|
|
}
|
|
emoji5 = Client.MustGeneric(Client.CreateEmoji(emoji5, utils.CreateTestPng(t, 10, 10), "image.png")).(*model.Emoji)
|
|
Client.MustGeneric(Client.DeleteEmoji(emoji5.Id))
|
|
|
|
if _, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji5.Id), "", ""); err == nil {
|
|
t.Fatal("should've failed to get image for deleted emoji")
|
|
}
|
|
}
|
|
|
|
func TestResizeEmoji(t *testing.T) {
|
|
// try to resize a jpeg image within MaxEmojiWidth and MaxEmojiHeight
|
|
small_img_data := utils.CreateTestJpeg(t, app.MaxEmojiWidth, app.MaxEmojiHeight)
|
|
if small_img, _, err := image.Decode(bytes.NewReader(small_img_data)); err != nil {
|
|
t.Fatal("failed to decode jpeg bytes to image.Image")
|
|
} else {
|
|
resized_img := resizeEmoji(small_img, small_img.Bounds().Dx(), small_img.Bounds().Dy())
|
|
if resized_img.Bounds().Dx() > app.MaxEmojiWidth || resized_img.Bounds().Dy() > app.MaxEmojiHeight {
|
|
t.Fatal("resized jpeg width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
|
}
|
|
if resized_img != small_img {
|
|
t.Fatal("should've returned small_img itself")
|
|
}
|
|
}
|
|
// try to resize a jpeg image
|
|
jpeg_data := utils.CreateTestJpeg(t, 256, 256)
|
|
if jpeg_img, _, err := image.Decode(bytes.NewReader(jpeg_data)); err != nil {
|
|
t.Fatal("failed to decode jpeg bytes to image.Image")
|
|
} else {
|
|
resized_jpeg := resizeEmoji(jpeg_img, jpeg_img.Bounds().Dx(), jpeg_img.Bounds().Dy())
|
|
if resized_jpeg.Bounds().Dx() > app.MaxEmojiWidth || resized_jpeg.Bounds().Dy() > app.MaxEmojiHeight {
|
|
t.Fatal("resized jpeg width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
|
}
|
|
}
|
|
// try to resize a png image
|
|
png_data := utils.CreateTestJpeg(t, 256, 256)
|
|
if png_img, _, err := image.Decode(bytes.NewReader(png_data)); err != nil {
|
|
t.Fatal("failed to decode png bytes to image.Image")
|
|
} else {
|
|
resized_png := resizeEmoji(png_img, png_img.Bounds().Dx(), png_img.Bounds().Dy())
|
|
if resized_png.Bounds().Dx() > app.MaxEmojiWidth || resized_png.Bounds().Dy() > app.MaxEmojiHeight {
|
|
t.Fatal("resized png width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
|
}
|
|
}
|
|
// try to resize an animated gif
|
|
gif_data := utils.CreateTestAnimatedGif(t, 256, 256, 10)
|
|
if gif_img, err := gif.DecodeAll(bytes.NewReader(gif_data)); err != nil {
|
|
t.Fatal("failed to decode gif bytes to gif.GIF")
|
|
} else {
|
|
resized_gif := resizeEmojiGif(gif_img)
|
|
if resized_gif.Config.Width > app.MaxEmojiWidth || resized_gif.Config.Height > app.MaxEmojiHeight {
|
|
t.Fatal("resized gif width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
|
}
|
|
if len(resized_gif.Image) != len(gif_img.Image) {
|
|
t.Fatal("resized gif should have the same number of frames as original gif")
|
|
}
|
|
}
|
|
}
|