mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-27 22:26:40 +00:00
21bb324156
* start updating media manager interface ready for storing attachments / emoji right away * store emoji and media as uncached immediately, then (re-)cache on Processing{}.Load() * remove now unused media workers * fix tests and issues * fix another test! * fix emoji activitypub uri setting behaviour, fix remainder of test compilation issues * fix more tests * fix (most of) remaining tests, add debouncing to repeatedly failing media / emojis * whoops, rebase issue * remove kim's whacky experiments * do some reshuffling, ensure emoji uri gets set * ensure marked as not cached on cleanup * tweaks to media / emoji processing to handle context canceled better * ensure newly fetched emojis actually get set in returned slice * use different varnames to be a bit more obvious * move emoji refresh rate limiting to dereferencer * add exported dereferencer functions for remote media, use these for recaching in processor * add check for nil attachment in updateAttachment() * remove unused emoji and media fields + columns * see previous commit * fix old migrations expecting image_updated_at to exists (from copies of old models) * remove freshness checking code (seems to be broken...) * fix error arg causing nil ptr exception * finish documentating functions with comments, slight tweaks to media / emoji deref error logic * remove some extra unneeded boolean checking * finish writing documentation (code comments) for exported media manager methods * undo changes to migration snapshot gtsmodels, updated failing migration to have its own snapshot * move doesColumnExist() to util.go in migrations package
611 lines
20 KiB
Go
611 lines
20 KiB
Go
// 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 users_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/suite"
|
|
"github.com/superseriousbusiness/activity/pub"
|
|
"github.com/superseriousbusiness/activity/streams"
|
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
|
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
|
)
|
|
|
|
type InboxPostTestSuite struct {
|
|
UserStandardTestSuite
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) inboxPost(
|
|
activity pub.Activity,
|
|
requestingAccount *gtsmodel.Account,
|
|
targetAccount *gtsmodel.Account,
|
|
expectedHTTPStatus int,
|
|
expectedBody string,
|
|
middlewares ...func(*gin.Context),
|
|
) {
|
|
var (
|
|
recorder = httptest.NewRecorder()
|
|
ctx, _ = testrig.CreateGinTestContext(recorder, nil)
|
|
)
|
|
|
|
// Prepare the requst body bytes.
|
|
bodyI, err := ap.Serialize(activity)
|
|
if err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
|
|
b, err := json.MarshalIndent(bodyI, "", " ")
|
|
if err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
suite.T().Logf("prepared POST body:\n%s", string(b))
|
|
|
|
// Prepare signature headers for this Activity.
|
|
signature, digestHeader, dateHeader := testrig.GetSignatureForActivity(
|
|
activity,
|
|
requestingAccount.PublicKeyURI,
|
|
requestingAccount.PrivateKey,
|
|
testrig.URLMustParse(targetAccount.InboxURI),
|
|
)
|
|
|
|
// Put the request together.
|
|
ctx.AddParam(users.UsernameKey, targetAccount.Username)
|
|
ctx.Request = httptest.NewRequest(http.MethodPost, targetAccount.InboxURI, bytes.NewReader(b))
|
|
ctx.Request.Header.Set("Signature", signature)
|
|
ctx.Request.Header.Set("Date", dateHeader)
|
|
ctx.Request.Header.Set("Digest", digestHeader)
|
|
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
|
|
|
// Pass the context through provided middlewares.
|
|
for _, middleware := range middlewares {
|
|
middleware(ctx)
|
|
}
|
|
|
|
// Trigger the function being tested.
|
|
suite.userModule.InboxPOSTHandler(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.NewMultiError(2)
|
|
|
|
// Check expected code + body.
|
|
if resultCode := recorder.Code; expectedHTTPStatus != resultCode {
|
|
errs.Appendf("expected %d got %d", expectedHTTPStatus, resultCode)
|
|
}
|
|
|
|
// If we got an expected body, return early.
|
|
if expectedBody != "" && string(b) != expectedBody {
|
|
errs.Appendf("expected %s got %s", expectedBody, string(b))
|
|
}
|
|
|
|
if err := errs.Combine(); err != nil {
|
|
suite.FailNow("", "%v (body %s)", err, string(b))
|
|
}
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) newBlock(blockID string, blockingAccount *gtsmodel.Account, blockedAccount *gtsmodel.Account) vocab.ActivityStreamsBlock {
|
|
block := streams.NewActivityStreamsBlock()
|
|
|
|
// set the actor property to the block-ing account's URI
|
|
actorProp := streams.NewActivityStreamsActorProperty()
|
|
actorIRI := testrig.URLMustParse(blockingAccount.URI)
|
|
actorProp.AppendIRI(actorIRI)
|
|
block.SetActivityStreamsActor(actorProp)
|
|
|
|
// set the ID property to the blocks's URI
|
|
idProp := streams.NewJSONLDIdProperty()
|
|
idProp.Set(testrig.URLMustParse(blockID))
|
|
block.SetJSONLDId(idProp)
|
|
|
|
// set the object property to the target account's URI
|
|
objectProp := streams.NewActivityStreamsObjectProperty()
|
|
targetIRI := testrig.URLMustParse(blockedAccount.URI)
|
|
objectProp.AppendIRI(targetIRI)
|
|
block.SetActivityStreamsObject(objectProp)
|
|
|
|
// set the TO property to the target account's IRI
|
|
toProp := streams.NewActivityStreamsToProperty()
|
|
toIRI := testrig.URLMustParse(blockedAccount.URI)
|
|
toProp.AppendIRI(toIRI)
|
|
block.SetActivityStreamsTo(toProp)
|
|
|
|
return block
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) newUndo(
|
|
originalActivity pub.Activity,
|
|
objectF func() vocab.ActivityStreamsObjectProperty,
|
|
to string,
|
|
undoIRI string,
|
|
) vocab.ActivityStreamsUndo {
|
|
undo := streams.NewActivityStreamsUndo()
|
|
|
|
// Set the appropriate actor.
|
|
undo.SetActivityStreamsActor(originalActivity.GetActivityStreamsActor())
|
|
|
|
// Set the original activity uri as the 'object' property.
|
|
undo.SetActivityStreamsObject(objectF())
|
|
|
|
// Set the To of the undo as the target of the activity.
|
|
undoTo := streams.NewActivityStreamsToProperty()
|
|
undoTo.AppendIRI(testrig.URLMustParse(to))
|
|
undo.SetActivityStreamsTo(undoTo)
|
|
|
|
// Set the ID property to the undo's URI.
|
|
undoID := streams.NewJSONLDIdProperty()
|
|
undoID.SetIRI(testrig.URLMustParse(undoIRI))
|
|
undo.SetJSONLDId(undoID)
|
|
|
|
return undo
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) newUpdatePerson(person vocab.ActivityStreamsPerson, cc string, updateIRI string) vocab.ActivityStreamsUpdate {
|
|
// create an update
|
|
update := streams.NewActivityStreamsUpdate()
|
|
|
|
// set the appropriate actor on it
|
|
updateActor := streams.NewActivityStreamsActorProperty()
|
|
updateActor.AppendIRI(person.GetJSONLDId().Get())
|
|
update.SetActivityStreamsActor(updateActor)
|
|
|
|
// Set the person as the 'object' property.
|
|
updateObject := streams.NewActivityStreamsObjectProperty()
|
|
updateObject.AppendActivityStreamsPerson(person)
|
|
update.SetActivityStreamsObject(updateObject)
|
|
|
|
// Set the To of the update as public
|
|
updateTo := streams.NewActivityStreamsToProperty()
|
|
updateTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
|
update.SetActivityStreamsTo(updateTo)
|
|
|
|
// set the cc of the update to the receivingAccount
|
|
updateCC := streams.NewActivityStreamsCcProperty()
|
|
updateCC.AppendIRI(testrig.URLMustParse(cc))
|
|
update.SetActivityStreamsCc(updateCC)
|
|
|
|
// set some random-ass ID for the activity
|
|
updateID := streams.NewJSONLDIdProperty()
|
|
updateID.SetIRI(testrig.URLMustParse(updateIRI))
|
|
update.SetJSONLDId(updateID)
|
|
|
|
return update
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, deleteIRI string) vocab.ActivityStreamsDelete {
|
|
// create a delete
|
|
delete := streams.NewActivityStreamsDelete()
|
|
|
|
// set the appropriate actor on it
|
|
deleteActor := streams.NewActivityStreamsActorProperty()
|
|
deleteActor.AppendIRI(testrig.URLMustParse(actorIRI))
|
|
delete.SetActivityStreamsActor(deleteActor)
|
|
|
|
// Set 'object' property.
|
|
deleteObject := streams.NewActivityStreamsObjectProperty()
|
|
deleteObject.AppendIRI(testrig.URLMustParse(objectIRI))
|
|
delete.SetActivityStreamsObject(deleteObject)
|
|
|
|
// Set the To of the delete as public
|
|
deleteTo := streams.NewActivityStreamsToProperty()
|
|
deleteTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
|
delete.SetActivityStreamsTo(deleteTo)
|
|
|
|
// set some random-ass ID for the activity
|
|
deleteID := streams.NewJSONLDIdProperty()
|
|
deleteID.SetIRI(testrig.URLMustParse(deleteIRI))
|
|
delete.SetJSONLDId(deleteID)
|
|
|
|
return delete
|
|
}
|
|
|
|
// TestPostBlock verifies that a remote account can block one of
|
|
// our instance users.
|
|
func (suite *InboxPostTestSuite) TestPostBlock() {
|
|
var (
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
activityID = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
|
)
|
|
|
|
block := suite.newBlock(activityID, requestingAccount, targetAccount)
|
|
|
|
// Block.
|
|
suite.inboxPost(
|
|
block,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
|
|
// Ensure block created in the database.
|
|
var (
|
|
dbBlock *gtsmodel.Block
|
|
err error
|
|
)
|
|
|
|
if !testrig.WaitFor(func() bool {
|
|
dbBlock, err = suite.db.GetBlock(context.Background(), requestingAccount.ID, targetAccount.ID)
|
|
return err == nil && dbBlock != nil
|
|
}) {
|
|
suite.FailNow("timed out waiting for block to be created")
|
|
}
|
|
}
|
|
|
|
// TestPostUnblock verifies that a remote account who blocks
|
|
// one of our instance users should be able to undo that block.
|
|
func (suite *InboxPostTestSuite) TestPostUnblock() {
|
|
var (
|
|
ctx = context.Background()
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
blockID = "http://fossbros-anonymous.io/blocks/01H1462TPRTVG2RTQCTSQ7N6Q0"
|
|
undoID = "http://fossbros-anonymous.io/some-activity/01H1463RDQNG5H98F29BXYHW6B"
|
|
)
|
|
|
|
// Put a block in the database so we have something to undo.
|
|
block := >smodel.Block{
|
|
ID: id.NewULID(),
|
|
URI: blockID,
|
|
AccountID: requestingAccount.ID,
|
|
TargetAccountID: targetAccount.ID,
|
|
}
|
|
if err := suite.db.PutBlock(ctx, block); err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
|
|
// Create the undo from the AS model block.
|
|
asBlock, err := suite.tc.BlockToAS(ctx, block)
|
|
if err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
|
|
undo := suite.newUndo(asBlock, func() vocab.ActivityStreamsObjectProperty {
|
|
// Append the whole block as Object.
|
|
op := streams.NewActivityStreamsObjectProperty()
|
|
op.AppendActivityStreamsBlock(asBlock)
|
|
return op
|
|
}, targetAccount.URI, undoID)
|
|
|
|
// Undo.
|
|
suite.inboxPost(
|
|
undo,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
|
|
// Ensure block removed from the database.
|
|
if !testrig.WaitFor(func() bool {
|
|
_, err := suite.db.GetBlockByID(ctx, block.ID)
|
|
return errors.Is(err, db.ErrNoEntries)
|
|
}) {
|
|
suite.FailNow("timed out waiting for block to be removed")
|
|
}
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|
var (
|
|
requestingAccount = new(gtsmodel.Account)
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
activityID = "http://fossbros-anonymous.io/72cc96a3-f742-4daf-b9f5-3407667260c5"
|
|
updatedDisplayName = "updated display name!"
|
|
)
|
|
|
|
// Copy the requesting account, since we'll be changing it.
|
|
*requestingAccount = *suite.testAccounts["remote_account_1"]
|
|
|
|
// Update the account's display name.
|
|
requestingAccount.DisplayName = updatedDisplayName
|
|
|
|
// Add an emoji to the account; because we're serializing this
|
|
// remote account from our own instance, we need to cheat a bit
|
|
// to get the emoji to work properly, just for this test.
|
|
testEmoji := >smodel.Emoji{}
|
|
*testEmoji = *testrig.NewTestEmojis()["yell"]
|
|
testEmoji.ImageURL = testEmoji.ImageRemoteURL // <- here's the cheat
|
|
requestingAccount.Emojis = []*gtsmodel.Emoji{testEmoji}
|
|
|
|
// Create an update from the account.
|
|
asAccount, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
|
if err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
update := suite.newUpdatePerson(asAccount, targetAccount.URI, activityID)
|
|
|
|
// Update.
|
|
suite.inboxPost(
|
|
update,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
|
|
// account should be changed in the database now
|
|
var dbUpdatedAccount *gtsmodel.Account
|
|
|
|
if !testrig.WaitFor(func() bool {
|
|
// displayName should be updated
|
|
dbUpdatedAccount, _ = suite.db.GetAccountByID(context.Background(), requestingAccount.ID)
|
|
return dbUpdatedAccount.DisplayName == updatedDisplayName
|
|
}) {
|
|
suite.FailNow("timed out waiting for account update")
|
|
}
|
|
|
|
// emojis should be updated
|
|
var haveUpdatedEmoji bool
|
|
for _, emoji := range dbUpdatedAccount.Emojis {
|
|
if emoji.Shortcode == testEmoji.Shortcode &&
|
|
emoji.Domain == testEmoji.Domain &&
|
|
emoji.ImageRemoteURL == emoji.ImageRemoteURL &&
|
|
emoji.ImageStaticRemoteURL == emoji.ImageStaticRemoteURL {
|
|
haveUpdatedEmoji = true
|
|
break
|
|
}
|
|
}
|
|
suite.True(haveUpdatedEmoji)
|
|
|
|
// account should be freshly fetched
|
|
suite.WithinDuration(time.Now(), dbUpdatedAccount.FetchedAt, 10*time.Second)
|
|
|
|
// everything else should be the same as it was before
|
|
suite.EqualValues(requestingAccount.Username, dbUpdatedAccount.Username)
|
|
suite.EqualValues(requestingAccount.Domain, dbUpdatedAccount.Domain)
|
|
suite.EqualValues(requestingAccount.AvatarMediaAttachmentID, dbUpdatedAccount.AvatarMediaAttachmentID)
|
|
suite.EqualValues(requestingAccount.AvatarMediaAttachment, dbUpdatedAccount.AvatarMediaAttachment)
|
|
suite.EqualValues(requestingAccount.AvatarRemoteURL, dbUpdatedAccount.AvatarRemoteURL)
|
|
suite.EqualValues(requestingAccount.HeaderMediaAttachmentID, dbUpdatedAccount.HeaderMediaAttachmentID)
|
|
suite.EqualValues(requestingAccount.HeaderMediaAttachment, dbUpdatedAccount.HeaderMediaAttachment)
|
|
suite.EqualValues(requestingAccount.HeaderRemoteURL, dbUpdatedAccount.HeaderRemoteURL)
|
|
suite.EqualValues(requestingAccount.Note, dbUpdatedAccount.Note)
|
|
suite.EqualValues(requestingAccount.Memorial, dbUpdatedAccount.Memorial)
|
|
suite.EqualValues(requestingAccount.AlsoKnownAsURIs, dbUpdatedAccount.AlsoKnownAsURIs)
|
|
suite.EqualValues(requestingAccount.MovedToURI, dbUpdatedAccount.MovedToURI)
|
|
suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot)
|
|
suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked)
|
|
suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable)
|
|
suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI)
|
|
suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL)
|
|
suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI)
|
|
suite.EqualValues(requestingAccount.OutboxURI, dbUpdatedAccount.OutboxURI)
|
|
suite.EqualValues(requestingAccount.FollowingURI, dbUpdatedAccount.FollowingURI)
|
|
suite.EqualValues(requestingAccount.FollowersURI, dbUpdatedAccount.FollowersURI)
|
|
suite.EqualValues(requestingAccount.FeaturedCollectionURI, dbUpdatedAccount.FeaturedCollectionURI)
|
|
suite.EqualValues(requestingAccount.ActorType, dbUpdatedAccount.ActorType)
|
|
suite.EqualValues(requestingAccount.PublicKey, dbUpdatedAccount.PublicKey)
|
|
suite.EqualValues(requestingAccount.PublicKeyURI, dbUpdatedAccount.PublicKeyURI)
|
|
suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt)
|
|
suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt)
|
|
suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt)
|
|
suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostDelete() {
|
|
var (
|
|
ctx = context.Background()
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
activityID = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
|
)
|
|
|
|
delete := suite.newDelete(requestingAccount.URI, requestingAccount.URI, activityID)
|
|
|
|
// Delete.
|
|
suite.inboxPost(
|
|
delete,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
|
|
if !testrig.WaitFor(func() bool {
|
|
// local account 2 blocked foss_satan, that block should be gone now
|
|
testBlock := suite.testBlocks["local_account_2_block_remote_account_1"]
|
|
_, err := suite.db.GetBlockByID(ctx, testBlock.ID)
|
|
return suite.ErrorIs(err, db.ErrNoEntries)
|
|
}) {
|
|
suite.FailNow("timed out waiting for block to be removed")
|
|
}
|
|
|
|
if !testrig.WaitFor(func() bool {
|
|
// no statuses from foss satan should be left in the database
|
|
dbStatuses, err := suite.db.GetAccountStatuses(ctx, requestingAccount.ID, 0, false, false, "", "", false, false)
|
|
return len(dbStatuses) == 0 && errors.Is(err, db.ErrNoEntries)
|
|
}) {
|
|
suite.FailNow("timed out waiting for statuses to be removed")
|
|
}
|
|
|
|
// Account should be stubbified.
|
|
dbAccount, err := suite.db.GetAccountByID(ctx, requestingAccount.ID)
|
|
suite.NoError(err)
|
|
suite.Empty(dbAccount.Note)
|
|
suite.Empty(dbAccount.DisplayName)
|
|
suite.Empty(dbAccount.AvatarMediaAttachmentID)
|
|
suite.Empty(dbAccount.AvatarRemoteURL)
|
|
suite.Empty(dbAccount.HeaderMediaAttachmentID)
|
|
suite.Empty(dbAccount.HeaderRemoteURL)
|
|
suite.Empty(dbAccount.Fields)
|
|
suite.False(*dbAccount.Discoverable)
|
|
suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second)
|
|
suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostEmptyCreate() {
|
|
var (
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
)
|
|
|
|
// Post a create with no object, this
|
|
// should get accepted and silently dropped
|
|
// as the lack of ID marks it as transient.
|
|
create := streams.NewActivityStreamsCreate()
|
|
|
|
suite.inboxPost(
|
|
create,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostCreateMalformedBlock() {
|
|
var (
|
|
blockingAcc = suite.testAccounts["remote_account_1"]
|
|
blockedAcc = suite.testAccounts["local_account_1"]
|
|
activityID = blockingAcc.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
|
)
|
|
|
|
block := streams.NewActivityStreamsBlock()
|
|
|
|
// set the actor property to the block-ing account's URI
|
|
actorProp := streams.NewActivityStreamsActorProperty()
|
|
actorIRI := testrig.URLMustParse(blockingAcc.URI)
|
|
actorProp.AppendIRI(actorIRI)
|
|
block.SetActivityStreamsActor(actorProp)
|
|
|
|
// set the ID property to the blocks's URI
|
|
idProp := streams.NewJSONLDIdProperty()
|
|
idProp.Set(testrig.URLMustParse(activityID))
|
|
block.SetJSONLDId(idProp)
|
|
|
|
// set the object property with MISSING block-ed URI.
|
|
objectProp := streams.NewActivityStreamsObjectProperty()
|
|
block.SetActivityStreamsObject(objectProp)
|
|
|
|
// set the TO property to the target account's IRI
|
|
toProp := streams.NewActivityStreamsToProperty()
|
|
toIRI := testrig.URLMustParse(blockedAcc.URI)
|
|
toProp.AppendIRI(toIRI)
|
|
block.SetActivityStreamsTo(toProp)
|
|
|
|
suite.inboxPost(
|
|
block,
|
|
blockingAcc,
|
|
blockedAcc,
|
|
http.StatusBadRequest,
|
|
`{"error":"Bad Request: malformed incoming activity"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostFromBlockedAccount() {
|
|
var (
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_2"]
|
|
activityID = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
|
)
|
|
|
|
person, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
|
if err != nil {
|
|
suite.FailNow(err.Error())
|
|
}
|
|
|
|
// Post an update from foss satan to turtle, who blocks him.
|
|
update := suite.newUpdatePerson(person, targetAccount.URI, activityID)
|
|
|
|
suite.inboxPost(
|
|
update,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusForbidden,
|
|
`{"error":"Forbidden: blocked"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostFromBlockedAccountToOtherAccount() {
|
|
var (
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
activity = suite.testActivities["reply_to_turtle_for_turtle"]
|
|
statusURI = "http://fossbros-anonymous.io/users/foss_satan/statuses/2f1195a6-5cb0-4475-adf5-92ab9a0147fe"
|
|
)
|
|
|
|
// Post an reply to turtle to ZORK from remote account.
|
|
// Turtle blocks the remote account but is only tangentially
|
|
// related to this POST request. The response will indicate
|
|
// accepted but the post won't actually be processed.
|
|
suite.inboxPost(
|
|
activity.Activity,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusAccepted,
|
|
`{"status":"Accepted"}`,
|
|
suite.signatureCheck,
|
|
)
|
|
|
|
_, err := suite.state.DB.GetStatusByURI(context.Background(), statusURI)
|
|
suite.ErrorIs(err, db.ErrNoEntries)
|
|
}
|
|
|
|
func (suite *InboxPostTestSuite) TestPostUnauthorized() {
|
|
var (
|
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
|
targetAccount = suite.testAccounts["local_account_1"]
|
|
)
|
|
|
|
// Post an empty create.
|
|
create := streams.NewActivityStreamsCreate()
|
|
|
|
suite.inboxPost(
|
|
create,
|
|
requestingAccount,
|
|
targetAccount,
|
|
http.StatusUnauthorized,
|
|
`{"error":"Unauthorized: http request wasn't signed or http signature was invalid: (verifier)"}`,
|
|
// Omit signature check middleware.
|
|
)
|
|
}
|
|
|
|
func TestInboxPostTestSuite(t *testing.T) {
|
|
suite.Run(t, &InboxPostTestSuite{})
|
|
}
|