summaryrefslogtreecommitdiff
path: root/internal/api
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2021-09-11 13:19:06 +0200
committerLibravatar GitHub <noreply@github.com>2021-09-11 13:19:06 +0200
commit9dc2255a8fab8ef0bc4b9f417c6131e4c468cb9c (patch)
treeae528bf14a3475bbea264ff26e5ffded3dfadf8a /internal/api
parentTest both dbs (#205) (diff)
downloadgotosocial-9dc2255a8fab8ef0bc4b9f417c6131e4c468cb9c.tar.xz
kim is a reply guy (#208)
* bun debug * bun trace logging hooks * more tests * fix up some stuffffff * drop the frontend cache until a proper fix is made * go fmt
Diffstat (limited to 'internal/api')
-rw-r--r--internal/api/client/account/accountupdate.go52
-rw-r--r--internal/api/client/account/accountupdate_test.go277
-rw-r--r--internal/api/model/status.go1
3 files changed, 320 insertions, 10 deletions
diff --git a/internal/api/client/account/accountupdate.go b/internal/api/client/account/accountupdate.go
index c38ede252..9a377f3b8 100644
--- a/internal/api/client/account/accountupdate.go
+++ b/internal/api/client/account/accountupdate.go
@@ -19,7 +19,9 @@
package account
import (
+ "fmt"
"net/http"
+ "strconv"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
@@ -107,17 +109,24 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
}
l.Tracef("retrieved account %+v", authed.Account.ID)
- form := &model.UpdateCredentialsRequest{}
- if err := c.ShouldBind(&form); err != nil || form == nil {
- l.Debugf("could not parse form from request: %s", err)
+ form, err := parseUpdateAccountForm(c)
+ if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
- l.Debugf("parsed request form %+v", form)
-
// if everything on the form is nil, then nothing has been set and we shouldn't continue
- if form.Discoverable == nil && form.Bot == nil && form.DisplayName == nil && form.Note == nil && form.Avatar == nil && form.Header == nil && form.Locked == nil && form.Source == nil && form.FieldsAttributes == nil {
+ if form.Discoverable == nil &&
+ form.Bot == nil &&
+ form.DisplayName == nil &&
+ form.Note == nil &&
+ form.Avatar == nil &&
+ form.Header == nil &&
+ form.Locked == nil &&
+ form.Source.Privacy == nil &&
+ form.Source.Sensitive == nil &&
+ form.Source.Language == nil &&
+ form.FieldsAttributes == nil {
l.Debugf("could not parse form from request")
c.JSON(http.StatusBadRequest, gin.H{"error": "empty form submitted"})
return
@@ -133,3 +142,34 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
l.Tracef("conversion successful, returning OK and mastosensitive account %+v", acctSensitive)
c.JSON(http.StatusOK, acctSensitive)
}
+
+func parseUpdateAccountForm(c *gin.Context) (*model.UpdateCredentialsRequest, error) {
+ // parse main fields from request
+ form := &model.UpdateCredentialsRequest{
+ Source: &model.UpdateSource{},
+ }
+ if err := c.ShouldBind(&form); err != nil || form == nil {
+ return nil, fmt.Errorf("could not parse form from request: %s", err)
+ }
+
+ // parse source field-by-field
+ sourceMap := c.PostFormMap("source")
+
+ if privacy, ok := sourceMap["privacy"]; ok {
+ form.Source.Privacy = &privacy
+ }
+
+ if sensitive, ok := sourceMap["sensitive"]; ok {
+ sensitiveBool, err := strconv.ParseBool(sensitive)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing form source[sensitive]: %s", err)
+ }
+ form.Source.Sensitive = &sensitiveBool
+ }
+
+ if language, ok := sourceMap["language"]; ok {
+ form.Source.Language = &language
+ }
+
+ return form, nil
+}
diff --git a/internal/api/client/account/accountupdate_test.go b/internal/api/client/account/accountupdate_test.go
index a02573631..bafda0e01 100644
--- a/internal/api/client/account/accountupdate_test.go
+++ b/internal/api/client/account/accountupdate_test.go
@@ -19,8 +19,8 @@
package account_test
import (
+ "context"
"encoding/json"
- "fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
@@ -37,7 +37,208 @@ type AccountUpdateTestSuite struct {
AccountStandardTestSuite
}
-func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerSimple() {
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler() {
+ // set up the request
+ // we're updating the note of zork
+ newBio := "this is my new bio read it and weep"
+ requestBody, w, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "note": newBio,
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes := requestBody.Bytes()
+ recorder := httptest.NewRecorder()
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder.Code)
+
+ // 2. we should have no error message in the result body
+ result := recorder.Result()
+ defer result.Body.Close()
+
+ // check the response
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ // unmarshal the returned account
+ apimodelAccount := &apimodel.Account{}
+ err = json.Unmarshal(b, apimodelAccount)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
+}
+
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnlockLock() {
+ // set up the first request
+ requestBody1, w1, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "locked": "false",
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes1 := requestBody1.Bytes()
+ recorder1 := httptest.NewRecorder()
+ ctx1 := suite.newContext(recorder1, http.MethodPatch, bodyBytes1, account.UpdateCredentialsPath, w1.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx1)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder1.Code)
+
+ // 2. we should have no error message in the result body
+ result1 := recorder1.Result()
+ defer result1.Body.Close()
+
+ // check the response
+ b1, err := ioutil.ReadAll(result1.Body)
+ assert.NoError(suite.T(), err)
+
+ // unmarshal the returned account
+ apimodelAccount1 := &apimodel.Account{}
+ err = json.Unmarshal(b1, apimodelAccount1)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.False(apimodelAccount1.Locked)
+
+ // set up the first request
+ requestBody2, w2, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "locked": "true",
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes2 := requestBody2.Bytes()
+ recorder2 := httptest.NewRecorder()
+ ctx2 := suite.newContext(recorder2, http.MethodPatch, bodyBytes2, account.UpdateCredentialsPath, w2.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx2)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder1.Code)
+
+ // 2. we should have no error message in the result body
+ result2 := recorder2.Result()
+ defer result2.Body.Close()
+
+ // check the response
+ b2, err := ioutil.ReadAll(result2.Body)
+ suite.NoError(err)
+
+ // unmarshal the returned account
+ apimodelAccount2 := &apimodel.Account{}
+ err = json.Unmarshal(b2, apimodelAccount2)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.True(apimodelAccount2.Locked)
+}
+
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGetAccountFirst() {
+ // 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 not return the old version
+ _, err := suite.db.GetAccountByID(context.Background(), suite.testAccounts["local_account_1"].ID)
+ suite.NoError(err)
+
+ // set up the request
+ // we're updating the note of zork
+ newBio := "this is my new bio read it and weep"
+ requestBody, w, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "note": newBio,
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes := requestBody.Bytes()
+ recorder := httptest.NewRecorder()
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder.Code)
+
+ // 2. we should have no error message in the result body
+ result := recorder.Result()
+ defer result.Body.Close()
+
+ // check the response
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ // unmarshal the returned account
+ apimodelAccount := &apimodel.Account{}
+ err = json.Unmarshal(b, apimodelAccount)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
+}
+
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwoFields() {
+ // set up the request
+ // we're updating the note of zork, and setting locked to true
+ newBio := "this is my new bio read it and weep"
+ requestBody, w, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "note": newBio,
+ "locked": "true",
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes := requestBody.Bytes()
+ recorder := httptest.NewRecorder()
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder.Code)
+
+ // 2. we should have no error message in the result body
+ result := recorder.Result()
+ defer result.Body.Close()
+
+ // check the response
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ // unmarshal the returned account
+ apimodelAccount := &apimodel.Account{}
+ err = json.Unmarshal(b, apimodelAccount)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
+ suite.True(apimodelAccount.Locked)
+}
+
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerWithMedia() {
// set up the request
// we're updating the header image, the display name, and the locked status of zork
// we're removing the note/bio
@@ -51,8 +252,9 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerSim
if err != nil {
panic(err)
}
+ bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, requestBody.Bytes(), account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
@@ -67,7 +269,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerSim
// check the response
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
- fmt.Println(string(b))
// unmarshal the returned account
apimodelAccount := &apimodel.Account{}
@@ -90,6 +291,74 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerSim
suite.NotEqual("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpeg", apimodelAccount.HeaderStatic)
}
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerEmptyForm() {
+ // set up the request
+ bodyBytes := []byte{}
+ recorder := httptest.NewRecorder()
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, "")
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusBadRequest, recorder.Code)
+
+ // 2. we should have no error message in the result body
+ result := recorder.Result()
+ defer result.Body.Close()
+
+ // check the response
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+ suite.Equal(`{"error":"empty form submitted"}`, string(b))
+}
+
+func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateSource() {
+ // set up the request
+ // we're updating the language of zork
+ newLanguage := "de"
+ requestBody, w, err := testrig.CreateMultipartFormData(
+ "", "",
+ map[string]string{
+ "source[privacy]": string(apimodel.VisibilityPrivate),
+ "source[language]": "de",
+ "source[sensitive]": "true",
+ "locked": "true",
+ })
+ if err != nil {
+ panic(err)
+ }
+ bodyBytes := requestBody.Bytes()
+ recorder := httptest.NewRecorder()
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+
+ // call the handler
+ suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+
+ // 1. we should have OK because our request was valid
+ suite.Equal(http.StatusOK, recorder.Code)
+
+ // 2. we should have no error message in the result body
+ result := recorder.Result()
+ defer result.Body.Close()
+
+ // check the response
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ // unmarshal the returned account
+ apimodelAccount := &apimodel.Account{}
+ err = json.Unmarshal(b, apimodelAccount)
+ suite.NoError(err)
+
+ // check the returned api model account
+ // fields should be updated
+ suite.Equal(newLanguage, apimodelAccount.Source.Language)
+ suite.EqualValues(apimodel.VisibilityPrivate, apimodelAccount.Source.Privacy)
+ suite.True(apimodelAccount.Source.Sensitive)
+ suite.True(apimodelAccount.Locked)
+}
+
func TestAccountUpdateTestSuite(t *testing.T) {
suite.Run(t, new(AccountUpdateTestSuite))
}
diff --git a/internal/api/model/status.go b/internal/api/model/status.go
index c5b5a4640..8be1a4870 100644
--- a/internal/api/model/status.go
+++ b/internal/api/model/status.go
@@ -160,6 +160,7 @@ type StatusCreateRequest struct {
// - public
// - unlisted
// - private
+// - mutuals_only
// - direct
type Visibility string