diff options
| -rw-r--r-- | internal/apimodule/account/test/accountcreate_test.go (renamed from internal/apimodule/account/accountcreate_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/account/test/accountupdate_test.go (renamed from internal/apimodule/account/accountupdate_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/account/test/accountverify_test.go (renamed from internal/apimodule/account/accountverify_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/admin/admin.go | 2 | ||||
| -rw-r--r-- | internal/apimodule/app/test/app_test.go (renamed from internal/apimodule/app/app_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/auth/auth.go | 4 | ||||
| -rw-r--r-- | internal/apimodule/auth/test/auth_test.go (renamed from internal/apimodule/auth/auth_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/fileserver/fileserver.go | 2 | ||||
| -rw-r--r-- | internal/apimodule/fileserver/test/servefile_test.go (renamed from internal/apimodule/fileserver/servefile_test.go) | 4 | ||||
| -rw-r--r-- | internal/apimodule/media/test/mediacreate_test.go (renamed from internal/apimodule/media/mediacreate_test.go) | 161 | ||||
| -rw-r--r-- | internal/apimodule/status/status.go | 20 | ||||
| -rw-r--r-- | internal/apimodule/status/statusreblog.go | 176 | ||||
| -rw-r--r-- | internal/apimodule/status/statusreblog_test.go | 265 | ||||
| -rw-r--r-- | internal/apimodule/status/test/statuscreate_test.go (renamed from internal/apimodule/status/statuscreate_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/status/test/statusfave_test.go (renamed from internal/apimodule/status/statusfave_test.go) | 4 | ||||
| -rw-r--r-- | internal/apimodule/status/test/statusfavedby_test.go (renamed from internal/apimodule/status/statusfavedby_test.go) | 4 | ||||
| -rw-r--r-- | internal/apimodule/status/test/statusget_test.go (renamed from internal/apimodule/status/statusget_test.go) | 2 | ||||
| -rw-r--r-- | internal/apimodule/status/test/statusunfave_test.go (renamed from internal/apimodule/status/statusunfave_test.go) | 4 | ||||
| -rw-r--r-- | internal/db/gtsmodel/status.go | 4 | ||||
| -rw-r--r-- | internal/mastotypes/converter.go | 50 | ||||
| -rw-r--r-- | testrig/testmodels.go | 24 | 
21 files changed, 114 insertions, 624 deletions
diff --git a/internal/apimodule/account/accountcreate_test.go b/internal/apimodule/account/test/accountcreate_test.go index da2b22510..81eab467a 100644 --- a/internal/apimodule/account/accountcreate_test.go +++ b/internal/apimodule/account/test/accountcreate_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package account_test +package account  import (  	"bytes" diff --git a/internal/apimodule/account/accountupdate_test.go b/internal/apimodule/account/test/accountupdate_test.go index 78664f19c..1c6f528a1 100644 --- a/internal/apimodule/account/accountupdate_test.go +++ b/internal/apimodule/account/test/accountupdate_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package account_test +package account  import (  	"bytes" diff --git a/internal/apimodule/account/accountverify_test.go b/internal/apimodule/account/test/accountverify_test.go index 85b0dce50..223a0c145 100644 --- a/internal/apimodule/account/accountverify_test.go +++ b/internal/apimodule/account/test/accountverify_test.go @@ -16,4 +16,4 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package account_test +package account diff --git a/internal/apimodule/admin/admin.go b/internal/apimodule/admin/admin.go index 200e7bd88..2ebe9c7a7 100644 --- a/internal/apimodule/admin/admin.go +++ b/internal/apimodule/admin/admin.go @@ -34,7 +34,7 @@ import (  const (  	// BasePath is the base API path for this module -	BasePath = "/api/v1/admin" +	BasePath  = "/api/v1/admin"  	// EmojiPath is used for posting/deleting custom emojis  	EmojiPath = BasePath + "/custom_emojis"  ) diff --git a/internal/apimodule/app/app_test.go b/internal/apimodule/app/test/app_test.go index 42760a2db..d45b04e74 100644 --- a/internal/apimodule/app/app_test.go +++ b/internal/apimodule/app/test/app_test.go @@ -16,6 +16,6 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package app_test +package app  // TODO: write tests diff --git a/internal/apimodule/auth/auth.go b/internal/apimodule/auth/auth.go index 0a6788c37..341805b40 100644 --- a/internal/apimodule/auth/auth.go +++ b/internal/apimodule/auth/auth.go @@ -32,9 +32,9 @@ import (  const (  	// AuthSignInPath is the API path for users to sign in through -	AuthSignInPath = "/auth/sign_in" +	AuthSignInPath     = "/auth/sign_in"  	// OauthTokenPath is the API path to use for granting token requests to users with valid credentials -	OauthTokenPath = "/oauth/token" +	OauthTokenPath     = "/oauth/token"  	// OauthAuthorizePath is the API path for authorization requests (eg., authorize this app to act on my behalf as a user)  	OauthAuthorizePath = "/oauth/authorize"  ) diff --git a/internal/apimodule/auth/auth_test.go b/internal/apimodule/auth/test/auth_test.go index 95ae0e751..2c272e985 100644 --- a/internal/apimodule/auth/auth_test.go +++ b/internal/apimodule/auth/test/auth_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package auth_test +package auth  import (  	"context" diff --git a/internal/apimodule/fileserver/fileserver.go b/internal/apimodule/fileserver/fileserver.go index 55b8781b6..7651c8cc1 100644 --- a/internal/apimodule/fileserver/fileserver.go +++ b/internal/apimodule/fileserver/fileserver.go @@ -39,7 +39,7 @@ const (  	// MediaSizeKey is the url key for the desired media size--original/small/static  	MediaSizeKey = "media_size"  	// FileNameKey is the actual filename being sought. Will usually be a UUID then something like .jpeg -	FileNameKey = "file_name" +	FileNameKey  = "file_name"  )  // FileServer implements the RESTAPIModule interface. diff --git a/internal/apimodule/fileserver/servefile_test.go b/internal/apimodule/fileserver/test/servefile_test.go index dbe979fb7..516e3528c 100644 --- a/internal/apimodule/fileserver/servefile_test.go +++ b/internal/apimodule/fileserver/test/servefile_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package fileserver_test +package test  import (  	"context" @@ -90,7 +90,7 @@ func (suite *ServeFileTestSuite) TearDownSuite() {  func (suite *ServeFileTestSuite) SetupTest() {  	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") +	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")  	suite.testTokens = testrig.NewTestTokens()  	suite.testClients = testrig.NewTestClients()  	suite.testApplications = testrig.NewTestApplications() diff --git a/internal/apimodule/media/mediacreate_test.go b/internal/apimodule/media/test/mediacreate_test.go index 8fca138ca..30bbb117a 100644 --- a/internal/apimodule/media/mediacreate_test.go +++ b/internal/apimodule/media/test/mediacreate_test.go @@ -16,19 +16,28 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package media_test +package test  import ( +	"bytes"  	"context" +	"encoding/json" +	"fmt" +	"io/ioutil" +	"net/http" +	"net/http/httptest"  	"testing" +	"github.com/gin-gonic/gin"  	"github.com/sirupsen/logrus" +	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	mediamodule "github.com/superseriousbusiness/gotosocial/internal/apimodule/media"  	"github.com/superseriousbusiness/gotosocial/internal/config"  	"github.com/superseriousbusiness/gotosocial/internal/db"  	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" +	mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"  	"github.com/superseriousbusiness/gotosocial/internal/media"  	"github.com/superseriousbusiness/gotosocial/internal/oauth"  	"github.com/superseriousbusiness/gotosocial/internal/storage" @@ -84,7 +93,7 @@ func (suite *MediaCreateTestSuite) TearDownSuite() {  func (suite *MediaCreateTestSuite) SetupTest() {  	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") +	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")  	suite.testTokens = testrig.NewTestTokens()  	suite.testClients = testrig.NewTestClients()  	suite.testApplications = testrig.NewTestApplications() @@ -104,80 +113,80 @@ func (suite *MediaCreateTestSuite) TearDownTest() {  func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() { -	// // set up the context for the request -	// t := suite.testTokens["local_account_1"] -	// oauthToken := oauth.TokenToOauthToken(t) -	// recorder := httptest.NewRecorder() -	// ctx, _ := gin.CreateTestContext(recorder) -	// 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"]) - -	// // see what's in storage *before* the request -	// storageKeysBeforeRequest, err := suite.storage.ListKeys() -	// if err != nil { -	// 	panic(err) -	// } - -	// // create the request -	// buf, w, err := testrig.CreateMultipartFormData("file", "../../../testrig/media/test-jpeg.jpg", map[string]string{ -	// 	"description": "this is a test image -- a cool background from somewhere", -	// 	"focus":       "-0.5,0.5", -	// }) -	// if err != nil { -	// 	panic(err) -	// } -	// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", mediamodule.BasePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting -	// ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) - -	// // do the actual request -	// suite.mediaModule.MediaCreatePOSTHandler(ctx) - -	// // check what's in storage *after* the request -	// storageKeysAfterRequest, err := suite.storage.ListKeys() -	// if err != nil { -	// 	panic(err) -	// } - -	// // check response -	// suite.EqualValues(http.StatusAccepted, recorder.Code) - -	// result := recorder.Result() -	// defer result.Body.Close() -	// b, err := ioutil.ReadAll(result.Body) -	// assert.NoError(suite.T(), err) -	// fmt.Println(string(b)) - -	// attachmentReply := &mastomodel.Attachment{} -	// err = json.Unmarshal(b, attachmentReply) -	// assert.NoError(suite.T(), err) - -	// assert.Equal(suite.T(), "this is a test image -- a cool background from somewhere", attachmentReply.Description) -	// assert.Equal(suite.T(), "image", attachmentReply.Type) -	// assert.EqualValues(suite.T(), mastomodel.MediaMeta{ -	// 	Original: mastomodel.MediaDimensions{ -	// 		Width:  1920, -	// 		Height: 1080, -	// 		Size:   "1920x1080", -	// 		Aspect: 1.7777778, -	// 	}, -	// 	Small: mastomodel.MediaDimensions{ -	// 		Width:  256, -	// 		Height: 144, -	// 		Size:   "256x144", -	// 		Aspect: 1.7777778, -	// 	}, -	// 	Focus: mastomodel.MediaFocus{ -	// 		X: -0.5, -	// 		Y: 0.5, -	// 	}, -	// }, attachmentReply.Meta) -	// assert.Equal(suite.T(), "LjCZnlvyRkRn_NvzRjWF?urqV@f9", attachmentReply.Blurhash) -	// assert.NotEmpty(suite.T(), attachmentReply.ID) -	// assert.NotEmpty(suite.T(), attachmentReply.URL) -	// assert.NotEmpty(suite.T(), attachmentReply.PreviewURL) -	// assert.Equal(suite.T(), len(storageKeysBeforeRequest)+2, len(storageKeysAfterRequest)) // 2 images should be added to storage: the original and the thumbnail +	// set up the context for the request +	t := suite.testTokens["local_account_1"] +	oauthToken := oauth.TokenToOauthToken(t) +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) +	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"]) + +	// see what's in storage *before* the request +	storageKeysBeforeRequest, err := suite.storage.ListKeys() +	if err != nil { +		panic(err) +	} + +	// create the request +	buf, w, err := testrig.CreateMultipartFormData("file", "../../../../testrig/media/test-jpeg.jpg", map[string]string{ +		"description": "this is a test image -- a cool background from somewhere", +		"focus":       "-0.5,0.5", +	}) +	if err != nil { +		panic(err) +	} +	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", mediamodule.BasePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting +	ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) + +	// do the actual request +	suite.mediaModule.MediaCreatePOSTHandler(ctx) + +	// check what's in storage *after* the request +	storageKeysAfterRequest, err := suite.storage.ListKeys() +	if err != nil { +		panic(err) +	} + +	// check response +	suite.EqualValues(http.StatusAccepted, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() +	b, err := ioutil.ReadAll(result.Body) +	assert.NoError(suite.T(), err) +	fmt.Println(string(b)) + +	attachmentReply := &mastomodel.Attachment{} +	err = json.Unmarshal(b, attachmentReply) +	assert.NoError(suite.T(), err) + +	assert.Equal(suite.T(), "this is a test image -- a cool background from somewhere", attachmentReply.Description) +	assert.Equal(suite.T(), "image", attachmentReply.Type) +	assert.EqualValues(suite.T(), mastomodel.MediaMeta{ +		Original: mastomodel.MediaDimensions{ +			Width:  1920, +			Height: 1080, +			Size:   "1920x1080", +			Aspect: 1.7777778, +		}, +		Small: mastomodel.MediaDimensions{ +			Width:  256, +			Height: 144, +			Size:   "256x144", +			Aspect: 1.7777778, +		}, +		Focus: mastomodel.MediaFocus{ +			X: -0.5, +			Y: 0.5, +		}, +	}, attachmentReply.Meta) +	assert.Equal(suite.T(), "LjCZnlvyRkRn_NvzRjWF?urqV@f9", attachmentReply.Blurhash) +	assert.NotEmpty(suite.T(), attachmentReply.ID) +	assert.NotEmpty(suite.T(), attachmentReply.URL) +	assert.NotEmpty(suite.T(), attachmentReply.PreviewURL) +	assert.Equal(suite.T(), len(storageKeysBeforeRequest)+2, len(storageKeysAfterRequest)) // 2 images should be added to storage: the original and the thumbnail  }  func TestMediaCreateTestSuite(t *testing.T) { diff --git a/internal/apimodule/status/status.go b/internal/apimodule/status/status.go index 900ec32b9..73a1b5847 100644 --- a/internal/apimodule/status/status.go +++ b/internal/apimodule/status/status.go @@ -37,9 +37,9 @@ import (  const (  	// IDKey is for status UUIDs -	IDKey = "id" +	IDKey          = "id"  	// BasePath is the base path for serving the status API -	BasePath = "/api/v1/statuses" +	BasePath       = "/api/v1/statuses"  	// BasePathWithID is just the base path with the ID key in it.  	// Use this anywhere you need to know the ID of the status being queried.  	BasePathWithID = BasePath + "/:" + IDKey @@ -48,31 +48,31 @@ const (  	ContextPath = BasePathWithID + "/context"  	// FavouritedPath is for seeing who's faved a given status -	FavouritedPath = BasePathWithID + "/favourited_by" +	FavouritedPath  = BasePathWithID + "/favourited_by"  	// FavouritePath is for posting a fave on a status -	FavouritePath = BasePathWithID + "/favourite" +	FavouritePath   = BasePathWithID + "/favourite"  	// UnfavouritePath is for removing a fave from a status  	UnfavouritePath = BasePathWithID + "/unfavourite"  	// RebloggedPath is for seeing who's boosted a given status  	RebloggedPath = BasePathWithID + "/reblogged_by"  	// ReblogPath is for boosting/reblogging a given status -	ReblogPath = BasePathWithID + "/reblog" +	ReblogPath    = BasePathWithID + "/reblog"  	// UnreblogPath is for undoing a boost/reblog of a given status -	UnreblogPath = BasePathWithID + "/unreblog" +	UnreblogPath  = BasePathWithID + "/unreblog"  	// BookmarkPath is for creating a bookmark on a given status -	BookmarkPath = BasePathWithID + "/bookmark" +	BookmarkPath   = BasePathWithID + "/bookmark"  	// UnbookmarkPath is for removing a bookmark from a given status  	UnbookmarkPath = BasePathWithID + "/unbookmark"  	// MutePath is for muting a given status so that notifications will no longer be received about it. -	MutePath = BasePathWithID + "/mute" +	MutePath   = BasePathWithID + "/mute"  	// UnmutePath is for undoing an existing mute  	UnmutePath = BasePathWithID + "/unmute"  	// PinPath is for pinning a status to an account profile so that it's the first thing people see -	PinPath = BasePathWithID + "/pin" +	PinPath   = BasePathWithID + "/pin"  	// UnpinPath is for undoing a pin and returning a status to the ever-swirling drain of time and entropy  	UnpinPath = BasePathWithID + "/unpin"  ) @@ -107,8 +107,6 @@ func (m *Module) Route(r router.Router) error {  	r.AttachHandler(http.MethodPost, FavouritePath, m.StatusFavePOSTHandler)  	r.AttachHandler(http.MethodPost, UnfavouritePath, m.StatusFavePOSTHandler) -	r.AttachHandler(http.MethodPost, ReblogPath, m.StatusReblogPOSTHandler) -  	r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)  	return nil  } diff --git a/internal/apimodule/status/statusreblog.go b/internal/apimodule/status/statusreblog.go deleted file mode 100644 index cf00679c0..000000000 --- a/internal/apimodule/status/statusreblog.go +++ /dev/null @@ -1,176 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org - -   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 status - -import ( -	"fmt" -	"net/http" -	"time" - -	"github.com/gin-gonic/gin" -	"github.com/google/uuid" -	"github.com/sirupsen/logrus" -	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" -	"github.com/superseriousbusiness/gotosocial/internal/distributor" -	"github.com/superseriousbusiness/gotosocial/internal/oauth" -	"github.com/superseriousbusiness/gotosocial/internal/util" -) - -// StatusReblogPOSTHandler handles boost/reblog requests against a given status ID -func (m *Module) StatusReblogPOSTHandler(c *gin.Context) { -	l := m.log.WithFields(logrus.Fields{ -		"func":        "StatusReblogPOSTHandler", -		"request_uri": c.Request.RequestURI, -		"user_agent":  c.Request.UserAgent(), -		"origin_ip":   c.ClientIP(), -	}) -	l.Debugf("entering function") - -	authed, err := oauth.MustAuth(c, true, false, true, true) // we don't really need an app here but we want everything else -	if err != nil { -		l.Debug("not authed so can't boost status") -		c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) -		return -	} - -	targetStatusID := c.Param(IDKey) -	if targetStatusID == "" { -		c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) -		return -	} - -	l.Tracef("going to search for target status %s", targetStatusID) -	targetStatus := >smodel.Status{} -	if err := m.db.GetByID(targetStatusID, targetStatus); err != nil { -		l.Errorf("error fetching status %s: %s", targetStatusID, err) -		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)}) -		return -	} - -	l.Tracef("going to search for target account %s", targetStatus.AccountID) -	targetAccount := >smodel.Account{} -	if err := m.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { -		l.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err) -		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)}) -		return -	} - -	l.Trace("going to get relevant accounts") -	relevantAccounts, err := m.db.PullRelevantAccountsFromStatus(targetStatus) -	if err != nil { -		l.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err) -		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)}) -		return -	} - -	l.Trace("going to see if status is visible") -	visible, err := m.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that -	if err != nil { -		l.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err) -		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)}) -		return -	} - -	if !visible { -		l.Trace("status is not visible so cannot be boosted") -		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)}) -		return -	} - -	// is the status boostable? -	if !targetStatus.VisibilityAdvanced.Boostable { -		l.Debug("status is not boostable") -		c.JSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("status %s not boostable", targetStatusID)}) -		return -	} - -	/* -		FROM THIS POINT ONWARDS WE ARE HAPPY WITH THE BOOST -- it is valid and we will try to create it -	*/ - -	// it's visible! it's boostable! so let's boost the FUCK out of it -	// first we create a new status and add some basic info to it -- this will be the wrapper for the boosted status - -	// the wrapper won't use the same ID as the boosted status so we generate some new UUIDs -	uris := util.GenerateURIs(authed.Account.Username, m.config.Protocol, m.config.Host) -	boostWrapperStatusID := uuid.NewString() -	boostWrapperStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, boostWrapperStatusID) -	boostWrapperStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, boostWrapperStatusID) - -	boostWrapperStatus := >smodel.Status{ -		ID:  boostWrapperStatusID, -		URI: boostWrapperStatusURI, -		URL: boostWrapperStatusURL, - -		// the boosted status is not created now, but the boost certainly is -		CreatedAt:                time.Now(), -		UpdatedAt:                time.Now(), -		Local:                    true, // always local since this is being done through the client API -		AccountID:                authed.Account.ID, -		CreatedWithApplicationID: authed.Application.ID, - -		// replies can be boosted, but boosts are never replies -		InReplyToID:        "", -		InReplyToAccountID: "", - -		// these will all be wrapped in the boosted status so set them empty here -		Attachments: []string{}, -		Tags:        []string{}, -		Mentions:    []string{}, -		Emojis:      []string{}, - -		// the below fields will be taken from the target status -		Content:             util.HTMLFormat(targetStatus.Content), // take content from target status -		ContentWarning:      targetStatus.ContentWarning,           // same warning as the target status -		ActivityStreamsType: targetStatus.ActivityStreamsType,      // same activitystreams type as target status -		Sensitive:           targetStatus.Sensitive, -		Language:            targetStatus.Language, -		Text:                targetStatus.Text, -		BoostOfID:           targetStatus.ID, -		Visibility:          targetStatus.Visibility, -		VisibilityAdvanced:  targetStatus.VisibilityAdvanced, - -		// attach these here for convenience -- the boosted status/account won't go in the DB -		// but they're needed in the distributor and for the frontend. Since we have them, we can -		// attach them so we don't need to fetch them again later (save some DB calls) -		GTSBoostedStatus:  targetStatus, -		GTSBoostedAccount: targetAccount, -	} - -	// put the boost in the database -	if err := m.db.Put(boostWrapperStatus); err != nil { -		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) -		return -	} - -	// pass to the distributor to take care of side effects asynchronously -- federation, mentions, updating metadata, etc, etc -	m.distributor.FromClientAPI() <- distributor.FromClientAPI{ -		APObjectType:   gtsmodel.ActivityStreamsNote, -		APActivityType: gtsmodel.ActivityStreamsAnnounce, // boost/reblog is an 'announce' activity -		Activity:       boostWrapperStatus, -	} - -	// return the frontend representation of the new status to the submitter -	mastoStatus, err := m.mastoConverter.StatusToMasto(boostWrapperStatus, authed.Account, authed.Account, targetAccount, nil, targetStatus) -	if err != nil { -		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) -		return -	} -	c.JSON(http.StatusOK, mastoStatus) -} diff --git a/internal/apimodule/status/statusreblog_test.go b/internal/apimodule/status/statusreblog_test.go deleted file mode 100644 index 094f8b24a..000000000 --- a/internal/apimodule/status/statusreblog_test.go +++ /dev/null @@ -1,265 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org - -   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 status_test - -import ( -	"encoding/json" -	"fmt" -	"io/ioutil" -	"net/http" -	"net/http/httptest" -	"strings" -	"testing" - -	"github.com/gin-gonic/gin" -	"github.com/sirupsen/logrus" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/suite" -	"github.com/superseriousbusiness/gotosocial/internal/apimodule/status" -	"github.com/superseriousbusiness/gotosocial/internal/config" -	"github.com/superseriousbusiness/gotosocial/internal/db" -	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" -	"github.com/superseriousbusiness/gotosocial/internal/distributor" -	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" -	mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" -	"github.com/superseriousbusiness/gotosocial/internal/media" -	"github.com/superseriousbusiness/gotosocial/internal/oauth" -	"github.com/superseriousbusiness/gotosocial/internal/storage" -	"github.com/superseriousbusiness/gotosocial/testrig" -) - -type StatusReblogTestSuite struct { -	// standard suite interfaces -	suite.Suite -	config         *config.Config -	db             db.DB -	log            *logrus.Logger -	storage        storage.Storage -	mastoConverter mastotypes.Converter -	mediaHandler   media.Handler -	oauthServer    oauth.Server -	distributor    distributor.Distributor - -	// standard suite models -	testTokens       map[string]*oauth.Token -	testClients      map[string]*oauth.Client -	testApplications map[string]*gtsmodel.Application -	testUsers        map[string]*gtsmodel.User -	testAccounts     map[string]*gtsmodel.Account -	testAttachments  map[string]*gtsmodel.MediaAttachment -	testStatuses     map[string]*gtsmodel.Status - -	// module being tested -	statusModule *status.Module -} - -/* -	TEST INFRASTRUCTURE -*/ - -// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout -func (suite *StatusReblogTestSuite) SetupSuite() { -	// setup standard items -	suite.config = testrig.NewTestConfig() -	suite.db = testrig.NewTestDB() -	suite.log = testrig.NewTestLog() -	suite.storage = testrig.NewTestStorage() -	suite.mastoConverter = testrig.NewTestMastoConverter(suite.db) -	suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) -	suite.oauthServer = testrig.NewTestOauthServer(suite.db) -	suite.distributor = testrig.NewTestDistributor() - -	// setup module being tested -	suite.statusModule = status.New(suite.config, suite.db, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*status.Module) -} - -func (suite *StatusReblogTestSuite) TearDownSuite() { -	testrig.StandardDBTeardown(suite.db) -	testrig.StandardStorageTeardown(suite.storage) -} - -func (suite *StatusReblogTestSuite) SetupTest() { -	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") -	suite.testTokens = testrig.NewTestTokens() -	suite.testClients = testrig.NewTestClients() -	suite.testApplications = testrig.NewTestApplications() -	suite.testUsers = testrig.NewTestUsers() -	suite.testAccounts = testrig.NewTestAccounts() -	suite.testAttachments = testrig.NewTestAttachments() -	suite.testStatuses = testrig.NewTestStatuses() -} - -// TearDownTest drops tables to make sure there's no data in the db -func (suite *StatusReblogTestSuite) TearDownTest() { -	testrig.StandardDBTeardown(suite.db) -	testrig.StandardStorageTeardown(suite.storage) -} - -/* -	ACTUAL TESTS -*/ - -// boost a status -func (suite *StatusReblogTestSuite) TestPostReblog() { - -	t := suite.testTokens["local_account_1"] -	oauthToken := oauth.TokenToOauthToken(t) - -	targetStatus := suite.testStatuses["admin_account_status_1"] - -	// setup -	recorder := httptest.NewRecorder() -	ctx, _ := gin.CreateTestContext(recorder) -	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(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting - -	// 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:   status.IDKey, -			Value: targetStatus.ID, -		}, -	} - -	suite.statusModule.StatusReblogPOSTHandler(ctx) - -	// check response -	suite.EqualValues(http.StatusOK, recorder.Code) - -	result := recorder.Result() -	defer result.Body.Close() -	b, err := ioutil.ReadAll(result.Body) -	assert.NoError(suite.T(), err) - -	fmt.Println(string(b)) - -	statusReply := &mastomodel.Status{} -	err = json.Unmarshal(b, statusReply) -	assert.NoError(suite.T(), err) - -	assert.False(suite.T(), statusReply.Sensitive) -	assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility) - -	assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText) -	assert.Equal(suite.T(), targetStatus.Content, statusReply.Content) -	assert.Equal(suite.T(), "the_mighty_zork", statusReply.Account.Username) -	assert.Len(suite.T(), statusReply.MediaAttachments, 0) -	assert.Len(suite.T(), statusReply.Mentions, 0) -	assert.Len(suite.T(), statusReply.Emojis, 0) -	assert.Len(suite.T(), statusReply.Tags, 0) - -	assert.NotNil(suite.T(), statusReply.Application) -	assert.Equal(suite.T(), "really cool gts application", statusReply.Application.Name) - -	assert.NotNil(suite.T(), statusReply.Reblog) -	assert.Equal(suite.T(), 1, statusReply.Reblog.ReblogsCount) -	assert.Equal(suite.T(), 1, statusReply.Reblog.FavouritesCount) -	assert.Equal(suite.T(), targetStatus.Content, statusReply.Reblog.Content) -	assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.Reblog.SpoilerText) -	assert.Equal(suite.T(), targetStatus.AccountID, statusReply.Reblog.Account.ID) -	assert.Len(suite.T(), statusReply.Reblog.MediaAttachments, 1) -	assert.Len(suite.T(), statusReply.Reblog.Tags, 1) -	assert.Len(suite.T(), statusReply.Reblog.Emojis, 1) -	assert.Equal(suite.T(), "superseriousbusiness", statusReply.Reblog.Application.Name) -} - -// try to boost a status that's not boostable -func (suite *StatusReblogTestSuite) TestPostUnboostable() { - -	t := suite.testTokens["local_account_1"] -	oauthToken := oauth.TokenToOauthToken(t) - -	targetStatus := suite.testStatuses["local_account_2_status_4"] - -	// setup -	recorder := httptest.NewRecorder() -	ctx, _ := gin.CreateTestContext(recorder) -	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(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting - -	// 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:   status.IDKey, -			Value: targetStatus.ID, -		}, -	} - -	suite.statusModule.StatusReblogPOSTHandler(ctx) - -	// check response -	suite.EqualValues(http.StatusForbidden, recorder.Code) // we 403 unboostable statuses - -	result := recorder.Result() -	defer result.Body.Close() -	b, err := ioutil.ReadAll(result.Body) -	assert.NoError(suite.T(), err) -	assert.Equal(suite.T(), fmt.Sprintf(`{"error":"status %s not boostable"}`, targetStatus.ID), string(b)) -} - -// try to boost a status that's not visible to the user -func (suite *StatusReblogTestSuite) TestPostNotVisible() { - -	t := suite.testTokens["local_account_2"] -	oauthToken := oauth.TokenToOauthToken(t) - -	targetStatus := suite.testStatuses["local_account_1_status_3"] // this is a mutual only status and these accounts aren't mutuals - -	// setup -	recorder := httptest.NewRecorder() -	ctx, _ := gin.CreateTestContext(recorder) -	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"]) -	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting - -	// 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:   status.IDKey, -			Value: targetStatus.ID, -		}, -	} - -	suite.statusModule.StatusReblogPOSTHandler(ctx) - -	// check response -	suite.EqualValues(http.StatusNotFound, recorder.Code) // we 404 statuses that aren't visible - -	result := recorder.Result() -	defer result.Body.Close() -	b, err := ioutil.ReadAll(result.Body) -	assert.NoError(suite.T(), err) -	assert.Equal(suite.T(), fmt.Sprintf(`{"error":"status %s not found"}`, targetStatus.ID), string(b)) -} - -func TestStatusReblogTestSuite(t *testing.T) { -	suite.Run(t, new(StatusReblogTestSuite)) -} diff --git a/internal/apimodule/status/statuscreate_test.go b/internal/apimodule/status/test/statuscreate_test.go index 8c2212b26..d143ac9a7 100644 --- a/internal/apimodule/status/statuscreate_test.go +++ b/internal/apimodule/status/test/statuscreate_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package status_test +package status  import (  	"encoding/json" diff --git a/internal/apimodule/status/statusfave_test.go b/internal/apimodule/status/test/statusfave_test.go index 824912513..9ccf58948 100644 --- a/internal/apimodule/status/statusfave_test.go +++ b/internal/apimodule/status/test/statusfave_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package status_test +package status  import (  	"encoding/json" @@ -96,7 +96,7 @@ func (suite *StatusFaveTestSuite) TearDownSuite() {  func (suite *StatusFaveTestSuite) SetupTest() {  	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") +	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")  	suite.testTokens = testrig.NewTestTokens()  	suite.testClients = testrig.NewTestClients()  	suite.testApplications = testrig.NewTestApplications() diff --git a/internal/apimodule/status/statusfavedby_test.go b/internal/apimodule/status/test/statusfavedby_test.go index b655f8365..169543a81 100644 --- a/internal/apimodule/status/statusfavedby_test.go +++ b/internal/apimodule/status/test/statusfavedby_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package status_test +package status  import (  	"encoding/json" @@ -92,7 +92,7 @@ func (suite *StatusFavedByTestSuite) TearDownSuite() {  func (suite *StatusFavedByTestSuite) SetupTest() {  	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") +	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")  	suite.testTokens = testrig.NewTestTokens()  	suite.testClients = testrig.NewTestClients()  	suite.testApplications = testrig.NewTestApplications() diff --git a/internal/apimodule/status/statusget_test.go b/internal/apimodule/status/test/statusget_test.go index bef51ee29..ce817d247 100644 --- a/internal/apimodule/status/statusget_test.go +++ b/internal/apimodule/status/test/statusget_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package status_test +package status  import (  	"testing" diff --git a/internal/apimodule/status/statusunfave_test.go b/internal/apimodule/status/test/statusunfave_test.go index fe7fdec9e..5f5277921 100644 --- a/internal/apimodule/status/statusunfave_test.go +++ b/internal/apimodule/status/test/statusunfave_test.go @@ -16,7 +16,7 @@     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -package status_test +package status  import (  	"encoding/json" @@ -96,7 +96,7 @@ func (suite *StatusUnfaveTestSuite) TearDownSuite() {  func (suite *StatusUnfaveTestSuite) SetupTest() {  	testrig.StandardDBSetup(suite.db) -	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") +	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")  	suite.testTokens = testrig.NewTestTokens()  	suite.testClients = testrig.NewTestClients()  	suite.testApplications = testrig.NewTestApplications() diff --git a/internal/db/gtsmodel/status.go b/internal/db/gtsmodel/status.go index 55ae91599..06ef61760 100644 --- a/internal/db/gtsmodel/status.go +++ b/internal/db/gtsmodel/status.go @@ -89,10 +89,6 @@ type Status struct {  	GTSReplyToStatus *Status `pg:"-"`  	// Account being replied to  	GTSReplyToAccount *Account `pg:"-"` -	// Status being boosted -	GTSBoostedStatus *Status `pg:"-"` -	// Account of the boosted status -	GTSBoostedAccount *Account `pg:"-"`  }  // Visibility represents the visibility granularity of a status. diff --git a/internal/mastotypes/converter.go b/internal/mastotypes/converter.go index c11e18638..e689b62da 100644 --- a/internal/mastotypes/converter.go +++ b/internal/mastotypes/converter.go @@ -380,55 +380,7 @@ func (c *converter) StatusToMasto(  		}  	} -	var mastoRebloggedStatus *mastotypes.Status -	if s.BoostOfID != "" { -		// the boosted status might have been set on this struct already so check first before doing db calls -		var gtsBoostedStatus *gtsmodel.Status -		if s.GTSBoostedStatus != nil { -			// it's set, great! -			gtsBoostedStatus = s.GTSBoostedStatus -		} else { -			// it's not set so fetch it from the db -			gtsBoostedStatus = >smodel.Status{} -			if err := c.db.GetByID(s.BoostOfID, gtsBoostedStatus); err != nil { -				return nil, fmt.Errorf("error getting boosted status with id %s: %s", s.BoostOfID, err) -			} -		} - -		// the boosted account might have been set on this struct already or passed as a param so check first before doing db calls -		var gtsBoostedAccount *gtsmodel.Account -		if s.GTSBoostedAccount != nil { -			// it's set, great! -			gtsBoostedAccount = s.GTSBoostedAccount -		} else if boostOfAccount != nil { -			// it's been given as a param, great! -			gtsBoostedAccount = boostOfAccount -		} else if boostOfAccount == nil && s.GTSBoostedAccount == nil { -			// it's not set so fetch it from the db -			gtsBoostedAccount = >smodel.Account{} -			if err := c.db.GetByID(gtsBoostedStatus.AccountID, gtsBoostedAccount); err != nil { -				return nil, fmt.Errorf("error getting boosted account %s from status with id %s: %s", gtsBoostedStatus.AccountID, s.BoostOfID, err) -			} -		} - -		// the boosted status might be a reply so check this -		var gtsBoostedReplyToAccount *gtsmodel.Account -		if gtsBoostedStatus.InReplyToAccountID != "" { -			gtsBoostedReplyToAccount = >smodel.Account{} -			if err := c.db.GetByID(gtsBoostedStatus.InReplyToAccountID, gtsBoostedReplyToAccount); err != nil { -				return nil, fmt.Errorf("error getting account that boosted status was a reply to: %s", err) -			} -		} - -		if gtsBoostedStatus != nil || gtsBoostedAccount != nil { -			mastoRebloggedStatus, err = c.StatusToMasto(gtsBoostedStatus, gtsBoostedAccount, requestingAccount, nil, gtsBoostedReplyToAccount, nil) -			if err != nil { -				return nil, fmt.Errorf("error converting boosted status to mastotype: %s", err) -			} -		} else { -			return nil, fmt.Errorf("boost of id was set to %s but that status or account was nil", s.BoostOfID) -		} -	} +	var mastoRebloggedStatus *mastotypes.Status // TODO  	var mastoApplication *mastotypes.Application  	if s.CreatedWithApplicationID != "" { diff --git a/testrig/testmodels.go b/testrig/testmodels.go index 2953ee59c..0d95ef21d 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -962,30 +962,6 @@ func NewTestStatuses() map[string]*gtsmodel.Status {  			},  			ActivityStreamsType: gtsmodel.ActivityStreamsNote,  		}, -		"local_account_2_status_4": { -			ID:                       "57e41a35-20da-4bc9-9cfd-db2089f924db", -			URI:                      "http://localhost:8080/users/1happyturtle/statuses/57e41a35-20da-4bc9-9cfd-db2089f924db", -			URL:                      "http://localhost:8080/@1happyturtle/statuses/57e41a35-20da-4bc9-9cfd-db2089f924db", -			Content:                  "🐢 this is a public status but I want it local only and not boostable 🐢", -			CreatedAt:                time.Now().Add(-1 * time.Minute), -			UpdatedAt:                time.Now().Add(-1 * time.Minute), -			Local:                    true, -			AccountID:                "eecaad73-5703-426d-9312-276641daa31e", -			InReplyToID:              "", -			BoostOfID:                "", -			ContentWarning:           "", -			Visibility:               gtsmodel.VisibilityPublic, -			Sensitive:                true, -			Language:                 "en", -			CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df", -			VisibilityAdvanced: >smodel.VisibilityAdvanced{ -				Federated: false, -				Boostable: false, -				Replyable: true, -				Likeable:  true, -			}, -			ActivityStreamsType: gtsmodel.ActivityStreamsNote, -		},  	}  }  | 
