search_test.go (11200B)
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 accounts_test 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "io" 24 "net/http" 25 "net/http/httptest" 26 "net/url" 27 "strconv" 28 "strings" 29 "testing" 30 31 "github.com/stretchr/testify/suite" 32 "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts" 33 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 34 apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 35 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 36 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 37 "github.com/superseriousbusiness/gotosocial/internal/oauth" 38 "github.com/superseriousbusiness/gotosocial/testrig" 39 ) 40 41 type AccountSearchTestSuite struct { 42 AccountStandardTestSuite 43 } 44 45 func (suite *AccountSearchTestSuite) getSearch( 46 requestingAccount *gtsmodel.Account, 47 token *gtsmodel.Token, 48 user *gtsmodel.User, 49 limit *int, 50 offset *int, 51 query string, 52 resolve *bool, 53 following *bool, 54 expectedHTTPStatus int, 55 expectedBody string, 56 ) ([]*apimodel.Account, error) { 57 var ( 58 recorder = httptest.NewRecorder() 59 ctx, _ = testrig.CreateGinTestContext(recorder, nil) 60 requestURL = testrig.URLMustParse("/api" + accounts.BasePath + "/search") 61 queryParts []string 62 ) 63 64 // Put the request together. 65 if limit != nil { 66 queryParts = append(queryParts, apiutil.LimitKey+"="+strconv.Itoa(*limit)) 67 } 68 69 if offset != nil { 70 queryParts = append(queryParts, apiutil.SearchOffsetKey+"="+strconv.Itoa(*offset)) 71 } 72 73 queryParts = append(queryParts, apiutil.SearchQueryKey+"="+url.QueryEscape(query)) 74 75 if resolve != nil { 76 queryParts = append(queryParts, apiutil.SearchResolveKey+"="+strconv.FormatBool(*resolve)) 77 } 78 79 if following != nil { 80 queryParts = append(queryParts, apiutil.SearchFollowingKey+"="+strconv.FormatBool(*following)) 81 } 82 83 requestURL.RawQuery = strings.Join(queryParts, "&") 84 ctx.Request = httptest.NewRequest(http.MethodGet, requestURL.String(), nil) 85 ctx.Set(oauth.SessionAuthorizedAccount, requestingAccount) 86 ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(token)) 87 ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) 88 ctx.Set(oauth.SessionAuthorizedUser, user) 89 90 // Trigger the function being tested. 91 suite.accountsModule.AccountSearchGETHandler(ctx) 92 93 // Read the result. 94 result := recorder.Result() 95 defer result.Body.Close() 96 97 b, err := io.ReadAll(result.Body) 98 if err != nil { 99 suite.FailNow(err.Error()) 100 } 101 102 errs := gtserror.MultiError{} 103 104 // Check expected code + body. 105 if resultCode := recorder.Code; expectedHTTPStatus != resultCode { 106 errs = append(errs, fmt.Sprintf("expected %d got %d", expectedHTTPStatus, resultCode)) 107 } 108 109 // If we got an expected body, return early. 110 if expectedBody != "" && string(b) != expectedBody { 111 errs = append(errs, fmt.Sprintf("expected %s got %s", expectedBody, string(b))) 112 } 113 114 if err := errs.Combine(); err != nil { 115 suite.FailNow("", "%v (body %s)", err, string(b)) 116 } 117 118 accounts := []*apimodel.Account{} 119 if err := json.Unmarshal(b, &accounts); err != nil { 120 suite.FailNow(err.Error()) 121 } 122 123 return accounts, nil 124 } 125 126 func (suite *AccountSearchTestSuite) TestSearchZorkOK() { 127 var ( 128 requestingAccount = suite.testAccounts["local_account_1"] 129 token = suite.testTokens["local_account_1"] 130 user = suite.testUsers["local_account_1"] 131 limit *int = nil 132 offset *int = nil 133 resolve *bool = nil 134 query = "zork" 135 following *bool = nil 136 expectedHTTPStatus = http.StatusOK 137 expectedBody = "" 138 ) 139 140 accounts, err := suite.getSearch( 141 requestingAccount, 142 token, 143 user, 144 limit, 145 offset, 146 query, 147 resolve, 148 following, 149 expectedHTTPStatus, 150 expectedBody, 151 ) 152 153 if err != nil { 154 suite.FailNow(err.Error()) 155 } 156 157 if l := len(accounts); l != 1 { 158 suite.FailNow("", "expected length %d got %d", 1, l) 159 } 160 } 161 162 func (suite *AccountSearchTestSuite) TestSearchZorkExactOK() { 163 var ( 164 requestingAccount = suite.testAccounts["local_account_1"] 165 token = suite.testTokens["local_account_1"] 166 user = suite.testUsers["local_account_1"] 167 limit *int = nil 168 offset *int = nil 169 resolve *bool = nil 170 query = "@the_mighty_zork" 171 following *bool = nil 172 expectedHTTPStatus = http.StatusOK 173 expectedBody = "" 174 ) 175 176 accounts, err := suite.getSearch( 177 requestingAccount, 178 token, 179 user, 180 limit, 181 offset, 182 query, 183 resolve, 184 following, 185 expectedHTTPStatus, 186 expectedBody, 187 ) 188 189 if err != nil { 190 suite.FailNow(err.Error()) 191 } 192 193 if l := len(accounts); l != 1 { 194 suite.FailNow("", "expected length %d got %d", 1, l) 195 } 196 } 197 198 func (suite *AccountSearchTestSuite) TestSearchZorkWithDomainOK() { 199 var ( 200 requestingAccount = suite.testAccounts["local_account_1"] 201 token = suite.testTokens["local_account_1"] 202 user = suite.testUsers["local_account_1"] 203 limit *int = nil 204 offset *int = nil 205 resolve *bool = nil 206 query = "@the_mighty_zork@localhost:8080" 207 following *bool = nil 208 expectedHTTPStatus = http.StatusOK 209 expectedBody = "" 210 ) 211 212 accounts, err := suite.getSearch( 213 requestingAccount, 214 token, 215 user, 216 limit, 217 offset, 218 query, 219 resolve, 220 following, 221 expectedHTTPStatus, 222 expectedBody, 223 ) 224 225 if err != nil { 226 suite.FailNow(err.Error()) 227 } 228 229 if l := len(accounts); l != 1 { 230 suite.FailNow("", "expected length %d got %d", 1, l) 231 } 232 } 233 234 func (suite *AccountSearchTestSuite) TestSearchFossSatanNotFollowing() { 235 var ( 236 requestingAccount = suite.testAccounts["local_account_1"] 237 token = suite.testTokens["local_account_1"] 238 user = suite.testUsers["local_account_1"] 239 limit *int = nil 240 offset *int = nil 241 resolve *bool = nil 242 query = "foss_satan" 243 following *bool = func() *bool { i := false; return &i }() 244 expectedHTTPStatus = http.StatusOK 245 expectedBody = "" 246 ) 247 248 accounts, err := suite.getSearch( 249 requestingAccount, 250 token, 251 user, 252 limit, 253 offset, 254 query, 255 resolve, 256 following, 257 expectedHTTPStatus, 258 expectedBody, 259 ) 260 261 if err != nil { 262 suite.FailNow(err.Error()) 263 } 264 265 if l := len(accounts); l != 1 { 266 suite.FailNow("", "expected length %d got %d", 1, l) 267 } 268 } 269 270 func (suite *AccountSearchTestSuite) TestSearchFossSatanFollowing() { 271 var ( 272 requestingAccount = suite.testAccounts["local_account_1"] 273 token = suite.testTokens["local_account_1"] 274 user = suite.testUsers["local_account_1"] 275 limit *int = nil 276 offset *int = nil 277 resolve *bool = nil 278 query = "foss_satan" 279 following *bool = func() *bool { i := true; return &i }() 280 expectedHTTPStatus = http.StatusOK 281 expectedBody = "" 282 ) 283 284 accounts, err := suite.getSearch( 285 requestingAccount, 286 token, 287 user, 288 limit, 289 offset, 290 query, 291 resolve, 292 following, 293 expectedHTTPStatus, 294 expectedBody, 295 ) 296 297 if err != nil { 298 suite.FailNow(err.Error()) 299 } 300 301 if l := len(accounts); l != 0 { 302 suite.FailNow("", "expected length %d got %d", 0, l) 303 } 304 } 305 306 func (suite *AccountSearchTestSuite) TestSearchBonkersQuery() { 307 var ( 308 requestingAccount = suite.testAccounts["local_account_1"] 309 token = suite.testTokens["local_account_1"] 310 user = suite.testUsers["local_account_1"] 311 limit *int = nil 312 offset *int = nil 313 resolve *bool = nil 314 query = "aaaaa@aaaaaaaaa@aaaaa **** this won't@ return anything!@!!" 315 following *bool = nil 316 expectedHTTPStatus = http.StatusOK 317 expectedBody = "" 318 ) 319 320 accounts, err := suite.getSearch( 321 requestingAccount, 322 token, 323 user, 324 limit, 325 offset, 326 query, 327 resolve, 328 following, 329 expectedHTTPStatus, 330 expectedBody, 331 ) 332 333 if err != nil { 334 suite.FailNow(err.Error()) 335 } 336 337 if l := len(accounts); l != 0 { 338 suite.FailNow("", "expected length %d got %d", 0, l) 339 } 340 } 341 342 func (suite *AccountSearchTestSuite) TestSearchAFollowing() { 343 var ( 344 requestingAccount = suite.testAccounts["local_account_1"] 345 token = suite.testTokens["local_account_1"] 346 user = suite.testUsers["local_account_1"] 347 limit *int = nil 348 offset *int = nil 349 resolve *bool = nil 350 query = "a" 351 following *bool = nil 352 expectedHTTPStatus = http.StatusOK 353 expectedBody = "" 354 ) 355 356 accounts, err := suite.getSearch( 357 requestingAccount, 358 token, 359 user, 360 limit, 361 offset, 362 query, 363 resolve, 364 following, 365 expectedHTTPStatus, 366 expectedBody, 367 ) 368 369 if err != nil { 370 suite.FailNow(err.Error()) 371 } 372 373 if l := len(accounts); l != 5 { 374 suite.FailNow("", "expected length %d got %d", 5, l) 375 } 376 377 usernames := make([]string, 0, 5) 378 for _, account := range accounts { 379 usernames = append(usernames, account.Username) 380 } 381 382 suite.EqualValues([]string{"her_fuckin_maj", "foss_satan", "1happyturtle", "the_mighty_zork", "admin"}, usernames) 383 } 384 385 func (suite *AccountSearchTestSuite) TestSearchANotFollowing() { 386 var ( 387 requestingAccount = suite.testAccounts["local_account_1"] 388 token = suite.testTokens["local_account_1"] 389 user = suite.testUsers["local_account_1"] 390 limit *int = nil 391 offset *int = nil 392 resolve *bool = nil 393 query = "a" 394 following *bool = func() *bool { i := true; return &i }() 395 expectedHTTPStatus = http.StatusOK 396 expectedBody = "" 397 ) 398 399 accounts, err := suite.getSearch( 400 requestingAccount, 401 token, 402 user, 403 limit, 404 offset, 405 query, 406 resolve, 407 following, 408 expectedHTTPStatus, 409 expectedBody, 410 ) 411 412 if err != nil { 413 suite.FailNow(err.Error()) 414 } 415 416 if l := len(accounts); l != 2 { 417 suite.FailNow("", "expected length %d got %d", 2, l) 418 } 419 420 usernames := make([]string, 0, 2) 421 for _, account := range accounts { 422 usernames = append(usernames, account.Username) 423 } 424 425 suite.EqualValues([]string{"1happyturtle", "admin"}, usernames) 426 } 427 428 func TestAccountSearchTestSuite(t *testing.T) { 429 suite.Run(t, new(AccountSearchTestSuite)) 430 }