2023-03-12 16:00:57 +01:00
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2021-05-08 14:25:55 +02:00
2023-01-02 13:10:50 +01:00
package accounts_test
2021-05-08 14:25:55 +02:00
import (
2021-09-11 13:19:06 +02:00
"context"
2021-09-10 14:36:10 +02:00
"encoding/json"
2023-05-12 11:17:31 +02:00
"fmt"
"io"
2021-05-08 14:25:55 +02:00
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/suite"
2023-01-02 13:10:50 +01:00
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
2021-09-10 14:36:10 +02:00
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
2023-05-12 11:17:31 +02:00
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
2021-05-08 14:25:55 +02:00
"github.com/superseriousbusiness/gotosocial/testrig"
)
type AccountUpdateTestSuite struct {
AccountStandardTestSuite
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) updateAccountFromFormData ( data map [ string ] string , expectedHTTPStatus int , expectedBody string ) ( * apimodel . Account , error ) {
requestBody , w , err := testrig . CreateMultipartFormData ( "" , "" , data )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
return suite . updateAccount ( requestBody . Bytes ( ) , w . FormDataContentType ( ) , expectedHTTPStatus , expectedBody )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) updateAccountFromFormDataWithFile ( fieldName string , fileName string , data map [ string ] string , expectedHTTPStatus int , expectedBody string ) ( * apimodel . Account , error ) {
requestBody , w , err := testrig . CreateMultipartFormData ( fieldName , fileName , data )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
return suite . updateAccount ( requestBody . Bytes ( ) , w . FormDataContentType ( ) , expectedHTTPStatus , expectedBody )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) updateAccountFromJSON ( data string , expectedHTTPStatus int , expectedBody string ) ( * apimodel . Account , error ) {
return suite . updateAccount ( [ ] byte ( data ) , "application/json" , expectedHTTPStatus , expectedBody )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) updateAccount (
bodyBytes [ ] byte ,
contentType string ,
expectedHTTPStatus int ,
expectedBody string ,
) ( * apimodel . Account , error ) {
// Initialize http test context.
recorder := httptest . NewRecorder ( )
ctx := suite . newContext ( recorder , http . MethodPatch , bodyBytes , accounts . UpdateCredentialsPath , contentType )
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
// Trigger the handler.
suite . accountsModule . AccountUpdateCredentialsPATCHHandler ( ctx )
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
// Read the result.
result := recorder . Result ( )
defer result . Body . Close ( )
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
b , err := io . ReadAll ( result . Body )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
return nil , err
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
errs := gtserror . MultiError { }
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
// Check expected code + body.
if resultCode := recorder . Code ; expectedHTTPStatus != resultCode {
errs = append ( errs , fmt . Sprintf ( "expected %d got %d" , expectedHTTPStatus , resultCode ) )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
// If we got an expected body, return early.
if expectedBody != "" && string ( b ) != expectedBody {
errs = append ( errs , fmt . Sprintf ( "expected %s got %s" , expectedBody , string ( b ) ) )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
if err := errs . Combine ( ) ; err != nil {
return nil , fmt . Errorf ( "%v (body %s)" , err , string ( b ) )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
// Return account response.
resp := & apimodel . Account { }
if err := json . Unmarshal ( b , resp ) ; err != nil {
return nil , err
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
return resp , nil
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountBasicFormData ( ) {
data := map [ string ] string {
"note" : "this is my new bio read it and weep" ,
"fields_attributes[0][name]" : "pronouns" ,
"fields_attributes[0][value]" : "they/them" ,
"fields_attributes[1][name]" : "Website" ,
"fields_attributes[1][value]" : "https://example.com" ,
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
suite . Equal ( "<p>this is my new bio read it and weep</p>" , apimodelAccount . Note )
suite . Equal ( "this is my new bio read it and weep" , apimodelAccount . Source . Note )
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
if l := len ( apimodelAccount . Fields ) ; l != 2 {
suite . FailNow ( "" , "expected %d fields, got %d" , 2 , l )
}
suite . Equal ( ` pronouns ` , apimodelAccount . Fields [ 0 ] . Name )
suite . Equal ( ` they/them ` , apimodelAccount . Fields [ 0 ] . Value )
suite . Equal ( ` Website ` , apimodelAccount . Fields [ 1 ] . Name )
suite . Equal ( ` <a href="https://example.com" rel="nofollow noreferrer noopener" target="_blank">https://example.com</a> ` , apimodelAccount . Fields [ 1 ] . Value )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountBasicJSON ( ) {
data := `
{
"note" : "this is my new bio read it and weep" ,
"fields_attributes" : {
"0" : {
"name" : "pronouns" ,
"value" : "they/them"
} ,
"1" : {
"name" : "Website" ,
"value" : "https://example.com"
}
}
}
`
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromJSON ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2021-09-11 13:19:06 +02:00
suite . Equal ( "<p>this is my new bio read it and weep</p>" , apimodelAccount . Note )
2023-05-12 11:17:31 +02:00
suite . Equal ( "this is my new bio read it and weep" , apimodelAccount . Source . Note )
if l := len ( apimodelAccount . Fields ) ; l != 2 {
suite . FailNow ( "" , "expected %d fields, got %d" , 2 , l )
}
suite . Equal ( ` pronouns ` , apimodelAccount . Fields [ 0 ] . Name )
suite . Equal ( ` they/them ` , apimodelAccount . Fields [ 0 ] . Value )
suite . Equal ( ` Website ` , apimodelAccount . Fields [ 1 ] . Name )
suite . Equal ( ` <a href="https://example.com" rel="nofollow noreferrer noopener" target="_blank">https://example.com</a> ` , apimodelAccount . Fields [ 1 ] . Value )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountLockFormData ( ) {
data := map [ string ] string {
"locked" : "true" ,
}
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
suite . True ( apimodelAccount . Locked )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountLockJSON ( ) {
data := `
{
"locked" : true
} `
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromJSON ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
suite . True ( apimodelAccount . Locked )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountUnlockFormData ( ) {
data := map [ string ] string {
"locked" : "false" ,
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2022-09-26 11:56:01 +02:00
2023-05-12 11:17:31 +02:00
suite . False ( apimodelAccount . Locked )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountUnlockJSON ( ) {
data := `
{
"locked" : false
} `
apimodelAccount , err := suite . updateAccountFromJSON ( data , http . StatusOK , "" )
2023-02-16 14:20:23 +01:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2023-02-16 14:20:23 +01:00
}
2023-05-12 11:17:31 +02:00
suite . False ( apimodelAccount . Locked )
}
2023-02-16 14:20:23 +01:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountCache ( ) {
// Get the account first to make sure it's in the database
// cache. When the account is updated via the PATCH handler,
// it should invalidate the cache and return the new version.
if _ , err := suite . db . GetAccountByID ( context . Background ( ) , suite . testAccounts [ "local_account_1" ] . ID ) ; err != nil {
suite . FailNow ( err . Error ( ) )
}
2023-02-16 14:20:23 +01:00
2023-05-12 11:17:31 +02:00
data := map [ string ] string {
"note" : "this is my new bio read it and weep" ,
}
2023-02-16 14:20:23 +01:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2023-02-16 14:20:23 +01:00
2023-05-12 11:17:31 +02:00
suite . Equal ( "<p>this is my new bio read it and weep</p>" , apimodelAccount . Note )
}
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountDiscoverableFormData ( ) {
data := map [ string ] string {
"discoverable" : "false" ,
}
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2023-02-16 14:20:23 +01:00
suite . False ( apimodelAccount . Discoverable )
2023-05-12 11:17:31 +02:00
// Check the account in the database too.
2023-02-16 14:20:23 +01:00
dbZork , err := suite . db . GetAccountByID ( context . Background ( ) , apimodelAccount . ID )
suite . NoError ( err )
suite . False ( * dbZork . Discoverable )
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountDiscoverableJSON ( ) {
data := `
{
"discoverable" : false
} `
apimodelAccount , err := suite . updateAccountFromJSON ( data , http . StatusOK , "" )
2021-05-08 14:25:55 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-05-08 14:25:55 +02:00
}
2023-05-12 11:17:31 +02:00
suite . False ( apimodelAccount . Discoverable )
2021-05-08 14:25:55 +02:00
2023-05-12 11:17:31 +02:00
// Check the account in the database too.
dbZork , err := suite . db . GetAccountByID ( context . Background ( ) , apimodelAccount . ID )
2022-06-08 20:38:03 +02:00
suite . NoError ( err )
2023-05-12 11:17:31 +02:00
suite . False ( * dbZork . Discoverable )
}
2021-05-08 14:25:55 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountWithImageFormData ( ) {
data := map [ string ] string {
"display_name" : "updated zork display name!!!" ,
"note" : "" ,
"locked" : "true" ,
}
apimodelAccount , err := suite . updateAccountFromFormDataWithFile ( "header" , "../../../../testrig/media/test-jpeg.jpg" , data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2021-09-10 14:36:10 +02:00
2023-05-12 11:17:31 +02:00
suite . Equal ( data [ "display_name" ] , apimodelAccount . DisplayName )
2021-09-10 14:36:10 +02:00
suite . True ( apimodelAccount . Locked )
suite . Empty ( apimodelAccount . Note )
2022-05-07 17:55:27 +02:00
suite . Empty ( apimodelAccount . Source . Note )
2021-09-10 14:36:10 +02:00
suite . NotEmpty ( apimodelAccount . Header )
suite . NotEmpty ( apimodelAccount . HeaderStatic )
2023-05-12 11:17:31 +02:00
// Can't predict IDs generated for new media
// so just ensure it's different than before.
2023-01-11 12:13:13 +01:00
suite . NotEqual ( "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg" , apimodelAccount . Header )
suite . NotEqual ( "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg" , apimodelAccount . HeaderStatic )
2021-05-08 14:25:55 +02:00
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountEmptyFormData ( ) {
data := make ( map [ string ] string )
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
_ , err := suite . updateAccountFromFormData ( data , http . StatusBadRequest , ` { "error":"Bad Request: empty form submitted"} ` )
2021-09-11 13:19:06 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2021-09-11 13:19:06 +02:00
}
2023-05-12 11:17:31 +02:00
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountSourceFormData ( ) {
data := map [ string ] string {
"source[privacy]" : string ( apimodel . VisibilityPrivate ) ,
"source[language]" : "de" ,
"source[sensitive]" : "true" ,
"locked" : "true" ,
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2021-09-11 13:19:06 +02:00
2023-05-12 11:17:31 +02:00
suite . Equal ( data [ "source[language]" ] , apimodelAccount . Source . Language )
2021-09-11 13:19:06 +02:00
suite . EqualValues ( apimodel . VisibilityPrivate , apimodelAccount . Source . Privacy )
suite . True ( apimodelAccount . Source . Sensitive )
suite . True ( apimodelAccount . Locked )
}
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountSourceJSON ( ) {
data := `
{
"source" : {
"privacy" : "private" ,
"language" : "de" ,
"sensitive" : true
} ,
"locked" : true
}
`
apimodelAccount , err := suite . updateAccountFromJSON ( data , http . StatusOK , "" )
2022-08-06 12:09:21 +02:00
if err != nil {
2023-05-12 11:17:31 +02:00
suite . FailNow ( err . Error ( ) )
2022-08-06 12:09:21 +02:00
}
2023-05-12 11:17:31 +02:00
suite . Equal ( "de" , apimodelAccount . Source . Language )
suite . EqualValues ( apimodel . VisibilityPrivate , apimodelAccount . Source . Privacy )
suite . True ( apimodelAccount . Source . Sensitive )
suite . True ( apimodelAccount . Locked )
}
2022-08-06 12:09:21 +02:00
2023-05-12 11:17:31 +02:00
func ( suite * AccountUpdateTestSuite ) TestUpdateAccountSourceBadContentTypeFormData ( ) {
data := map [ string ] string {
"source[status_content_type]" : "text/markdown" ,
}
2022-08-06 12:09:21 +02:00
2023-05-12 11:17:31 +02:00
apimodelAccount , err := suite . updateAccountFromFormData ( data , http . StatusOK , "" )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2022-08-06 12:09:21 +02:00
2023-05-12 11:17:31 +02:00
suite . Equal ( data [ "source[status_content_type]" ] , apimodelAccount . Source . StatusContentType )
2022-08-06 12:09:21 +02:00
2023-05-12 11:17:31 +02:00
// Check the account in the database too.
2022-08-06 12:09:21 +02:00
dbAccount , err := suite . db . GetAccountByID ( context . Background ( ) , suite . testAccounts [ "local_account_1" ] . ID )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2023-05-12 11:17:31 +02:00
suite . Equal ( data [ "source[status_content_type]" ] , dbAccount . StatusContentType )
2022-08-06 12:09:21 +02:00
}
2023-03-02 12:06:40 +01:00
func ( suite * AccountUpdateTestSuite ) TestAccountUpdateCredentialsPATCHHandlerUpdateStatusContentTypeBad ( ) {
2023-05-12 11:17:31 +02:00
data := map [ string ] string {
"source[status_content_type]" : "peepeepoopoo" ,
2022-08-06 12:09:21 +02:00
}
2023-05-12 11:17:31 +02:00
_ , err := suite . updateAccountFromFormData ( data , http . StatusBadRequest , ` { "error":"Bad Request: status content type 'peepeepoopoo' was not recognized, valid options are 'text/plain', 'text/markdown'"} ` )
if err != nil {
suite . FailNow ( err . Error ( ) )
}
2022-08-06 12:09:21 +02:00
}
2021-05-08 14:25:55 +02:00
func TestAccountUpdateTestSuite ( t * testing . T ) {
suite . Run ( t , new ( AccountUpdateTestSuite ) )
}