gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

emojiupdate_test.go (17112B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package admin_test
     19 
     20 import (
     21 	"context"
     22 	"encoding/json"
     23 	"io/ioutil"
     24 	"net/http"
     25 	"net/http/httptest"
     26 	"testing"
     27 
     28 	"github.com/stretchr/testify/suite"
     29 	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
     30 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
     31 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     32 	"github.com/superseriousbusiness/gotosocial/testrig"
     33 )
     34 
     35 type EmojiUpdateTestSuite struct {
     36 	AdminStandardTestSuite
     37 }
     38 
     39 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateNewCategory() {
     40 	testEmoji := &gtsmodel.Emoji{}
     41 	*testEmoji = *suite.testEmojis["rainbow"]
     42 
     43 	// set up the request
     44 	requestBody, w, err := testrig.CreateMultipartFormData(
     45 		"", "",
     46 		map[string]string{
     47 			"category": "New Category", // this category doesn't exist yet
     48 			"type":     "modify",
     49 		})
     50 	if err != nil {
     51 		panic(err)
     52 	}
     53 	bodyBytes := requestBody.Bytes()
     54 	recorder := httptest.NewRecorder()
     55 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
     56 	ctx.AddParam(admin.IDKey, testEmoji.ID)
     57 
     58 	// call the handler
     59 	suite.adminModule.EmojiPATCHHandler(ctx)
     60 
     61 	// 1. we should have OK because our request was valid
     62 	suite.Equal(http.StatusOK, recorder.Code)
     63 
     64 	// 2. we should have no error message in the result body
     65 	result := recorder.Result()
     66 	defer result.Body.Close()
     67 
     68 	// check the response
     69 	b, err := ioutil.ReadAll(result.Body)
     70 	suite.NoError(err)
     71 	suite.NotEmpty(b)
     72 
     73 	// response should be an admin model emoji
     74 	adminEmoji := &apimodel.AdminEmoji{}
     75 	err = json.Unmarshal(b, adminEmoji)
     76 	suite.NoError(err)
     77 
     78 	// appropriate fields should be set
     79 	suite.Equal("rainbow", adminEmoji.Shortcode)
     80 	suite.NotEmpty(adminEmoji.URL)
     81 	suite.NotEmpty(adminEmoji.StaticURL)
     82 	suite.True(adminEmoji.VisibleInPicker)
     83 
     84 	// emoji should be in the db
     85 	dbEmoji, err := suite.db.GetEmojiByShortcodeDomain(context.Background(), adminEmoji.Shortcode, "")
     86 	suite.NoError(err)
     87 
     88 	// check fields on the emoji
     89 	suite.NotEmpty(dbEmoji.ID)
     90 	suite.Equal("rainbow", dbEmoji.Shortcode)
     91 	suite.Empty(dbEmoji.Domain)
     92 	suite.Empty(dbEmoji.ImageRemoteURL)
     93 	suite.Empty(dbEmoji.ImageStaticRemoteURL)
     94 	suite.Equal(adminEmoji.URL, dbEmoji.ImageURL)
     95 	suite.Equal(adminEmoji.StaticURL, dbEmoji.ImageStaticURL)
     96 	suite.NotEmpty(dbEmoji.ImagePath)
     97 	suite.NotEmpty(dbEmoji.ImageStaticPath)
     98 	suite.Equal("image/png", dbEmoji.ImageContentType)
     99 	suite.Equal("image/png", dbEmoji.ImageStaticContentType)
    100 	suite.Equal(36702, dbEmoji.ImageFileSize)
    101 	suite.Equal(10413, dbEmoji.ImageStaticFileSize)
    102 	suite.False(*dbEmoji.Disabled)
    103 	suite.NotEmpty(dbEmoji.URI)
    104 	suite.True(*dbEmoji.VisibleInPicker)
    105 	suite.NotEmpty(dbEmoji.CategoryID)
    106 
    107 	// emoji should be in storage
    108 	emojiBytes, err := suite.storage.Get(ctx, dbEmoji.ImagePath)
    109 	suite.NoError(err)
    110 	suite.Len(emojiBytes, dbEmoji.ImageFileSize)
    111 	emojiStaticBytes, err := suite.storage.Get(ctx, dbEmoji.ImageStaticPath)
    112 	suite.NoError(err)
    113 	suite.Len(emojiStaticBytes, dbEmoji.ImageStaticFileSize)
    114 }
    115 
    116 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateSwitchCategory() {
    117 	testEmoji := &gtsmodel.Emoji{}
    118 	*testEmoji = *suite.testEmojis["rainbow"]
    119 
    120 	// set up the request
    121 	requestBody, w, err := testrig.CreateMultipartFormData(
    122 		"", "",
    123 		map[string]string{
    124 			"type":     "modify",
    125 			"category": "cute stuff",
    126 		})
    127 	if err != nil {
    128 		panic(err)
    129 	}
    130 	bodyBytes := requestBody.Bytes()
    131 	recorder := httptest.NewRecorder()
    132 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    133 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    134 
    135 	// call the handler
    136 	suite.adminModule.EmojiPATCHHandler(ctx)
    137 
    138 	// 1. we should have OK because our request was valid
    139 	suite.Equal(http.StatusOK, recorder.Code)
    140 
    141 	// 2. we should have no error message in the result body
    142 	result := recorder.Result()
    143 	defer result.Body.Close()
    144 
    145 	// check the response
    146 	b, err := ioutil.ReadAll(result.Body)
    147 	suite.NoError(err)
    148 	suite.NotEmpty(b)
    149 
    150 	// response should be an admin model emoji
    151 	adminEmoji := &apimodel.AdminEmoji{}
    152 	err = json.Unmarshal(b, adminEmoji)
    153 	suite.NoError(err)
    154 
    155 	// appropriate fields should be set
    156 	suite.Equal("rainbow", adminEmoji.Shortcode)
    157 	suite.NotEmpty(adminEmoji.URL)
    158 	suite.NotEmpty(adminEmoji.StaticURL)
    159 	suite.True(adminEmoji.VisibleInPicker)
    160 
    161 	// emoji should be in the db
    162 	dbEmoji, err := suite.db.GetEmojiByShortcodeDomain(context.Background(), adminEmoji.Shortcode, "")
    163 	suite.NoError(err)
    164 
    165 	// check fields on the emoji
    166 	suite.NotEmpty(dbEmoji.ID)
    167 	suite.Equal("rainbow", dbEmoji.Shortcode)
    168 	suite.Empty(dbEmoji.Domain)
    169 	suite.Empty(dbEmoji.ImageRemoteURL)
    170 	suite.Empty(dbEmoji.ImageStaticRemoteURL)
    171 	suite.Equal(adminEmoji.URL, dbEmoji.ImageURL)
    172 	suite.Equal(adminEmoji.StaticURL, dbEmoji.ImageStaticURL)
    173 	suite.NotEmpty(dbEmoji.ImagePath)
    174 	suite.NotEmpty(dbEmoji.ImageStaticPath)
    175 	suite.Equal("image/png", dbEmoji.ImageContentType)
    176 	suite.Equal("image/png", dbEmoji.ImageStaticContentType)
    177 	suite.Equal(36702, dbEmoji.ImageFileSize)
    178 	suite.Equal(10413, dbEmoji.ImageStaticFileSize)
    179 	suite.False(*dbEmoji.Disabled)
    180 	suite.NotEmpty(dbEmoji.URI)
    181 	suite.True(*dbEmoji.VisibleInPicker)
    182 	suite.NotEmpty(dbEmoji.CategoryID)
    183 
    184 	// emoji should be in storage
    185 	emojiBytes, err := suite.storage.Get(ctx, dbEmoji.ImagePath)
    186 	suite.NoError(err)
    187 	suite.Len(emojiBytes, dbEmoji.ImageFileSize)
    188 	emojiStaticBytes, err := suite.storage.Get(ctx, dbEmoji.ImageStaticPath)
    189 	suite.NoError(err)
    190 	suite.Len(emojiStaticBytes, dbEmoji.ImageStaticFileSize)
    191 }
    192 
    193 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyRemoteToLocal() {
    194 	testEmoji := &gtsmodel.Emoji{}
    195 	*testEmoji = *suite.testEmojis["yell"]
    196 
    197 	// set up the request
    198 	requestBody, w, err := testrig.CreateMultipartFormData(
    199 		"", "",
    200 		map[string]string{
    201 			"type":      "copy",
    202 			"category":  "emojis i stole",
    203 			"shortcode": "yell",
    204 		})
    205 	if err != nil {
    206 		panic(err)
    207 	}
    208 	bodyBytes := requestBody.Bytes()
    209 	recorder := httptest.NewRecorder()
    210 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    211 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    212 
    213 	// call the handler
    214 	suite.adminModule.EmojiPATCHHandler(ctx)
    215 
    216 	// 1. we should have OK because our request was valid
    217 	suite.Equal(http.StatusOK, recorder.Code)
    218 
    219 	// 2. we should have no error message in the result body
    220 	result := recorder.Result()
    221 	defer result.Body.Close()
    222 
    223 	// check the response
    224 	b, err := ioutil.ReadAll(result.Body)
    225 	suite.NoError(err)
    226 	suite.NotEmpty(b)
    227 
    228 	// response should be an admin model emoji
    229 	adminEmoji := &apimodel.AdminEmoji{}
    230 	err = json.Unmarshal(b, adminEmoji)
    231 	suite.NoError(err)
    232 
    233 	// appropriate fields should be set
    234 	suite.Equal("yell", adminEmoji.Shortcode)
    235 	suite.NotEmpty(adminEmoji.URL)
    236 	suite.NotEmpty(adminEmoji.StaticURL)
    237 	suite.True(adminEmoji.VisibleInPicker)
    238 
    239 	// emoji should be in the db
    240 	dbEmoji, err := suite.db.GetEmojiByShortcodeDomain(context.Background(), adminEmoji.Shortcode, "")
    241 	suite.NoError(err)
    242 
    243 	// check fields on the emoji
    244 	suite.NotEmpty(dbEmoji.ID)
    245 	suite.Equal("yell", dbEmoji.Shortcode)
    246 	suite.Empty(dbEmoji.Domain)
    247 	suite.Empty(dbEmoji.ImageRemoteURL)
    248 	suite.Empty(dbEmoji.ImageStaticRemoteURL)
    249 	suite.Equal(adminEmoji.URL, dbEmoji.ImageURL)
    250 	suite.Equal(adminEmoji.StaticURL, dbEmoji.ImageStaticURL)
    251 	suite.NotEmpty(dbEmoji.ImagePath)
    252 	suite.NotEmpty(dbEmoji.ImageStaticPath)
    253 	suite.Equal("image/png", dbEmoji.ImageContentType)
    254 	suite.Equal("image/png", dbEmoji.ImageStaticContentType)
    255 	suite.Equal(10889, dbEmoji.ImageFileSize)
    256 	suite.Equal(10672, dbEmoji.ImageStaticFileSize)
    257 	suite.False(*dbEmoji.Disabled)
    258 	suite.NotEmpty(dbEmoji.URI)
    259 	suite.True(*dbEmoji.VisibleInPicker)
    260 	suite.NotEmpty(dbEmoji.CategoryID)
    261 
    262 	// emoji should be in storage
    263 	emojiBytes, err := suite.storage.Get(ctx, dbEmoji.ImagePath)
    264 	suite.NoError(err)
    265 	suite.Len(emojiBytes, dbEmoji.ImageFileSize)
    266 	emojiStaticBytes, err := suite.storage.Get(ctx, dbEmoji.ImageStaticPath)
    267 	suite.NoError(err)
    268 	suite.Len(emojiStaticBytes, dbEmoji.ImageStaticFileSize)
    269 }
    270 
    271 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateDisableEmoji() {
    272 	testEmoji := &gtsmodel.Emoji{}
    273 	*testEmoji = *suite.testEmojis["yell"]
    274 
    275 	// set up the request
    276 	requestBody, w, err := testrig.CreateMultipartFormData(
    277 		"", "",
    278 		map[string]string{
    279 			"type": "disable",
    280 		})
    281 	if err != nil {
    282 		panic(err)
    283 	}
    284 	bodyBytes := requestBody.Bytes()
    285 	recorder := httptest.NewRecorder()
    286 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    287 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    288 
    289 	// call the handler
    290 	suite.adminModule.EmojiPATCHHandler(ctx)
    291 
    292 	// 1. we should have OK because our request was valid
    293 	suite.Equal(http.StatusOK, recorder.Code)
    294 
    295 	// 2. we should have no error message in the result body
    296 	result := recorder.Result()
    297 	defer result.Body.Close()
    298 
    299 	// check the response
    300 	b, err := ioutil.ReadAll(result.Body)
    301 	suite.NoError(err)
    302 	suite.NotEmpty(b)
    303 
    304 	// response should be an admin model emoji
    305 	adminEmoji := &apimodel.AdminEmoji{}
    306 	err = json.Unmarshal(b, adminEmoji)
    307 	suite.NoError(err)
    308 
    309 	suite.True(adminEmoji.Disabled)
    310 }
    311 
    312 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateDisableLocalEmoji() {
    313 	testEmoji := &gtsmodel.Emoji{}
    314 	*testEmoji = *suite.testEmojis["rainbow"]
    315 
    316 	// set up the request
    317 	requestBody, w, err := testrig.CreateMultipartFormData(
    318 		"", "",
    319 		map[string]string{
    320 			"type": "disable",
    321 		})
    322 	if err != nil {
    323 		panic(err)
    324 	}
    325 	bodyBytes := requestBody.Bytes()
    326 	recorder := httptest.NewRecorder()
    327 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    328 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    329 
    330 	// call the handler
    331 	suite.adminModule.EmojiPATCHHandler(ctx)
    332 	suite.Equal(http.StatusBadRequest, recorder.Code)
    333 
    334 	// 2. we should have no error message in the result body
    335 	result := recorder.Result()
    336 	defer result.Body.Close()
    337 
    338 	// check the response
    339 	b, err := ioutil.ReadAll(result.Body)
    340 	suite.NoError(err)
    341 
    342 	suite.Equal(`{"error":"Bad Request: emojiUpdateDisable: emoji 01F8MH9H8E4VG3KDYJR9EGPXCQ is not a remote emoji, cannot disable it via this endpoint"}`, string(b))
    343 }
    344 
    345 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateModifyRemoteEmoji() {
    346 	testEmoji := &gtsmodel.Emoji{}
    347 	*testEmoji = *suite.testEmojis["yell"]
    348 
    349 	// set up the request
    350 	requestBody, w, err := testrig.CreateMultipartFormData(
    351 		"image", "../../../../testrig/media/kip-original.gif",
    352 		map[string]string{
    353 			"type": "modify",
    354 		})
    355 	if err != nil {
    356 		panic(err)
    357 	}
    358 	bodyBytes := requestBody.Bytes()
    359 	recorder := httptest.NewRecorder()
    360 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    361 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    362 
    363 	// call the handler
    364 	suite.adminModule.EmojiPATCHHandler(ctx)
    365 	suite.Equal(http.StatusBadRequest, recorder.Code)
    366 
    367 	// 2. we should have no error message in the result body
    368 	result := recorder.Result()
    369 	defer result.Body.Close()
    370 
    371 	// check the response
    372 	b, err := ioutil.ReadAll(result.Body)
    373 	suite.NoError(err)
    374 
    375 	suite.Equal(`{"error":"Bad Request: emojiUpdateModify: emoji 01GD5KP5CQEE1R3X43Y1EHS2CW is not a local emoji, cannot do a modify action on it"}`, string(b))
    376 }
    377 
    378 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateModifyNoParams() {
    379 	testEmoji := &gtsmodel.Emoji{}
    380 	*testEmoji = *suite.testEmojis["rainbow"]
    381 
    382 	// set up the request
    383 	requestBody, w, err := testrig.CreateMultipartFormData(
    384 		"", "",
    385 		map[string]string{
    386 			"type": "modify",
    387 		})
    388 	if err != nil {
    389 		panic(err)
    390 	}
    391 	bodyBytes := requestBody.Bytes()
    392 	recorder := httptest.NewRecorder()
    393 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    394 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    395 
    396 	// call the handler
    397 	suite.adminModule.EmojiPATCHHandler(ctx)
    398 	suite.Equal(http.StatusBadRequest, recorder.Code)
    399 
    400 	// 2. we should have no error message in the result body
    401 	result := recorder.Result()
    402 	defer result.Body.Close()
    403 
    404 	// check the response
    405 	b, err := ioutil.ReadAll(result.Body)
    406 	suite.NoError(err)
    407 
    408 	suite.Equal(`{"error":"Bad Request: emoji action type was 'modify' but no image or category name was provided"}`, string(b))
    409 }
    410 
    411 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyLocalToLocal() {
    412 	testEmoji := &gtsmodel.Emoji{}
    413 	*testEmoji = *suite.testEmojis["rainbow"]
    414 
    415 	// set up the request
    416 	requestBody, w, err := testrig.CreateMultipartFormData(
    417 		"", "",
    418 		map[string]string{
    419 			"type":      "copy",
    420 			"shortcode": "bottoms",
    421 		})
    422 	if err != nil {
    423 		panic(err)
    424 	}
    425 	bodyBytes := requestBody.Bytes()
    426 	recorder := httptest.NewRecorder()
    427 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    428 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    429 
    430 	// call the handler
    431 	suite.adminModule.EmojiPATCHHandler(ctx)
    432 	suite.Equal(http.StatusBadRequest, recorder.Code)
    433 
    434 	// 2. we should have no error message in the result body
    435 	result := recorder.Result()
    436 	defer result.Body.Close()
    437 
    438 	// check the response
    439 	b, err := ioutil.ReadAll(result.Body)
    440 	suite.NoError(err)
    441 
    442 	suite.Equal(`{"error":"Bad Request: emojiUpdateCopy: emoji 01F8MH9H8E4VG3KDYJR9EGPXCQ is not a remote emoji, cannot copy it to local"}`, string(b))
    443 }
    444 
    445 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyEmptyShortcode() {
    446 	testEmoji := &gtsmodel.Emoji{}
    447 	*testEmoji = *suite.testEmojis["yell"]
    448 
    449 	// set up the request
    450 	requestBody, w, err := testrig.CreateMultipartFormData(
    451 		"", "",
    452 		map[string]string{
    453 			"type":      "copy",
    454 			"shortcode": "",
    455 		})
    456 	if err != nil {
    457 		panic(err)
    458 	}
    459 	bodyBytes := requestBody.Bytes()
    460 	recorder := httptest.NewRecorder()
    461 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    462 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    463 
    464 	// call the handler
    465 	suite.adminModule.EmojiPATCHHandler(ctx)
    466 	suite.Equal(http.StatusBadRequest, recorder.Code)
    467 
    468 	// 2. we should have no error message in the result body
    469 	result := recorder.Result()
    470 	defer result.Body.Close()
    471 
    472 	// check the response
    473 	b, err := ioutil.ReadAll(result.Body)
    474 	suite.NoError(err)
    475 
    476 	suite.Equal(`{"error":"Bad Request: shortcode  did not pass validation, must be between 2 and 30 characters, letters, numbers, and underscores only"}`, string(b))
    477 }
    478 
    479 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyNoShortcode() {
    480 	testEmoji := &gtsmodel.Emoji{}
    481 	*testEmoji = *suite.testEmojis["yell"]
    482 
    483 	// set up the request
    484 	requestBody, w, err := testrig.CreateMultipartFormData(
    485 		"", "",
    486 		map[string]string{
    487 			"type": "copy",
    488 		})
    489 	if err != nil {
    490 		panic(err)
    491 	}
    492 	bodyBytes := requestBody.Bytes()
    493 	recorder := httptest.NewRecorder()
    494 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    495 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    496 
    497 	// call the handler
    498 	suite.adminModule.EmojiPATCHHandler(ctx)
    499 	suite.Equal(http.StatusBadRequest, recorder.Code)
    500 
    501 	// 2. we should have no error message in the result body
    502 	result := recorder.Result()
    503 	defer result.Body.Close()
    504 
    505 	// check the response
    506 	b, err := ioutil.ReadAll(result.Body)
    507 	suite.NoError(err)
    508 
    509 	suite.Equal(`{"error":"Bad Request: emoji action type was 'copy' but no shortcode was provided"}`, string(b))
    510 }
    511 
    512 func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyShortcodeAlreadyInUse() {
    513 	testEmoji := &gtsmodel.Emoji{}
    514 	*testEmoji = *suite.testEmojis["yell"]
    515 
    516 	// set up the request
    517 	requestBody, w, err := testrig.CreateMultipartFormData(
    518 		"", "",
    519 		map[string]string{
    520 			"type":      "copy",
    521 			"shortcode": "rainbow",
    522 		})
    523 	if err != nil {
    524 		panic(err)
    525 	}
    526 	bodyBytes := requestBody.Bytes()
    527 	recorder := httptest.NewRecorder()
    528 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType())
    529 	ctx.AddParam(admin.IDKey, testEmoji.ID)
    530 
    531 	// call the handler
    532 	suite.adminModule.EmojiPATCHHandler(ctx)
    533 	suite.Equal(http.StatusConflict, recorder.Code)
    534 
    535 	// 2. we should have no error message in the result body
    536 	result := recorder.Result()
    537 	defer result.Body.Close()
    538 
    539 	// check the response
    540 	b, err := ioutil.ReadAll(result.Body)
    541 	suite.NoError(err)
    542 
    543 	suite.Equal(`{"error":"Conflict: emojiUpdateCopy: emoji 01GD5KP5CQEE1R3X43Y1EHS2CW could not be copied, emoji with shortcode rainbow already exists on this instance"}`, string(b))
    544 }
    545 
    546 func TestEmojiUpdateTestSuite(t *testing.T) {
    547 	suite.Run(t, &EmojiUpdateTestSuite{})
    548 }