statusboost_test.go (9593B)
1 /* 2 GoToSocial 3 Copyright (C) GoToSocial Authors admin@gotosocial.org 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU Affero General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU Affero General Public License for more details. 12 You should have received a copy of the GNU Affero General Public License 13 along with this program. If not, see <http://www.gnu.org/licenses/>. 14 */ 15 16 package statuses_test 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "net/http" 24 "net/http/httptest" 25 "strings" 26 "testing" 27 28 "github.com/gin-gonic/gin" 29 "github.com/stretchr/testify/suite" 30 "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses" 31 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 32 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 33 "github.com/superseriousbusiness/gotosocial/internal/oauth" 34 "github.com/superseriousbusiness/gotosocial/testrig" 35 ) 36 37 type StatusBoostTestSuite struct { 38 StatusStandardTestSuite 39 } 40 41 func (suite *StatusBoostTestSuite) TestPostBoost() { 42 t := suite.testTokens["local_account_1"] 43 oauthToken := oauth.DBTokenToToken(t) 44 45 targetStatus := suite.testStatuses["admin_account_status_1"] 46 47 // setup 48 recorder := httptest.NewRecorder() 49 ctx, _ := testrig.CreateGinTestContext(recorder, nil) 50 ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) 51 ctx.Set(oauth.SessionAuthorizedToken, oauthToken) 52 ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) 53 ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) 54 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 55 ctx.Request.Header.Set("accept", "application/json") 56 57 // normally the router would populate these params from the path values, 58 // but because we're calling the function directly, we need to set them manually. 59 ctx.Params = gin.Params{ 60 gin.Param{ 61 Key: statuses.IDKey, 62 Value: targetStatus.ID, 63 }, 64 } 65 66 suite.statusModule.StatusBoostPOSTHandler(ctx) 67 68 // check response 69 suite.EqualValues(http.StatusOK, recorder.Code) 70 71 result := recorder.Result() 72 defer result.Body.Close() 73 b, err := ioutil.ReadAll(result.Body) 74 suite.NoError(err) 75 76 statusReply := &apimodel.Status{} 77 err = json.Unmarshal(b, statusReply) 78 suite.NoError(err) 79 80 suite.False(statusReply.Sensitive) 81 suite.Equal(apimodel.VisibilityPublic, statusReply.Visibility) 82 83 suite.Equal(targetStatus.ContentWarning, statusReply.SpoilerText) 84 suite.Equal(targetStatus.Content, statusReply.Content) 85 suite.Equal("the_mighty_zork", statusReply.Account.Username) 86 suite.Len(statusReply.MediaAttachments, 0) 87 suite.Len(statusReply.Mentions, 0) 88 suite.Len(statusReply.Emojis, 0) 89 suite.Len(statusReply.Tags, 0) 90 91 suite.NotNil(statusReply.Application) 92 suite.Equal("really cool gts application", statusReply.Application.Name) 93 94 suite.NotNil(statusReply.Reblog) 95 suite.Equal(1, statusReply.Reblog.ReblogsCount) 96 suite.Equal(1, statusReply.Reblog.FavouritesCount) 97 suite.Equal(targetStatus.Content, statusReply.Reblog.Content) 98 suite.Equal(targetStatus.ContentWarning, statusReply.Reblog.SpoilerText) 99 suite.Equal(targetStatus.AccountID, statusReply.Reblog.Account.ID) 100 suite.Len(statusReply.Reblog.MediaAttachments, 1) 101 suite.Len(statusReply.Reblog.Tags, 1) 102 suite.Len(statusReply.Reblog.Emojis, 1) 103 suite.Equal("superseriousbusiness", statusReply.Reblog.Application.Name) 104 } 105 106 func (suite *StatusBoostTestSuite) TestPostBoostOwnFollowersOnly() { 107 t := suite.testTokens["local_account_1"] 108 oauthToken := oauth.DBTokenToToken(t) 109 110 testStatus := suite.testStatuses["local_account_1_status_5"] 111 testAccount := suite.testAccounts["local_account_1"] 112 testUser := suite.testUsers["local_account_1"] 113 114 recorder := httptest.NewRecorder() 115 ctx, _ := testrig.CreateGinTestContext(recorder, nil) 116 ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) 117 ctx.Set(oauth.SessionAuthorizedToken, oauthToken) 118 ctx.Set(oauth.SessionAuthorizedUser, testUser) 119 ctx.Set(oauth.SessionAuthorizedAccount, testAccount) 120 ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", testStatus.ID, 1)), nil) 121 ctx.Request.Header.Set("accept", "application/json") 122 123 ctx.Params = gin.Params{ 124 gin.Param{ 125 Key: statuses.IDKey, 126 Value: testStatus.ID, 127 }, 128 } 129 130 suite.statusModule.StatusBoostPOSTHandler(ctx) 131 132 // check response 133 suite.EqualValues(http.StatusOK, recorder.Code) 134 135 result := recorder.Result() 136 defer result.Body.Close() 137 b, err := ioutil.ReadAll(result.Body) 138 suite.NoError(err) 139 140 responseStatus := &apimodel.Status{} 141 err = json.Unmarshal(b, responseStatus) 142 suite.NoError(err) 143 144 suite.False(responseStatus.Sensitive) 145 suite.Equal(suite.tc.VisToAPIVis(context.Background(), testStatus.Visibility), responseStatus.Visibility) 146 147 suite.Equal(testStatus.ContentWarning, responseStatus.SpoilerText) 148 suite.Equal(testStatus.Content, responseStatus.Content) 149 suite.Equal("the_mighty_zork", responseStatus.Account.Username) 150 suite.Len(responseStatus.MediaAttachments, 0) 151 suite.Len(responseStatus.Mentions, 0) 152 suite.Len(responseStatus.Emojis, 0) 153 suite.Len(responseStatus.Tags, 0) 154 155 suite.NotNil(responseStatus.Application) 156 suite.Equal("really cool gts application", responseStatus.Application.Name) 157 158 suite.NotNil(responseStatus.Reblog) 159 suite.Equal(1, responseStatus.Reblog.ReblogsCount) 160 suite.Equal(0, responseStatus.Reblog.FavouritesCount) 161 suite.Equal(testStatus.Content, responseStatus.Reblog.Content) 162 suite.Equal(testStatus.ContentWarning, responseStatus.Reblog.SpoilerText) 163 suite.Equal(testStatus.AccountID, responseStatus.Reblog.Account.ID) 164 suite.Equal(suite.tc.VisToAPIVis(context.Background(), testStatus.Visibility), responseStatus.Reblog.Visibility) 165 suite.Empty(responseStatus.Reblog.MediaAttachments) 166 suite.Empty(responseStatus.Reblog.Tags) 167 suite.Empty(responseStatus.Reblog.Emojis) 168 suite.Equal("really cool gts application", responseStatus.Reblog.Application.Name) 169 } 170 171 // try to boost a status that's not boostable / visible to us 172 func (suite *StatusBoostTestSuite) TestPostUnboostable() { 173 t := suite.testTokens["local_account_1"] 174 oauthToken := oauth.DBTokenToToken(t) 175 176 targetStatus := suite.testStatuses["local_account_2_status_4"] 177 178 // setup 179 recorder := httptest.NewRecorder() 180 ctx, _ := testrig.CreateGinTestContext(recorder, nil) 181 ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) 182 ctx.Set(oauth.SessionAuthorizedToken, oauthToken) 183 ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) 184 ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) 185 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 186 ctx.Request.Header.Set("accept", "application/json") 187 188 // normally the router would populate these params from the path values, 189 // but because we're calling the function directly, we need to set them manually. 190 ctx.Params = gin.Params{ 191 gin.Param{ 192 Key: statuses.IDKey, 193 Value: targetStatus.ID, 194 }, 195 } 196 197 suite.statusModule.StatusBoostPOSTHandler(ctx) 198 199 // check response 200 suite.Equal(http.StatusNotFound, recorder.Code) // we 404 unboostable statuses 201 202 result := recorder.Result() 203 defer result.Body.Close() 204 b, err := ioutil.ReadAll(result.Body) 205 suite.NoError(err) 206 suite.Equal(`{"error":"Not Found"}`, string(b)) 207 } 208 209 // try to boost a status that's not visible to the user 210 func (suite *StatusBoostTestSuite) TestPostNotVisible() { 211 // stop local_account_2 following zork 212 err := suite.db.DeleteByID(context.Background(), suite.testFollows["local_account_2_local_account_1"].ID, >smodel.Follow{}) 213 suite.NoError(err) 214 215 t := suite.testTokens["local_account_2"] 216 oauthToken := oauth.DBTokenToToken(t) 217 218 targetStatus := suite.testStatuses["local_account_1_status_3"] // this is a mutual only status and these accounts aren't mutuals 219 220 // setup 221 recorder := httptest.NewRecorder() 222 ctx, _ := testrig.CreateGinTestContext(recorder, nil) 223 ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) 224 ctx.Set(oauth.SessionAuthorizedToken, oauthToken) 225 ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"]) 226 ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"]) 227 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 228 ctx.Request.Header.Set("accept", "application/json") 229 230 // normally the router would populate these params from the path values, 231 // but because we're calling the function directly, we need to set them manually. 232 ctx.Params = gin.Params{ 233 gin.Param{ 234 Key: statuses.IDKey, 235 Value: targetStatus.ID, 236 }, 237 } 238 239 suite.statusModule.StatusBoostPOSTHandler(ctx) 240 241 // check response 242 suite.Equal(http.StatusNotFound, recorder.Code) // we 404 statuses that aren't visible 243 } 244 245 func TestStatusBoostTestSuite(t *testing.T) { 246 suite.Run(t, new(StatusBoostTestSuite)) 247 }