2021-05-08 15:16:24 +02:00
/ *
GoToSocial
2023-03-12 16:00:57 +01:00
Copyright ( C ) GoToSocial Authors admin @ gotosocial . org
2021-05-08 15:16:24 +02:00
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/>.
* /
2023-01-02 13:10:50 +01:00
package statuses_test
2021-05-08 15:16:24 +02:00
import (
2022-04-29 15:05:13 +02:00
"context"
2021-05-08 15:16:24 +02:00
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
2023-01-02 13:10:50 +01:00
"github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
2022-04-29 15:05:13 +02:00
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
2021-05-08 15:16:24 +02:00
"github.com/superseriousbusiness/gotosocial/internal/oauth"
2022-07-12 09:32:20 +02:00
"github.com/superseriousbusiness/gotosocial/testrig"
2021-05-08 15:16:24 +02:00
)
type StatusBoostTestSuite struct {
StatusStandardTestSuite
}
func ( suite * StatusBoostTestSuite ) TestPostBoost ( ) {
t := suite . testTokens [ "local_account_1" ]
2021-08-25 15:34:33 +02:00
oauthToken := oauth . DBTokenToToken ( t )
2021-05-08 15:16:24 +02:00
targetStatus := suite . testStatuses [ "admin_account_status_1" ]
// setup
recorder := httptest . NewRecorder ( )
2022-07-12 09:32:20 +02:00
ctx , _ := testrig . CreateGinTestContext ( recorder , nil )
2021-05-08 15:16:24 +02:00
ctx . Set ( oauth . SessionAuthorizedApplication , suite . testApplications [ "application_1" ] )
ctx . Set ( oauth . SessionAuthorizedToken , oauthToken )
ctx . Set ( oauth . SessionAuthorizedUser , suite . testUsers [ "local_account_1" ] )
ctx . Set ( oauth . SessionAuthorizedAccount , suite . testAccounts [ "local_account_1" ] )
2023-01-02 13:10:50 +01:00
ctx . Request = httptest . NewRequest ( http . MethodPost , fmt . Sprintf ( "http://localhost:8080%s" , strings . Replace ( statuses . ReblogPath , ":id" , targetStatus . ID , 1 ) ) , nil ) // the endpoint we're hitting
2021-12-11 17:50:00 +01:00
ctx . Request . Header . Set ( "accept" , "application/json" )
2021-05-08 15:16:24 +02:00
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx . Params = gin . Params {
gin . Param {
2023-01-02 13:10:50 +01:00
Key : statuses . IDKey ,
2021-05-08 15:16:24 +02:00
Value : targetStatus . ID ,
} ,
}
suite . statusModule . StatusBoostPOSTHandler ( ctx )
// check response
suite . EqualValues ( http . StatusOK , recorder . Code )
result := recorder . Result ( )
defer result . Body . Close ( )
b , err := ioutil . ReadAll ( result . Body )
2022-07-04 15:41:20 +02:00
suite . NoError ( err )
2021-05-08 15:16:24 +02:00
2023-01-02 13:10:50 +01:00
statusReply := & apimodel . Status { }
2021-05-08 15:16:24 +02:00
err = json . Unmarshal ( b , statusReply )
2022-07-04 15:41:20 +02:00
suite . NoError ( err )
suite . False ( statusReply . Sensitive )
2023-01-02 13:10:50 +01:00
suite . Equal ( apimodel . VisibilityPublic , statusReply . Visibility )
2022-07-04 15:41:20 +02:00
suite . Equal ( targetStatus . ContentWarning , statusReply . SpoilerText )
suite . Equal ( targetStatus . Content , statusReply . Content )
suite . Equal ( "the_mighty_zork" , statusReply . Account . Username )
suite . Len ( statusReply . MediaAttachments , 0 )
suite . Len ( statusReply . Mentions , 0 )
suite . Len ( statusReply . Emojis , 0 )
suite . Len ( statusReply . Tags , 0 )
suite . NotNil ( statusReply . Application )
suite . Equal ( "really cool gts application" , statusReply . Application . Name )
suite . NotNil ( statusReply . Reblog )
suite . Equal ( 1 , statusReply . Reblog . ReblogsCount )
suite . Equal ( 1 , statusReply . Reblog . FavouritesCount )
suite . Equal ( targetStatus . Content , statusReply . Reblog . Content )
suite . Equal ( targetStatus . ContentWarning , statusReply . Reblog . SpoilerText )
suite . Equal ( targetStatus . AccountID , statusReply . Reblog . Account . ID )
suite . Len ( statusReply . Reblog . MediaAttachments , 1 )
suite . Len ( statusReply . Reblog . Tags , 1 )
suite . Len ( statusReply . Reblog . Emojis , 1 )
2024-04-17 12:41:40 +02:00
suite . True ( statusReply . Reblogged )
suite . True ( statusReply . Reblog . Reblogged )
2022-07-04 15:41:20 +02:00
suite . Equal ( "superseriousbusiness" , statusReply . Reblog . Application . Name )
}
func ( suite * StatusBoostTestSuite ) TestPostBoostOwnFollowersOnly ( ) {
t := suite . testTokens [ "local_account_1" ]
oauthToken := oauth . DBTokenToToken ( t )
testStatus := suite . testStatuses [ "local_account_1_status_5" ]
testAccount := suite . testAccounts [ "local_account_1" ]
testUser := suite . testUsers [ "local_account_1" ]
recorder := httptest . NewRecorder ( )
2022-07-12 09:32:20 +02:00
ctx , _ := testrig . CreateGinTestContext ( recorder , nil )
2022-07-04 15:41:20 +02:00
ctx . Set ( oauth . SessionAuthorizedApplication , suite . testApplications [ "application_1" ] )
ctx . Set ( oauth . SessionAuthorizedToken , oauthToken )
ctx . Set ( oauth . SessionAuthorizedUser , testUser )
ctx . Set ( oauth . SessionAuthorizedAccount , testAccount )
2023-01-02 13:10:50 +01:00
ctx . Request = httptest . NewRequest ( http . MethodPost , fmt . Sprintf ( "http://localhost:8080%s" , strings . Replace ( statuses . ReblogPath , ":id" , testStatus . ID , 1 ) ) , nil )
2022-07-04 15:41:20 +02:00
ctx . Request . Header . Set ( "accept" , "application/json" )
ctx . Params = gin . Params {
gin . Param {
2023-01-02 13:10:50 +01:00
Key : statuses . IDKey ,
2022-07-04 15:41:20 +02:00
Value : testStatus . ID ,
} ,
}
suite . statusModule . StatusBoostPOSTHandler ( ctx )
// check response
suite . EqualValues ( http . StatusOK , recorder . Code )
result := recorder . Result ( )
defer result . Body . Close ( )
b , err := ioutil . ReadAll ( result . Body )
suite . NoError ( err )
2023-01-02 13:10:50 +01:00
responseStatus := & apimodel . Status { }
2022-07-04 15:41:20 +02:00
err = json . Unmarshal ( b , responseStatus )
suite . NoError ( err )
suite . False ( responseStatus . Sensitive )
suite . Equal ( suite . tc . VisToAPIVis ( context . Background ( ) , testStatus . Visibility ) , responseStatus . Visibility )
suite . Equal ( testStatus . ContentWarning , responseStatus . SpoilerText )
suite . Equal ( testStatus . Content , responseStatus . Content )
suite . Equal ( "the_mighty_zork" , responseStatus . Account . Username )
suite . Len ( responseStatus . MediaAttachments , 0 )
suite . Len ( responseStatus . Mentions , 0 )
suite . Len ( responseStatus . Emojis , 0 )
suite . Len ( responseStatus . Tags , 0 )
suite . NotNil ( responseStatus . Application )
suite . Equal ( "really cool gts application" , responseStatus . Application . Name )
suite . NotNil ( responseStatus . Reblog )
suite . Equal ( 1 , responseStatus . Reblog . ReblogsCount )
suite . Equal ( 0 , responseStatus . Reblog . FavouritesCount )
suite . Equal ( testStatus . Content , responseStatus . Reblog . Content )
suite . Equal ( testStatus . ContentWarning , responseStatus . Reblog . SpoilerText )
suite . Equal ( testStatus . AccountID , responseStatus . Reblog . Account . ID )
suite . Equal ( suite . tc . VisToAPIVis ( context . Background ( ) , testStatus . Visibility ) , responseStatus . Reblog . Visibility )
suite . Empty ( responseStatus . Reblog . MediaAttachments )
suite . Empty ( responseStatus . Reblog . Tags )
suite . Empty ( responseStatus . Reblog . Emojis )
2024-04-17 12:41:40 +02:00
suite . True ( responseStatus . Reblogged )
suite . True ( responseStatus . Reblog . Reblogged )
2022-07-04 15:41:20 +02:00
suite . Equal ( "really cool gts application" , responseStatus . Reblog . Application . Name )
2021-05-08 15:16:24 +02:00
}
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 15:03:14 +02:00
// try to boost a status that's not boostable / visible to us
2024-07-11 16:44:29 +02:00
// TODO: sort this out with new interaction policies
// func (suite *StatusBoostTestSuite) TestPostUnboostable() {
// t := suite.testTokens["local_account_1"]
// oauthToken := oauth.DBTokenToToken(t)
// targetStatus := suite.testStatuses["local_account_2_status_4"]
// // setup
// recorder := httptest.NewRecorder()
// ctx, _ := testrig.CreateGinTestContext(recorder, nil)
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
// ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
// ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
// ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
// ctx.Request.Header.Set("accept", "application/json")
// // normally the router would populate these params from the path values,
// // but because we're calling the function directly, we need to set them manually.
// ctx.Params = gin.Params{
// gin.Param{
// Key: statuses.IDKey,
// Value: targetStatus.ID,
// },
// }
// suite.statusModule.StatusBoostPOSTHandler(ctx)
// // check response
// suite.Equal(http.StatusNotFound, recorder.Code) // we 404 unboostable statuses
// result := recorder.Result()
// defer result.Body.Close()
// b, err := ioutil.ReadAll(result.Body)
// suite.NoError(err)
// suite.Equal(`{"error":"Not Found"}`, string(b))
// }
2021-05-08 15:16:24 +02:00
// try to boost a status that's not visible to the user
func ( suite * StatusBoostTestSuite ) TestPostNotVisible ( ) {
2022-04-29 15:05:13 +02:00
// stop local_account_2 following zork
err := suite . db . DeleteByID ( context . Background ( ) , suite . testFollows [ "local_account_2_local_account_1" ] . ID , & gtsmodel . Follow { } )
suite . NoError ( err )
2021-05-08 15:16:24 +02:00
t := suite . testTokens [ "local_account_2" ]
2021-08-25 15:34:33 +02:00
oauthToken := oauth . DBTokenToToken ( t )
2021-05-08 15:16:24 +02:00
targetStatus := suite . testStatuses [ "local_account_1_status_3" ] // this is a mutual only status and these accounts aren't mutuals
// setup
recorder := httptest . NewRecorder ( )
2022-07-12 09:32:20 +02:00
ctx , _ := testrig . CreateGinTestContext ( recorder , nil )
2021-05-08 15:16:24 +02:00
ctx . Set ( oauth . SessionAuthorizedApplication , suite . testApplications [ "application_1" ] )
ctx . Set ( oauth . SessionAuthorizedToken , oauthToken )
ctx . Set ( oauth . SessionAuthorizedUser , suite . testUsers [ "local_account_2" ] )
ctx . Set ( oauth . SessionAuthorizedAccount , suite . testAccounts [ "local_account_2" ] )
2023-01-02 13:10:50 +01:00
ctx . Request = httptest . NewRequest ( http . MethodPost , fmt . Sprintf ( "http://localhost:8080%s" , strings . Replace ( statuses . ReblogPath , ":id" , targetStatus . ID , 1 ) ) , nil ) // the endpoint we're hitting
2021-12-11 17:50:00 +01:00
ctx . Request . Header . Set ( "accept" , "application/json" )
2021-05-08 15:16:24 +02:00
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx . Params = gin . Params {
gin . Param {
2023-01-02 13:10:50 +01:00
Key : statuses . IDKey ,
2021-05-08 15:16:24 +02:00
Value : targetStatus . ID ,
} ,
}
suite . statusModule . StatusBoostPOSTHandler ( ctx )
// check response
2022-06-08 20:38:03 +02:00
suite . Equal ( http . StatusNotFound , recorder . Code ) // we 404 statuses that aren't visible
2021-05-08 15:16:24 +02:00
}
func TestStatusBoostTestSuite ( t * testing . T ) {
suite . Run ( t , new ( StatusBoostTestSuite ) )
}