gtsocial-umbx

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

instancepatch_test.go (24740B)


      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 instance_test
     19 
     20 import (
     21 	"bytes"
     22 	"context"
     23 	"encoding/json"
     24 	"io"
     25 	"net/http"
     26 	"net/http/httptest"
     27 	"testing"
     28 
     29 	"github.com/stretchr/testify/suite"
     30 	"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
     31 	"github.com/superseriousbusiness/gotosocial/internal/oauth"
     32 	"github.com/superseriousbusiness/gotosocial/testrig"
     33 )
     34 
     35 type InstancePatchTestSuite struct {
     36 	InstanceStandardTestSuite
     37 }
     38 
     39 func (suite *InstancePatchTestSuite) instancePatch(fieldName string, fileName string, extraFields map[string]string) (code int, body []byte) {
     40 	requestBody, w, err := testrig.CreateMultipartFormData(fieldName, fileName, extraFields)
     41 	if err != nil {
     42 		suite.FailNow(err.Error())
     43 	}
     44 
     45 	recorder := httptest.NewRecorder()
     46 	ctx := suite.newContext(recorder, http.MethodPatch, instance.InstanceInformationPathV1, requestBody.Bytes(), w.FormDataContentType(), true)
     47 
     48 	suite.instanceModule.InstanceUpdatePATCHHandler(ctx)
     49 
     50 	result := recorder.Result()
     51 	defer result.Body.Close()
     52 
     53 	b, err := io.ReadAll(result.Body)
     54 	if err != nil {
     55 		suite.FailNow(err.Error())
     56 	}
     57 
     58 	return recorder.Code, b
     59 }
     60 
     61 func (suite *InstancePatchTestSuite) TestInstancePatch1() {
     62 	code, b := suite.instancePatch("", "", map[string]string{
     63 		"title":            "Example Instance",
     64 		"contact_username": "admin",
     65 		"contact_email":    "someone@example.org",
     66 	})
     67 
     68 	if expectedCode := http.StatusOK; code != expectedCode {
     69 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
     70 	}
     71 
     72 	dst := new(bytes.Buffer)
     73 	if err := json.Indent(dst, b, "", "  "); err != nil {
     74 		suite.FailNow(err.Error())
     75 	}
     76 
     77 	suite.Equal(`{
     78   "uri": "http://localhost:8080",
     79   "account_domain": "localhost:8080",
     80   "title": "Example Instance",
     81   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
     82   "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
     83   "email": "someone@example.org",
     84   "version": "0.0.0-testrig",
     85   "languages": [],
     86   "registrations": true,
     87   "approval_required": true,
     88   "invites_enabled": false,
     89   "configuration": {
     90     "statuses": {
     91       "max_characters": 5000,
     92       "max_media_attachments": 6,
     93       "characters_reserved_per_url": 25,
     94       "supported_mime_types": [
     95         "text/plain",
     96         "text/markdown"
     97       ]
     98     },
     99     "media_attachments": {
    100       "supported_mime_types": [
    101         "image/jpeg",
    102         "image/gif",
    103         "image/png",
    104         "image/webp",
    105         "video/mp4"
    106       ],
    107       "image_size_limit": 10485760,
    108       "image_matrix_limit": 16777216,
    109       "video_size_limit": 41943040,
    110       "video_frame_rate_limit": 60,
    111       "video_matrix_limit": 16777216
    112     },
    113     "polls": {
    114       "max_options": 6,
    115       "max_characters_per_option": 50,
    116       "min_expiration": 300,
    117       "max_expiration": 2629746
    118     },
    119     "accounts": {
    120       "allow_custom_css": true,
    121       "max_featured_tags": 10,
    122       "max_profile_fields": 6
    123     },
    124     "emojis": {
    125       "emoji_size_limit": 51200
    126     }
    127   },
    128   "urls": {
    129     "streaming_api": "wss://localhost:8080"
    130   },
    131   "stats": {
    132     "domain_count": 2,
    133     "status_count": 16,
    134     "user_count": 4
    135   },
    136   "thumbnail": "http://localhost:8080/assets/logo.png",
    137   "contact_account": {
    138     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    139     "username": "admin",
    140     "acct": "admin",
    141     "display_name": "",
    142     "locked": false,
    143     "discoverable": true,
    144     "bot": false,
    145     "created_at": "2022-05-17T13:10:59.000Z",
    146     "note": "",
    147     "url": "http://localhost:8080/@admin",
    148     "avatar": "",
    149     "avatar_static": "",
    150     "header": "http://localhost:8080/assets/default_header.png",
    151     "header_static": "http://localhost:8080/assets/default_header.png",
    152     "followers_count": 1,
    153     "following_count": 1,
    154     "statuses_count": 4,
    155     "last_status_at": "2021-10-20T10:41:37.000Z",
    156     "emojis": [],
    157     "fields": [],
    158     "enable_rss": true,
    159     "role": {
    160       "name": "admin"
    161     }
    162   },
    163   "max_toot_chars": 5000
    164 }`, dst.String())
    165 }
    166 
    167 func (suite *InstancePatchTestSuite) TestInstancePatch2() {
    168 	code, b := suite.instancePatch("", "", map[string]string{
    169 		"title": "<p>Geoff's Instance</p>",
    170 	})
    171 
    172 	if expectedCode := http.StatusOK; code != expectedCode {
    173 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    174 	}
    175 
    176 	dst := new(bytes.Buffer)
    177 	if err := json.Indent(dst, b, "", "  "); err != nil {
    178 		suite.FailNow(err.Error())
    179 	}
    180 
    181 	suite.Equal(`{
    182   "uri": "http://localhost:8080",
    183   "account_domain": "localhost:8080",
    184   "title": "Geoff's Instance",
    185   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    186   "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    187   "email": "admin@example.org",
    188   "version": "0.0.0-testrig",
    189   "languages": [],
    190   "registrations": true,
    191   "approval_required": true,
    192   "invites_enabled": false,
    193   "configuration": {
    194     "statuses": {
    195       "max_characters": 5000,
    196       "max_media_attachments": 6,
    197       "characters_reserved_per_url": 25,
    198       "supported_mime_types": [
    199         "text/plain",
    200         "text/markdown"
    201       ]
    202     },
    203     "media_attachments": {
    204       "supported_mime_types": [
    205         "image/jpeg",
    206         "image/gif",
    207         "image/png",
    208         "image/webp",
    209         "video/mp4"
    210       ],
    211       "image_size_limit": 10485760,
    212       "image_matrix_limit": 16777216,
    213       "video_size_limit": 41943040,
    214       "video_frame_rate_limit": 60,
    215       "video_matrix_limit": 16777216
    216     },
    217     "polls": {
    218       "max_options": 6,
    219       "max_characters_per_option": 50,
    220       "min_expiration": 300,
    221       "max_expiration": 2629746
    222     },
    223     "accounts": {
    224       "allow_custom_css": true,
    225       "max_featured_tags": 10,
    226       "max_profile_fields": 6
    227     },
    228     "emojis": {
    229       "emoji_size_limit": 51200
    230     }
    231   },
    232   "urls": {
    233     "streaming_api": "wss://localhost:8080"
    234   },
    235   "stats": {
    236     "domain_count": 2,
    237     "status_count": 16,
    238     "user_count": 4
    239   },
    240   "thumbnail": "http://localhost:8080/assets/logo.png",
    241   "contact_account": {
    242     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    243     "username": "admin",
    244     "acct": "admin",
    245     "display_name": "",
    246     "locked": false,
    247     "discoverable": true,
    248     "bot": false,
    249     "created_at": "2022-05-17T13:10:59.000Z",
    250     "note": "",
    251     "url": "http://localhost:8080/@admin",
    252     "avatar": "",
    253     "avatar_static": "",
    254     "header": "http://localhost:8080/assets/default_header.png",
    255     "header_static": "http://localhost:8080/assets/default_header.png",
    256     "followers_count": 1,
    257     "following_count": 1,
    258     "statuses_count": 4,
    259     "last_status_at": "2021-10-20T10:41:37.000Z",
    260     "emojis": [],
    261     "fields": [],
    262     "enable_rss": true,
    263     "role": {
    264       "name": "admin"
    265     }
    266   },
    267   "max_toot_chars": 5000
    268 }`, dst.String())
    269 }
    270 
    271 func (suite *InstancePatchTestSuite) TestInstancePatch3() {
    272 	code, b := suite.instancePatch("", "", map[string]string{
    273 		"short_description": "<p>This is some html, which is <em>allowed</em> in short descriptions.</p>",
    274 	})
    275 
    276 	if expectedCode := http.StatusOK; code != expectedCode {
    277 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    278 	}
    279 
    280 	dst := new(bytes.Buffer)
    281 	if err := json.Indent(dst, b, "", "  "); err != nil {
    282 		suite.FailNow(err.Error())
    283 	}
    284 
    285 	suite.Equal(`{
    286   "uri": "http://localhost:8080",
    287   "account_domain": "localhost:8080",
    288   "title": "GoToSocial Testrig Instance",
    289   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    290   "short_description": "\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e",
    291   "email": "admin@example.org",
    292   "version": "0.0.0-testrig",
    293   "languages": [],
    294   "registrations": true,
    295   "approval_required": true,
    296   "invites_enabled": false,
    297   "configuration": {
    298     "statuses": {
    299       "max_characters": 5000,
    300       "max_media_attachments": 6,
    301       "characters_reserved_per_url": 25,
    302       "supported_mime_types": [
    303         "text/plain",
    304         "text/markdown"
    305       ]
    306     },
    307     "media_attachments": {
    308       "supported_mime_types": [
    309         "image/jpeg",
    310         "image/gif",
    311         "image/png",
    312         "image/webp",
    313         "video/mp4"
    314       ],
    315       "image_size_limit": 10485760,
    316       "image_matrix_limit": 16777216,
    317       "video_size_limit": 41943040,
    318       "video_frame_rate_limit": 60,
    319       "video_matrix_limit": 16777216
    320     },
    321     "polls": {
    322       "max_options": 6,
    323       "max_characters_per_option": 50,
    324       "min_expiration": 300,
    325       "max_expiration": 2629746
    326     },
    327     "accounts": {
    328       "allow_custom_css": true,
    329       "max_featured_tags": 10,
    330       "max_profile_fields": 6
    331     },
    332     "emojis": {
    333       "emoji_size_limit": 51200
    334     }
    335   },
    336   "urls": {
    337     "streaming_api": "wss://localhost:8080"
    338   },
    339   "stats": {
    340     "domain_count": 2,
    341     "status_count": 16,
    342     "user_count": 4
    343   },
    344   "thumbnail": "http://localhost:8080/assets/logo.png",
    345   "contact_account": {
    346     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    347     "username": "admin",
    348     "acct": "admin",
    349     "display_name": "",
    350     "locked": false,
    351     "discoverable": true,
    352     "bot": false,
    353     "created_at": "2022-05-17T13:10:59.000Z",
    354     "note": "",
    355     "url": "http://localhost:8080/@admin",
    356     "avatar": "",
    357     "avatar_static": "",
    358     "header": "http://localhost:8080/assets/default_header.png",
    359     "header_static": "http://localhost:8080/assets/default_header.png",
    360     "followers_count": 1,
    361     "following_count": 1,
    362     "statuses_count": 4,
    363     "last_status_at": "2021-10-20T10:41:37.000Z",
    364     "emojis": [],
    365     "fields": [],
    366     "enable_rss": true,
    367     "role": {
    368       "name": "admin"
    369     }
    370   },
    371   "max_toot_chars": 5000
    372 }`, dst.String())
    373 }
    374 
    375 func (suite *InstancePatchTestSuite) TestInstancePatch4() {
    376 	code, b := suite.instancePatch("", "", map[string]string{
    377 		"": "",
    378 	})
    379 
    380 	if expectedCode := http.StatusBadRequest; code != expectedCode {
    381 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    382 	}
    383 
    384 	dst := new(bytes.Buffer)
    385 	if err := json.Indent(dst, b, "", "  "); err != nil {
    386 		suite.FailNow(err.Error())
    387 	}
    388 
    389 	suite.Equal(`{"error":"Bad Request: empty form submitted"}`, string(b))
    390 }
    391 
    392 func (suite *InstancePatchTestSuite) TestInstancePatch5() {
    393 	requestBody, w, err := testrig.CreateMultipartFormData(
    394 		"", "",
    395 		map[string]string{
    396 			"short_description": "<p>This is some html, which is <em>allowed</em> in short descriptions.</p>",
    397 		})
    398 	if err != nil {
    399 		panic(err)
    400 	}
    401 	bodyBytes := requestBody.Bytes()
    402 
    403 	// set up the request
    404 	recorder := httptest.NewRecorder()
    405 	ctx := suite.newContext(recorder, http.MethodPatch, instance.InstanceInformationPathV1, bodyBytes, w.FormDataContentType(), true)
    406 
    407 	ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
    408 	ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
    409 	ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
    410 	ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
    411 
    412 	// call the handler
    413 	suite.instanceModule.InstanceUpdatePATCHHandler(ctx)
    414 
    415 	suite.Equal(http.StatusForbidden, recorder.Code)
    416 
    417 	result := recorder.Result()
    418 	defer result.Body.Close()
    419 
    420 	b, err := io.ReadAll(result.Body)
    421 	suite.NoError(err)
    422 
    423 	suite.Equal(`{"error":"Forbidden: user is not an admin so cannot update instance settings"}`, string(b))
    424 }
    425 
    426 func (suite *InstancePatchTestSuite) TestInstancePatch6() {
    427 	code, b := suite.instancePatch("", "", map[string]string{
    428 		"contact_email": "",
    429 	})
    430 
    431 	if expectedCode := http.StatusOK; code != expectedCode {
    432 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    433 	}
    434 
    435 	dst := new(bytes.Buffer)
    436 	if err := json.Indent(dst, b, "", "  "); err != nil {
    437 		suite.FailNow(err.Error())
    438 	}
    439 
    440 	suite.Equal(`{
    441   "uri": "http://localhost:8080",
    442   "account_domain": "localhost:8080",
    443   "title": "GoToSocial Testrig Instance",
    444   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    445   "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    446   "email": "",
    447   "version": "0.0.0-testrig",
    448   "languages": [],
    449   "registrations": true,
    450   "approval_required": true,
    451   "invites_enabled": false,
    452   "configuration": {
    453     "statuses": {
    454       "max_characters": 5000,
    455       "max_media_attachments": 6,
    456       "characters_reserved_per_url": 25,
    457       "supported_mime_types": [
    458         "text/plain",
    459         "text/markdown"
    460       ]
    461     },
    462     "media_attachments": {
    463       "supported_mime_types": [
    464         "image/jpeg",
    465         "image/gif",
    466         "image/png",
    467         "image/webp",
    468         "video/mp4"
    469       ],
    470       "image_size_limit": 10485760,
    471       "image_matrix_limit": 16777216,
    472       "video_size_limit": 41943040,
    473       "video_frame_rate_limit": 60,
    474       "video_matrix_limit": 16777216
    475     },
    476     "polls": {
    477       "max_options": 6,
    478       "max_characters_per_option": 50,
    479       "min_expiration": 300,
    480       "max_expiration": 2629746
    481     },
    482     "accounts": {
    483       "allow_custom_css": true,
    484       "max_featured_tags": 10,
    485       "max_profile_fields": 6
    486     },
    487     "emojis": {
    488       "emoji_size_limit": 51200
    489     }
    490   },
    491   "urls": {
    492     "streaming_api": "wss://localhost:8080"
    493   },
    494   "stats": {
    495     "domain_count": 2,
    496     "status_count": 16,
    497     "user_count": 4
    498   },
    499   "thumbnail": "http://localhost:8080/assets/logo.png",
    500   "contact_account": {
    501     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    502     "username": "admin",
    503     "acct": "admin",
    504     "display_name": "",
    505     "locked": false,
    506     "discoverable": true,
    507     "bot": false,
    508     "created_at": "2022-05-17T13:10:59.000Z",
    509     "note": "",
    510     "url": "http://localhost:8080/@admin",
    511     "avatar": "",
    512     "avatar_static": "",
    513     "header": "http://localhost:8080/assets/default_header.png",
    514     "header_static": "http://localhost:8080/assets/default_header.png",
    515     "followers_count": 1,
    516     "following_count": 1,
    517     "statuses_count": 4,
    518     "last_status_at": "2021-10-20T10:41:37.000Z",
    519     "emojis": [],
    520     "fields": [],
    521     "enable_rss": true,
    522     "role": {
    523       "name": "admin"
    524     }
    525   },
    526   "max_toot_chars": 5000
    527 }`, dst.String())
    528 }
    529 
    530 func (suite *InstancePatchTestSuite) TestInstancePatch7() {
    531 	code, b := suite.instancePatch("", "", map[string]string{
    532 		"contact_email": "not.an.email.address",
    533 	})
    534 
    535 	if expectedCode := http.StatusBadRequest; code != expectedCode {
    536 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    537 	}
    538 
    539 	dst := new(bytes.Buffer)
    540 	if err := json.Indent(dst, b, "", "  "); err != nil {
    541 		suite.FailNow(err.Error())
    542 	}
    543 
    544 	suite.Equal(`{"error":"Bad Request: mail: missing '@' or angle-addr"}`, string(b))
    545 }
    546 
    547 func (suite *InstancePatchTestSuite) TestInstancePatch8() {
    548 	code, b := suite.instancePatch("thumbnail", "../../../../testrig/media/peglin.gif", map[string]string{
    549 		"thumbnail_description": "A bouncing little green peglin.",
    550 	})
    551 
    552 	if expectedCode := http.StatusOK; code != expectedCode {
    553 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    554 	}
    555 
    556 	dst := new(bytes.Buffer)
    557 	if err := json.Indent(dst, b, "", "  "); err != nil {
    558 		suite.FailNow(err.Error())
    559 	}
    560 
    561 	instanceAccount, err := suite.db.GetInstanceAccount(context.Background(), "")
    562 	if err != nil {
    563 		suite.FailNow(err.Error())
    564 	}
    565 
    566 	suite.Equal(`{
    567   "uri": "http://localhost:8080",
    568   "account_domain": "localhost:8080",
    569   "title": "GoToSocial Testrig Instance",
    570   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    571   "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    572   "email": "admin@example.org",
    573   "version": "0.0.0-testrig",
    574   "languages": [],
    575   "registrations": true,
    576   "approval_required": true,
    577   "invites_enabled": false,
    578   "configuration": {
    579     "statuses": {
    580       "max_characters": 5000,
    581       "max_media_attachments": 6,
    582       "characters_reserved_per_url": 25,
    583       "supported_mime_types": [
    584         "text/plain",
    585         "text/markdown"
    586       ]
    587     },
    588     "media_attachments": {
    589       "supported_mime_types": [
    590         "image/jpeg",
    591         "image/gif",
    592         "image/png",
    593         "image/webp",
    594         "video/mp4"
    595       ],
    596       "image_size_limit": 10485760,
    597       "image_matrix_limit": 16777216,
    598       "video_size_limit": 41943040,
    599       "video_frame_rate_limit": 60,
    600       "video_matrix_limit": 16777216
    601     },
    602     "polls": {
    603       "max_options": 6,
    604       "max_characters_per_option": 50,
    605       "min_expiration": 300,
    606       "max_expiration": 2629746
    607     },
    608     "accounts": {
    609       "allow_custom_css": true,
    610       "max_featured_tags": 10,
    611       "max_profile_fields": 6
    612     },
    613     "emojis": {
    614       "emoji_size_limit": 51200
    615     }
    616   },
    617   "urls": {
    618     "streaming_api": "wss://localhost:8080"
    619   },
    620   "stats": {
    621     "domain_count": 2,
    622     "status_count": 16,
    623     "user_count": 4
    624   },
    625   "thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+`
    626   "thumbnail_type": "image/gif",
    627   "thumbnail_description": "A bouncing little green peglin.",
    628   "contact_account": {
    629     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    630     "username": "admin",
    631     "acct": "admin",
    632     "display_name": "",
    633     "locked": false,
    634     "discoverable": true,
    635     "bot": false,
    636     "created_at": "2022-05-17T13:10:59.000Z",
    637     "note": "",
    638     "url": "http://localhost:8080/@admin",
    639     "avatar": "",
    640     "avatar_static": "",
    641     "header": "http://localhost:8080/assets/default_header.png",
    642     "header_static": "http://localhost:8080/assets/default_header.png",
    643     "followers_count": 1,
    644     "following_count": 1,
    645     "statuses_count": 4,
    646     "last_status_at": "2021-10-20T10:41:37.000Z",
    647     "emojis": [],
    648     "fields": [],
    649     "enable_rss": true,
    650     "role": {
    651       "name": "admin"
    652     }
    653   },
    654   "max_toot_chars": 5000
    655 }`, dst.String())
    656 
    657 	// extra bonus: check the v2 model thumbnail after the patch
    658 	instanceV2, err := suite.processor.InstanceGetV2(context.Background())
    659 	if err != nil {
    660 		suite.FailNow(err.Error())
    661 	}
    662 
    663 	instanceV2ThumbnailJson, err := json.MarshalIndent(instanceV2.Thumbnail, "", "  ")
    664 	if err != nil {
    665 		suite.FailNow(err.Error())
    666 	}
    667 
    668 	suite.Equal(`{
    669   "url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+`
    670   "thumbnail_type": "image/gif",
    671   "thumbnail_description": "A bouncing little green peglin.",
    672   "blurhash": "LG9t;qRS4YtO.4WDRlt5IXoxtPj["
    673 }`, string(instanceV2ThumbnailJson))
    674 
    675 	// double extra special bonus: now update the image description without changing the image
    676 	code2, b2 := suite.instancePatch("", "", map[string]string{
    677 		"thumbnail_description": "updating the thumbnail description without changing anything else!",
    678 	})
    679 
    680 	if expectedCode := http.StatusOK; code2 != expectedCode {
    681 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code2)
    682 	}
    683 
    684 	// just extract the value we wanna check, no need to print the whole thing again
    685 	i := make(map[string]interface{})
    686 	if err := json.Unmarshal(b2, &i); err != nil {
    687 		suite.FailNow(err.Error())
    688 	}
    689 
    690 	suite.EqualValues("updating the thumbnail description without changing anything else!", i["thumbnail_description"])
    691 }
    692 
    693 func (suite *InstancePatchTestSuite) TestInstancePatch9() {
    694 	code, b := suite.instancePatch("", "", map[string]string{
    695 		"thumbnail_description": "setting a new description without having a custom image set; this should change nothing!",
    696 	})
    697 
    698 	if expectedCode := http.StatusOK; code != expectedCode {
    699 		suite.FailNowf("wrong status code", "expected %d but got %d", expectedCode, code)
    700 	}
    701 
    702 	dst := new(bytes.Buffer)
    703 	if err := json.Indent(dst, b, "", "  "); err != nil {
    704 		suite.FailNow(err.Error())
    705 	}
    706 
    707 	suite.Equal(`{
    708   "uri": "http://localhost:8080",
    709   "account_domain": "localhost:8080",
    710   "title": "GoToSocial Testrig Instance",
    711   "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    712   "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
    713   "email": "admin@example.org",
    714   "version": "0.0.0-testrig",
    715   "languages": [],
    716   "registrations": true,
    717   "approval_required": true,
    718   "invites_enabled": false,
    719   "configuration": {
    720     "statuses": {
    721       "max_characters": 5000,
    722       "max_media_attachments": 6,
    723       "characters_reserved_per_url": 25,
    724       "supported_mime_types": [
    725         "text/plain",
    726         "text/markdown"
    727       ]
    728     },
    729     "media_attachments": {
    730       "supported_mime_types": [
    731         "image/jpeg",
    732         "image/gif",
    733         "image/png",
    734         "image/webp",
    735         "video/mp4"
    736       ],
    737       "image_size_limit": 10485760,
    738       "image_matrix_limit": 16777216,
    739       "video_size_limit": 41943040,
    740       "video_frame_rate_limit": 60,
    741       "video_matrix_limit": 16777216
    742     },
    743     "polls": {
    744       "max_options": 6,
    745       "max_characters_per_option": 50,
    746       "min_expiration": 300,
    747       "max_expiration": 2629746
    748     },
    749     "accounts": {
    750       "allow_custom_css": true,
    751       "max_featured_tags": 10,
    752       "max_profile_fields": 6
    753     },
    754     "emojis": {
    755       "emoji_size_limit": 51200
    756     }
    757   },
    758   "urls": {
    759     "streaming_api": "wss://localhost:8080"
    760   },
    761   "stats": {
    762     "domain_count": 2,
    763     "status_count": 16,
    764     "user_count": 4
    765   },
    766   "thumbnail": "http://localhost:8080/assets/logo.png",
    767   "contact_account": {
    768     "id": "01F8MH17FWEB39HZJ76B6VXSKF",
    769     "username": "admin",
    770     "acct": "admin",
    771     "display_name": "",
    772     "locked": false,
    773     "discoverable": true,
    774     "bot": false,
    775     "created_at": "2022-05-17T13:10:59.000Z",
    776     "note": "",
    777     "url": "http://localhost:8080/@admin",
    778     "avatar": "",
    779     "avatar_static": "",
    780     "header": "http://localhost:8080/assets/default_header.png",
    781     "header_static": "http://localhost:8080/assets/default_header.png",
    782     "followers_count": 1,
    783     "following_count": 1,
    784     "statuses_count": 4,
    785     "last_status_at": "2021-10-20T10:41:37.000Z",
    786     "emojis": [],
    787     "fields": [],
    788     "enable_rss": true,
    789     "role": {
    790       "name": "admin"
    791     }
    792   },
    793   "max_toot_chars": 5000
    794 }`, dst.String())
    795 }
    796 
    797 func TestInstancePatchTestSuite(t *testing.T) {
    798 	suite.Run(t, &InstancePatchTestSuite{})
    799 }