diff options
Diffstat (limited to 'internal/api/client/accounts')
-rw-r--r-- | internal/api/client/accounts/accountdelete_test.go | 6 | ||||
-rw-r--r-- | internal/api/client/accounts/accounts.go | 86 | ||||
-rw-r--r-- | internal/api/client/accounts/accountupdate_test.go | 2 | ||||
-rw-r--r-- | internal/api/client/accounts/lookup.go | 93 | ||||
-rw-r--r-- | internal/api/client/accounts/search.go | 166 | ||||
-rw-r--r-- | internal/api/client/accounts/search_test.go | 430 |
6 files changed, 728 insertions, 55 deletions
diff --git a/internal/api/client/accounts/accountdelete_test.go b/internal/api/client/accounts/accountdelete_test.go index fe328487b..d8889b680 100644 --- a/internal/api/client/accounts/accountdelete_test.go +++ b/internal/api/client/accounts/accountdelete_test.go @@ -44,7 +44,7 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandler() { } bodyBytes := requestBody.Bytes() recorder := httptest.NewRecorder() - ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType()) + ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeletePath, w.FormDataContentType()) // call the handler suite.accountsModule.AccountDeletePOSTHandler(ctx) @@ -66,7 +66,7 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerWrongPassword() } bodyBytes := requestBody.Bytes() recorder := httptest.NewRecorder() - ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType()) + ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeletePath, w.FormDataContentType()) // call the handler suite.accountsModule.AccountDeletePOSTHandler(ctx) @@ -86,7 +86,7 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerNoPassword() { } bodyBytes := requestBody.Bytes() recorder := httptest.NewRecorder() - ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType()) + ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeletePath, w.FormDataContentType()) // call the handler suite.accountsModule.AccountDeletePOSTHandler(ctx) diff --git a/internal/api/client/accounts/accounts.go b/internal/api/client/accounts/accounts.go index 298104a8d..9bb13231d 100644 --- a/internal/api/client/accounts/accounts.go +++ b/internal/api/client/accounts/accounts.go @@ -25,53 +25,33 @@ import ( ) const ( - // LimitKey is for setting the return amount limit for eg., requesting an account's statuses - LimitKey = "limit" - // ExcludeRepliesKey is for specifying whether to exclude replies in a list of returned statuses by an account. - ExcludeRepliesKey = "exclude_replies" - // ExcludeReblogsKey is for specifying whether to exclude reblogs in a list of returned statuses by an account. ExcludeReblogsKey = "exclude_reblogs" - // PinnedKey is for specifying whether to include pinned statuses in a list of returned statuses by an account. - PinnedKey = "pinned" - // MaxIDKey is for specifying the maximum ID of the status to retrieve. - MaxIDKey = "max_id" - // MinIDKey is for specifying the minimum ID of the status to retrieve. - MinIDKey = "min_id" - // OnlyMediaKey is for specifying that only statuses with media should be returned in a list of returned statuses by an account. - OnlyMediaKey = "only_media" - // OnlyPublicKey is for specifying that only statuses with visibility public should be returned in a list of returned statuses by account. - OnlyPublicKey = "only_public" - - // IDKey is the key to use for retrieving account ID in requests - IDKey = "id" - // BasePath is the base API path for this module, excluding the 'api' prefix - BasePath = "/v1/accounts" - // BasePathWithID is the base path for this module with the ID key + ExcludeRepliesKey = "exclude_replies" + LimitKey = "limit" + MaxIDKey = "max_id" + MinIDKey = "min_id" + OnlyMediaKey = "only_media" + OnlyPublicKey = "only_public" + PinnedKey = "pinned" + + BasePath = "/v1/accounts" + IDKey = "id" BasePathWithID = BasePath + "/:" + IDKey - // VerifyPath is for verifying account credentials - VerifyPath = BasePath + "/verify_credentials" - // UpdateCredentialsPath is for updating account credentials - UpdateCredentialsPath = BasePath + "/update_credentials" - // GetStatusesPath is for showing an account's statuses - GetStatusesPath = BasePathWithID + "/statuses" - // GetFollowersPath is for showing an account's followers - GetFollowersPath = BasePathWithID + "/followers" - // GetFollowingPath is for showing account's that an account follows. - GetFollowingPath = BasePathWithID + "/following" - // GetRelationshipsPath is for showing an account's relationship with other accounts - GetRelationshipsPath = BasePath + "/relationships" - // FollowPath is for POSTing new follows to, and updating existing follows - FollowPath = BasePathWithID + "/follow" - // UnfollowPath is for POSTing an unfollow - UnfollowPath = BasePathWithID + "/unfollow" - // BlockPath is for creating a block of an account - BlockPath = BasePathWithID + "/block" - // UnblockPath is for removing a block of an account - UnblockPath = BasePathWithID + "/unblock" - // DeleteAccountPath is for deleting one's account via the API - DeleteAccountPath = BasePath + "/delete" - // ListsPath is for seeing which lists an account is. - ListsPath = BasePathWithID + "/lists" + + BlockPath = BasePathWithID + "/block" + DeletePath = BasePath + "/delete" + FollowersPath = BasePathWithID + "/followers" + FollowingPath = BasePathWithID + "/following" + FollowPath = BasePathWithID + "/follow" + ListsPath = BasePathWithID + "/lists" + LookupPath = BasePath + "/lookup" + RelationshipsPath = BasePath + "/relationships" + SearchPath = BasePath + "/search" + StatusesPath = BasePathWithID + "/statuses" + UnblockPath = BasePathWithID + "/unblock" + UnfollowPath = BasePathWithID + "/unfollow" + UpdatePath = BasePath + "/update_credentials" + VerifyPath = BasePath + "/verify_credentials" ) type Module struct { @@ -92,23 +72,23 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H attachHandler(http.MethodGet, BasePathWithID, m.AccountGETHandler) // delete account - attachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler) + attachHandler(http.MethodPost, DeletePath, m.AccountDeletePOSTHandler) // verify account attachHandler(http.MethodGet, VerifyPath, m.AccountVerifyGETHandler) // modify account - attachHandler(http.MethodPatch, UpdateCredentialsPath, m.AccountUpdateCredentialsPATCHHandler) + attachHandler(http.MethodPatch, UpdatePath, m.AccountUpdateCredentialsPATCHHandler) // get account's statuses - attachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler) + attachHandler(http.MethodGet, StatusesPath, m.AccountStatusesGETHandler) // get following or followers - attachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler) - attachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler) + attachHandler(http.MethodGet, FollowersPath, m.AccountFollowersGETHandler) + attachHandler(http.MethodGet, FollowingPath, m.AccountFollowingGETHandler) // get relationship with account - attachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler) + attachHandler(http.MethodGet, RelationshipsPath, m.AccountRelationshipsGETHandler) // follow or unfollow account attachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler) @@ -120,4 +100,8 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H // account lists attachHandler(http.MethodGet, ListsPath, m.AccountListsGETHandler) + + // search for accounts + attachHandler(http.MethodGet, SearchPath, m.AccountSearchGETHandler) + attachHandler(http.MethodGet, LookupPath, m.AccountLookupGETHandler) } diff --git a/internal/api/client/accounts/accountupdate_test.go b/internal/api/client/accounts/accountupdate_test.go index f6bff4825..01d12ab27 100644 --- a/internal/api/client/accounts/accountupdate_test.go +++ b/internal/api/client/accounts/accountupdate_test.go @@ -76,7 +76,7 @@ func (suite *AccountUpdateTestSuite) updateAccount( ) (*apimodel.Account, error) { // Initialize http test context. recorder := httptest.NewRecorder() - ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, contentType) + ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdatePath, contentType) // Trigger the handler. suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx) diff --git a/internal/api/client/accounts/lookup.go b/internal/api/client/accounts/lookup.go new file mode 100644 index 000000000..4b31ea6cc --- /dev/null +++ b/internal/api/client/accounts/lookup.go @@ -0,0 +1,93 @@ +// 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/>. + +package accounts + +import ( + "net/http" + + "github.com/gin-gonic/gin" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// AccountLookupGETHandler swagger:operation GET /api/v1/accounts/lookup accountLookupGet +// +// Quickly lookup a username to see if it is available, skipping WebFinger resolution. +// +// --- +// tags: +// - accounts +// +// produces: +// - application/json +// +// parameters: +// - +// name: acct +// type: string +// description: The username or Webfinger address to lookup. +// in: query +// required: true +// +// security: +// - OAuth2 Bearer: +// - read:accounts +// +// responses: +// '200': +// name: lookup result +// description: Result of the lookup. +// schema: +// "$ref": "#/definitions/account" +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) AccountLookupGETHandler(c *gin.Context) { + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) + return + } + + query, errWithCode := apiutil.ParseSearchLookup(c.Query(apiutil.SearchLookupKey)) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + account, errWithCode := m.processor.Search().Lookup(c.Request.Context(), authed.Account, query) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, account) +} diff --git a/internal/api/client/accounts/search.go b/internal/api/client/accounts/search.go new file mode 100644 index 000000000..c10fb2960 --- /dev/null +++ b/internal/api/client/accounts/search.go @@ -0,0 +1,166 @@ +// 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/>. + +package accounts + +import ( + "net/http" + + "github.com/gin-gonic/gin" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// AccountSearchGETHandler swagger:operation GET /api/v1/accounts/search accountSearchGet +// +// Search for accounts by username and/or display name. +// +// --- +// tags: +// - accounts +// +// produces: +// - application/json +// +// parameters: +// - +// name: limit +// type: integer +// description: Number of results to try to return. +// default: 40 +// maximum: 80 +// minimum: 1 +// in: query +// - +// name: offset +// type: integer +// description: >- +// Page number of results to return (starts at 0). +// This parameter is currently not used, offsets +// over 0 will always return 0 results. +// default: 0 +// maximum: 10 +// minimum: 0 +// in: query +// - +// name: q +// type: string +// description: |- +// Query string to search for. This can be in the following forms: +// - `@[username]` -- search for an account with the given username on any domain. Can return multiple results. +// - `@[username]@[domain]` -- search for a remote account with exact username and domain. Will only ever return 1 result at most. +// - any arbitrary string -- search for accounts containing the given string in their username or display name. Can return multiple results. +// in: query +// required: true +// - +// name: resolve +// type: boolean +// description: >- +// If query is for `@[username]@[domain]`, or a URL, allow the GoToSocial instance to resolve +// the search by making calls to remote instances (webfinger, ActivityPub, etc). +// default: false +// in: query +// - +// name: following +// type: boolean +// description: >- +// Show only accounts that the requesting account follows. If this is set to `true`, then the GoToSocial instance +// will enhance the search by also searching within account notes, not just in usernames and display names. +// default: false +// in: query +// +// security: +// - OAuth2 Bearer: +// - read:accounts +// +// responses: +// '200': +// name: search results +// description: Results of the search. +// schema: +// type: array +// items: +// "$ref": "#/definitions/account" +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) AccountSearchGETHandler(c *gin.Context) { + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) + return + } + + limit, errWithCode := apiutil.ParseLimit(c.Query(apiutil.LimitKey), 40, 80, 1) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + offset, errWithCode := apiutil.ParseSearchOffset(c.Query(apiutil.SearchOffsetKey), 0, 10, 0) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + query, errWithCode := apiutil.ParseSearchQuery(c.Query(apiutil.SearchQueryKey)) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + resolve, errWithCode := apiutil.ParseSearchResolve(c.Query(apiutil.SearchResolveKey), false) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + following, errWithCode := apiutil.ParseSearchFollowing(c.Query(apiutil.SearchFollowingKey), false) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + results, errWithCode := m.processor.Search().Accounts( + c.Request.Context(), + authed.Account, + query, + limit, + offset, + resolve, + following, + ) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, results) +} diff --git a/internal/api/client/accounts/search_test.go b/internal/api/client/accounts/search_test.go new file mode 100644 index 000000000..7d778f090 --- /dev/null +++ b/internal/api/client/accounts/search_test.go @@ -0,0 +1,430 @@ +// 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/>. + +package accounts_test + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/oauth" + "github.com/superseriousbusiness/gotosocial/testrig" +) + +type AccountSearchTestSuite struct { + AccountStandardTestSuite +} + +func (suite *AccountSearchTestSuite) getSearch( + requestingAccount *gtsmodel.Account, + token *gtsmodel.Token, + user *gtsmodel.User, + limit *int, + offset *int, + query string, + resolve *bool, + following *bool, + expectedHTTPStatus int, + expectedBody string, +) ([]*apimodel.Account, error) { + var ( + recorder = httptest.NewRecorder() + ctx, _ = testrig.CreateGinTestContext(recorder, nil) + requestURL = testrig.URLMustParse("/api" + accounts.BasePath + "/search") + queryParts []string + ) + + // Put the request together. + if limit != nil { + queryParts = append(queryParts, apiutil.LimitKey+"="+strconv.Itoa(*limit)) + } + + if offset != nil { + queryParts = append(queryParts, apiutil.SearchOffsetKey+"="+strconv.Itoa(*offset)) + } + + queryParts = append(queryParts, apiutil.SearchQueryKey+"="+url.QueryEscape(query)) + + if resolve != nil { + queryParts = append(queryParts, apiutil.SearchResolveKey+"="+strconv.FormatBool(*resolve)) + } + + if following != nil { + queryParts = append(queryParts, apiutil.SearchFollowingKey+"="+strconv.FormatBool(*following)) + } + + requestURL.RawQuery = strings.Join(queryParts, "&") + ctx.Request = httptest.NewRequest(http.MethodGet, requestURL.String(), nil) + ctx.Set(oauth.SessionAuthorizedAccount, requestingAccount) + ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(token)) + ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) + ctx.Set(oauth.SessionAuthorizedUser, user) + + // Trigger the function being tested. + suite.accountsModule.AccountSearchGETHandler(ctx) + + // Read the result. + result := recorder.Result() + defer result.Body.Close() + + b, err := io.ReadAll(result.Body) + if err != nil { + suite.FailNow(err.Error()) + } + + errs := gtserror.MultiError{} + + // Check expected code + body. + if resultCode := recorder.Code; expectedHTTPStatus != resultCode { + errs = append(errs, fmt.Sprintf("expected %d got %d", expectedHTTPStatus, resultCode)) + } + + // 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))) + } + + if err := errs.Combine(); err != nil { + suite.FailNow("", "%v (body %s)", err, string(b)) + } + + accounts := []*apimodel.Account{} + if err := json.Unmarshal(b, &accounts); err != nil { + suite.FailNow(err.Error()) + } + + return accounts, nil +} + +func (suite *AccountSearchTestSuite) TestSearchZorkOK() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "zork" + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 1 { + suite.FailNow("", "expected length %d got %d", 1, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchZorkExactOK() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "@the_mighty_zork" + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 1 { + suite.FailNow("", "expected length %d got %d", 1, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchZorkWithDomainOK() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "@the_mighty_zork@localhost:8080" + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 1 { + suite.FailNow("", "expected length %d got %d", 1, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchFossSatanNotFollowing() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "foss_satan" + following *bool = func() *bool { i := false; return &i }() + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 1 { + suite.FailNow("", "expected length %d got %d", 1, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchFossSatanFollowing() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "foss_satan" + following *bool = func() *bool { i := true; return &i }() + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 0 { + suite.FailNow("", "expected length %d got %d", 0, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchBonkersQuery() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "aaaaa@aaaaaaaaa@aaaaa **** this won't@ return anything!@!!" + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 0 { + suite.FailNow("", "expected length %d got %d", 0, l) + } +} + +func (suite *AccountSearchTestSuite) TestSearchAFollowing() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "a" + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 5 { + suite.FailNow("", "expected length %d got %d", 5, l) + } + + usernames := make([]string, 0, 5) + for _, account := range accounts { + usernames = append(usernames, account.Username) + } + + suite.EqualValues([]string{"her_fuckin_maj", "foss_satan", "1happyturtle", "the_mighty_zork", "admin"}, usernames) +} + +func (suite *AccountSearchTestSuite) TestSearchANotFollowing() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "a" + following *bool = func() *bool { i := true; return &i }() + expectedHTTPStatus = http.StatusOK + expectedBody = "" + ) + + accounts, err := suite.getSearch( + requestingAccount, + token, + user, + limit, + offset, + query, + resolve, + following, + expectedHTTPStatus, + expectedBody, + ) + + if err != nil { + suite.FailNow(err.Error()) + } + + if l := len(accounts); l != 2 { + suite.FailNow("", "expected length %d got %d", 2, l) + } + + usernames := make([]string, 0, 2) + for _, account := range accounts { + usernames = append(usernames, account.Username) + } + + suite.EqualValues([]string{"1happyturtle", "admin"}, usernames) +} + +func TestAccountSearchTestSuite(t *testing.T) { + suite.Run(t, new(AccountSearchTestSuite)) +} |