gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

inboxpost_test.go (19562B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package users_test
     19 
     20 import (
     21 	"bytes"
     22 	"context"
     23 	"encoding/json"
     24 	"errors"
     25 	"fmt"
     26 	"io"
     27 	"net/http"
     28 	"net/http/httptest"
     29 	"testing"
     30 	"time"
     31 
     32 	"github.com/gin-gonic/gin"
     33 	"github.com/stretchr/testify/suite"
     34 	"github.com/superseriousbusiness/activity/pub"
     35 	"github.com/superseriousbusiness/activity/streams"
     36 	"github.com/superseriousbusiness/activity/streams/vocab"
     37 	"github.com/superseriousbusiness/gotosocial/internal/ap"
     38 	"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
     39 	"github.com/superseriousbusiness/gotosocial/internal/db"
     40 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     41 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     42 	"github.com/superseriousbusiness/gotosocial/internal/id"
     43 	"github.com/superseriousbusiness/gotosocial/testrig"
     44 )
     45 
     46 type InboxPostTestSuite struct {
     47 	UserStandardTestSuite
     48 }
     49 
     50 func (suite *InboxPostTestSuite) inboxPost(
     51 	activity pub.Activity,
     52 	requestingAccount *gtsmodel.Account,
     53 	targetAccount *gtsmodel.Account,
     54 	expectedHTTPStatus int,
     55 	expectedBody string,
     56 	middlewares ...func(*gin.Context),
     57 ) {
     58 	var (
     59 		recorder = httptest.NewRecorder()
     60 		ctx, _   = testrig.CreateGinTestContext(recorder, nil)
     61 	)
     62 
     63 	// Prepare the requst body bytes.
     64 	bodyI, err := ap.Serialize(activity)
     65 	if err != nil {
     66 		suite.FailNow(err.Error())
     67 	}
     68 
     69 	b, err := json.MarshalIndent(bodyI, "", "  ")
     70 	if err != nil {
     71 		suite.FailNow(err.Error())
     72 	}
     73 	suite.T().Logf("prepared POST body:\n%s", string(b))
     74 
     75 	// Prepare signature headers for this Activity.
     76 	signature, digestHeader, dateHeader := testrig.GetSignatureForActivity(
     77 		activity,
     78 		requestingAccount.PublicKeyURI,
     79 		requestingAccount.PrivateKey,
     80 		testrig.URLMustParse(targetAccount.InboxURI),
     81 	)
     82 
     83 	// Put the request together.
     84 	ctx.AddParam(users.UsernameKey, targetAccount.Username)
     85 	ctx.Request = httptest.NewRequest(http.MethodPost, targetAccount.InboxURI, bytes.NewReader(b))
     86 	ctx.Request.Header.Set("Signature", signature)
     87 	ctx.Request.Header.Set("Date", dateHeader)
     88 	ctx.Request.Header.Set("Digest", digestHeader)
     89 	ctx.Request.Header.Set("Content-Type", "application/activity+json")
     90 
     91 	// Pass the context through provided middlewares.
     92 	for _, middleware := range middlewares {
     93 		middleware(ctx)
     94 	}
     95 
     96 	// Trigger the function being tested.
     97 	suite.userModule.InboxPOSTHandler(ctx)
     98 
     99 	// Read the result.
    100 	result := recorder.Result()
    101 	defer result.Body.Close()
    102 
    103 	b, err = io.ReadAll(result.Body)
    104 	if err != nil {
    105 		suite.FailNow(err.Error())
    106 	}
    107 
    108 	errs := gtserror.MultiError{}
    109 
    110 	// Check expected code + body.
    111 	if resultCode := recorder.Code; expectedHTTPStatus != resultCode {
    112 		errs = append(errs, fmt.Sprintf("expected %d got %d", expectedHTTPStatus, resultCode))
    113 	}
    114 
    115 	// If we got an expected body, return early.
    116 	if expectedBody != "" && string(b) != expectedBody {
    117 		errs = append(errs, fmt.Sprintf("expected %s got %s", expectedBody, string(b)))
    118 	}
    119 
    120 	if err := errs.Combine(); err != nil {
    121 		suite.FailNow("", "%v (body %s)", err, string(b))
    122 	}
    123 }
    124 
    125 func (suite *InboxPostTestSuite) newBlock(blockID string, blockingAccount *gtsmodel.Account, blockedAccount *gtsmodel.Account) vocab.ActivityStreamsBlock {
    126 	block := streams.NewActivityStreamsBlock()
    127 
    128 	// set the actor property to the block-ing account's URI
    129 	actorProp := streams.NewActivityStreamsActorProperty()
    130 	actorIRI := testrig.URLMustParse(blockingAccount.URI)
    131 	actorProp.AppendIRI(actorIRI)
    132 	block.SetActivityStreamsActor(actorProp)
    133 
    134 	// set the ID property to the blocks's URI
    135 	idProp := streams.NewJSONLDIdProperty()
    136 	idProp.Set(testrig.URLMustParse(blockID))
    137 	block.SetJSONLDId(idProp)
    138 
    139 	// set the object property to the target account's URI
    140 	objectProp := streams.NewActivityStreamsObjectProperty()
    141 	targetIRI := testrig.URLMustParse(blockedAccount.URI)
    142 	objectProp.AppendIRI(targetIRI)
    143 	block.SetActivityStreamsObject(objectProp)
    144 
    145 	// set the TO property to the target account's IRI
    146 	toProp := streams.NewActivityStreamsToProperty()
    147 	toIRI := testrig.URLMustParse(blockedAccount.URI)
    148 	toProp.AppendIRI(toIRI)
    149 	block.SetActivityStreamsTo(toProp)
    150 
    151 	return block
    152 }
    153 
    154 func (suite *InboxPostTestSuite) newUndo(
    155 	originalActivity pub.Activity,
    156 	objectF func() vocab.ActivityStreamsObjectProperty,
    157 	to string,
    158 	undoIRI string,
    159 ) vocab.ActivityStreamsUndo {
    160 	undo := streams.NewActivityStreamsUndo()
    161 
    162 	// Set the appropriate actor.
    163 	undo.SetActivityStreamsActor(originalActivity.GetActivityStreamsActor())
    164 
    165 	// Set the original activity uri as the 'object' property.
    166 	undo.SetActivityStreamsObject(objectF())
    167 
    168 	// Set the To of the undo as the target of the activity.
    169 	undoTo := streams.NewActivityStreamsToProperty()
    170 	undoTo.AppendIRI(testrig.URLMustParse(to))
    171 	undo.SetActivityStreamsTo(undoTo)
    172 
    173 	// Set the ID property to the undo's URI.
    174 	undoID := streams.NewJSONLDIdProperty()
    175 	undoID.SetIRI(testrig.URLMustParse(undoIRI))
    176 	undo.SetJSONLDId(undoID)
    177 
    178 	return undo
    179 }
    180 
    181 func (suite *InboxPostTestSuite) newUpdatePerson(person vocab.ActivityStreamsPerson, cc string, updateIRI string) vocab.ActivityStreamsUpdate {
    182 	// create an update
    183 	update := streams.NewActivityStreamsUpdate()
    184 
    185 	// set the appropriate actor on it
    186 	updateActor := streams.NewActivityStreamsActorProperty()
    187 	updateActor.AppendIRI(person.GetJSONLDId().Get())
    188 	update.SetActivityStreamsActor(updateActor)
    189 
    190 	// Set the person as the 'object' property.
    191 	updateObject := streams.NewActivityStreamsObjectProperty()
    192 	updateObject.AppendActivityStreamsPerson(person)
    193 	update.SetActivityStreamsObject(updateObject)
    194 
    195 	// Set the To of the update as public
    196 	updateTo := streams.NewActivityStreamsToProperty()
    197 	updateTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
    198 	update.SetActivityStreamsTo(updateTo)
    199 
    200 	// set the cc of the update to the receivingAccount
    201 	updateCC := streams.NewActivityStreamsCcProperty()
    202 	updateCC.AppendIRI(testrig.URLMustParse(cc))
    203 	update.SetActivityStreamsCc(updateCC)
    204 
    205 	// set some random-ass ID for the activity
    206 	updateID := streams.NewJSONLDIdProperty()
    207 	updateID.SetIRI(testrig.URLMustParse(updateIRI))
    208 	update.SetJSONLDId(updateID)
    209 
    210 	return update
    211 }
    212 
    213 func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, deleteIRI string) vocab.ActivityStreamsDelete {
    214 	// create a delete
    215 	delete := streams.NewActivityStreamsDelete()
    216 
    217 	// set the appropriate actor on it
    218 	deleteActor := streams.NewActivityStreamsActorProperty()
    219 	deleteActor.AppendIRI(testrig.URLMustParse(actorIRI))
    220 	delete.SetActivityStreamsActor(deleteActor)
    221 
    222 	// Set 'object' property.
    223 	deleteObject := streams.NewActivityStreamsObjectProperty()
    224 	deleteObject.AppendIRI(testrig.URLMustParse(objectIRI))
    225 	delete.SetActivityStreamsObject(deleteObject)
    226 
    227 	// Set the To of the delete as public
    228 	deleteTo := streams.NewActivityStreamsToProperty()
    229 	deleteTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
    230 	delete.SetActivityStreamsTo(deleteTo)
    231 
    232 	// set some random-ass ID for the activity
    233 	deleteID := streams.NewJSONLDIdProperty()
    234 	deleteID.SetIRI(testrig.URLMustParse(deleteIRI))
    235 	delete.SetJSONLDId(deleteID)
    236 
    237 	return delete
    238 }
    239 
    240 // TestPostBlock verifies that a remote account can block one of
    241 // our instance users.
    242 func (suite *InboxPostTestSuite) TestPostBlock() {
    243 	var (
    244 		requestingAccount = suite.testAccounts["remote_account_1"]
    245 		targetAccount     = suite.testAccounts["local_account_1"]
    246 		activityID        = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
    247 	)
    248 
    249 	block := suite.newBlock(activityID, requestingAccount, targetAccount)
    250 
    251 	// Block.
    252 	suite.inboxPost(
    253 		block,
    254 		requestingAccount,
    255 		targetAccount,
    256 		http.StatusAccepted,
    257 		`{"status":"Accepted"}`,
    258 		suite.signatureCheck,
    259 	)
    260 
    261 	// Ensure block created in the database.
    262 	var (
    263 		dbBlock *gtsmodel.Block
    264 		err     error
    265 	)
    266 
    267 	if !testrig.WaitFor(func() bool {
    268 		dbBlock, err = suite.db.GetBlock(context.Background(), requestingAccount.ID, targetAccount.ID)
    269 		return err == nil && dbBlock != nil
    270 	}) {
    271 		suite.FailNow("timed out waiting for block to be created")
    272 	}
    273 }
    274 
    275 // TestPostUnblock verifies that a remote account who blocks
    276 // one of our instance users should be able to undo that block.
    277 func (suite *InboxPostTestSuite) TestPostUnblock() {
    278 	var (
    279 		ctx               = context.Background()
    280 		requestingAccount = suite.testAccounts["remote_account_1"]
    281 		targetAccount     = suite.testAccounts["local_account_1"]
    282 		blockID           = "http://fossbros-anonymous.io/blocks/01H1462TPRTVG2RTQCTSQ7N6Q0"
    283 		undoID            = "http://fossbros-anonymous.io/some-activity/01H1463RDQNG5H98F29BXYHW6B"
    284 	)
    285 
    286 	// Put a block in the database so we have something to undo.
    287 	block := &gtsmodel.Block{
    288 		ID:              id.NewULID(),
    289 		URI:             blockID,
    290 		AccountID:       requestingAccount.ID,
    291 		TargetAccountID: targetAccount.ID,
    292 	}
    293 	if err := suite.db.PutBlock(ctx, block); err != nil {
    294 		suite.FailNow(err.Error())
    295 	}
    296 
    297 	// Create the undo from the AS model block.
    298 	asBlock, err := suite.tc.BlockToAS(ctx, block)
    299 	if err != nil {
    300 		suite.FailNow(err.Error())
    301 	}
    302 
    303 	undo := suite.newUndo(asBlock, func() vocab.ActivityStreamsObjectProperty {
    304 		// Append the whole block as Object.
    305 		op := streams.NewActivityStreamsObjectProperty()
    306 		op.AppendActivityStreamsBlock(asBlock)
    307 		return op
    308 	}, targetAccount.URI, undoID)
    309 
    310 	// Undo.
    311 	suite.inboxPost(
    312 		undo,
    313 		requestingAccount,
    314 		targetAccount,
    315 		http.StatusAccepted,
    316 		`{"status":"Accepted"}`,
    317 		suite.signatureCheck,
    318 	)
    319 
    320 	// Ensure block removed from the database.
    321 	if !testrig.WaitFor(func() bool {
    322 		_, err := suite.db.GetBlockByID(ctx, block.ID)
    323 		return errors.Is(err, db.ErrNoEntries)
    324 	}) {
    325 		suite.FailNow("timed out waiting for block to be removed")
    326 	}
    327 }
    328 
    329 func (suite *InboxPostTestSuite) TestPostUpdate() {
    330 	var (
    331 		requestingAccount  = new(gtsmodel.Account)
    332 		targetAccount      = suite.testAccounts["local_account_1"]
    333 		activityID         = "http://fossbros-anonymous.io/72cc96a3-f742-4daf-b9f5-3407667260c5"
    334 		updatedDisplayName = "updated display name!"
    335 	)
    336 
    337 	// Copy the requesting account, since we'll be changing it.
    338 	*requestingAccount = *suite.testAccounts["remote_account_1"]
    339 
    340 	// Update the account's display name.
    341 	requestingAccount.DisplayName = updatedDisplayName
    342 
    343 	// Add an emoji to the account; because we're serializing this
    344 	// remote account from our own instance, we need to cheat a bit
    345 	// to get the emoji to work properly, just for this test.
    346 	testEmoji := &gtsmodel.Emoji{}
    347 	*testEmoji = *testrig.NewTestEmojis()["yell"]
    348 	testEmoji.ImageURL = testEmoji.ImageRemoteURL // <- here's the cheat
    349 	requestingAccount.Emojis = []*gtsmodel.Emoji{testEmoji}
    350 
    351 	// Create an update from the account.
    352 	asAccount, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
    353 	if err != nil {
    354 		suite.FailNow(err.Error())
    355 	}
    356 	update := suite.newUpdatePerson(asAccount, targetAccount.URI, activityID)
    357 
    358 	// Update.
    359 	suite.inboxPost(
    360 		update,
    361 		requestingAccount,
    362 		targetAccount,
    363 		http.StatusAccepted,
    364 		`{"status":"Accepted"}`,
    365 		suite.signatureCheck,
    366 	)
    367 
    368 	// account should be changed in the database now
    369 	var dbUpdatedAccount *gtsmodel.Account
    370 
    371 	if !testrig.WaitFor(func() bool {
    372 		// displayName should be updated
    373 		dbUpdatedAccount, _ = suite.db.GetAccountByID(context.Background(), requestingAccount.ID)
    374 		return dbUpdatedAccount.DisplayName == updatedDisplayName
    375 	}) {
    376 		suite.FailNow("timed out waiting for account update")
    377 	}
    378 
    379 	// emojis should be updated
    380 	suite.Contains(dbUpdatedAccount.EmojiIDs, testEmoji.ID)
    381 
    382 	// account should be freshly fetched
    383 	suite.WithinDuration(time.Now(), dbUpdatedAccount.FetchedAt, 10*time.Second)
    384 
    385 	// everything else should be the same as it was before
    386 	suite.EqualValues(requestingAccount.Username, dbUpdatedAccount.Username)
    387 	suite.EqualValues(requestingAccount.Domain, dbUpdatedAccount.Domain)
    388 	suite.EqualValues(requestingAccount.AvatarMediaAttachmentID, dbUpdatedAccount.AvatarMediaAttachmentID)
    389 	suite.EqualValues(requestingAccount.AvatarMediaAttachment, dbUpdatedAccount.AvatarMediaAttachment)
    390 	suite.EqualValues(requestingAccount.AvatarRemoteURL, dbUpdatedAccount.AvatarRemoteURL)
    391 	suite.EqualValues(requestingAccount.HeaderMediaAttachmentID, dbUpdatedAccount.HeaderMediaAttachmentID)
    392 	suite.EqualValues(requestingAccount.HeaderMediaAttachment, dbUpdatedAccount.HeaderMediaAttachment)
    393 	suite.EqualValues(requestingAccount.HeaderRemoteURL, dbUpdatedAccount.HeaderRemoteURL)
    394 	suite.EqualValues(requestingAccount.Note, dbUpdatedAccount.Note)
    395 	suite.EqualValues(requestingAccount.Memorial, dbUpdatedAccount.Memorial)
    396 	suite.EqualValues(requestingAccount.AlsoKnownAs, dbUpdatedAccount.AlsoKnownAs)
    397 	suite.EqualValues(requestingAccount.MovedToAccountID, dbUpdatedAccount.MovedToAccountID)
    398 	suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot)
    399 	suite.EqualValues(requestingAccount.Reason, dbUpdatedAccount.Reason)
    400 	suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked)
    401 	suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable)
    402 	suite.EqualValues(requestingAccount.Privacy, dbUpdatedAccount.Privacy)
    403 	suite.EqualValues(requestingAccount.Sensitive, dbUpdatedAccount.Sensitive)
    404 	suite.EqualValues(requestingAccount.Language, dbUpdatedAccount.Language)
    405 	suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI)
    406 	suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL)
    407 	suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI)
    408 	suite.EqualValues(requestingAccount.OutboxURI, dbUpdatedAccount.OutboxURI)
    409 	suite.EqualValues(requestingAccount.FollowingURI, dbUpdatedAccount.FollowingURI)
    410 	suite.EqualValues(requestingAccount.FollowersURI, dbUpdatedAccount.FollowersURI)
    411 	suite.EqualValues(requestingAccount.FeaturedCollectionURI, dbUpdatedAccount.FeaturedCollectionURI)
    412 	suite.EqualValues(requestingAccount.ActorType, dbUpdatedAccount.ActorType)
    413 	suite.EqualValues(requestingAccount.PublicKey, dbUpdatedAccount.PublicKey)
    414 	suite.EqualValues(requestingAccount.PublicKeyURI, dbUpdatedAccount.PublicKeyURI)
    415 	suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt)
    416 	suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt)
    417 	suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt)
    418 	suite.EqualValues(requestingAccount.HideCollections, dbUpdatedAccount.HideCollections)
    419 	suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin)
    420 }
    421 
    422 func (suite *InboxPostTestSuite) TestPostDelete() {
    423 	var (
    424 		ctx               = context.Background()
    425 		requestingAccount = suite.testAccounts["remote_account_1"]
    426 		targetAccount     = suite.testAccounts["local_account_1"]
    427 		activityID        = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
    428 	)
    429 
    430 	delete := suite.newDelete(requestingAccount.URI, requestingAccount.URI, activityID)
    431 
    432 	// Delete.
    433 	suite.inboxPost(
    434 		delete,
    435 		requestingAccount,
    436 		targetAccount,
    437 		http.StatusAccepted,
    438 		`{"status":"Accepted"}`,
    439 		suite.signatureCheck,
    440 	)
    441 
    442 	if !testrig.WaitFor(func() bool {
    443 		// local account 2 blocked foss_satan, that block should be gone now
    444 		testBlock := suite.testBlocks["local_account_2_block_remote_account_1"]
    445 		_, err := suite.db.GetBlockByID(ctx, testBlock.ID)
    446 		return suite.ErrorIs(err, db.ErrNoEntries)
    447 	}) {
    448 		suite.FailNow("timed out waiting for block to be removed")
    449 	}
    450 
    451 	if !testrig.WaitFor(func() bool {
    452 		// no statuses from foss satan should be left in the database
    453 		dbStatuses, err := suite.db.GetAccountStatuses(ctx, requestingAccount.ID, 0, false, false, "", "", false, false)
    454 		return len(dbStatuses) == 0 && errors.Is(err, db.ErrNoEntries)
    455 	}) {
    456 		suite.FailNow("timed out waiting for statuses to be removed")
    457 	}
    458 
    459 	// Account should be stubbified.
    460 	dbAccount, err := suite.db.GetAccountByID(ctx, requestingAccount.ID)
    461 	suite.NoError(err)
    462 	suite.Empty(dbAccount.Note)
    463 	suite.Empty(dbAccount.DisplayName)
    464 	suite.Empty(dbAccount.AvatarMediaAttachmentID)
    465 	suite.Empty(dbAccount.AvatarRemoteURL)
    466 	suite.Empty(dbAccount.HeaderMediaAttachmentID)
    467 	suite.Empty(dbAccount.HeaderRemoteURL)
    468 	suite.Empty(dbAccount.Reason)
    469 	suite.Empty(dbAccount.Fields)
    470 	suite.True(*dbAccount.HideCollections)
    471 	suite.False(*dbAccount.Discoverable)
    472 	suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second)
    473 	suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin)
    474 }
    475 
    476 func (suite *InboxPostTestSuite) TestPostEmptyCreate() {
    477 	var (
    478 		requestingAccount = suite.testAccounts["remote_account_1"]
    479 		targetAccount     = suite.testAccounts["local_account_1"]
    480 	)
    481 
    482 	// Post a create with no object.
    483 	create := streams.NewActivityStreamsCreate()
    484 
    485 	suite.inboxPost(
    486 		create,
    487 		requestingAccount,
    488 		targetAccount,
    489 		http.StatusBadRequest,
    490 		`{"error":"Bad Request: incoming Activity Create did not have required id property set"}`,
    491 		suite.signatureCheck,
    492 	)
    493 }
    494 
    495 func (suite *InboxPostTestSuite) TestPostFromBlockedAccount() {
    496 	var (
    497 		requestingAccount = suite.testAccounts["remote_account_1"]
    498 		targetAccount     = suite.testAccounts["local_account_2"]
    499 		activityID        = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
    500 	)
    501 
    502 	person, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
    503 	if err != nil {
    504 		suite.FailNow(err.Error())
    505 	}
    506 
    507 	// Post an update from foss satan to turtle, who blocks him.
    508 	update := suite.newUpdatePerson(person, targetAccount.URI, activityID)
    509 
    510 	suite.inboxPost(
    511 		update,
    512 		requestingAccount,
    513 		targetAccount,
    514 		http.StatusForbidden,
    515 		`{"error":"Forbidden"}`,
    516 		suite.signatureCheck,
    517 	)
    518 }
    519 
    520 func (suite *InboxPostTestSuite) TestPostFromBlockedAccountToOtherAccount() {
    521 	var (
    522 		requestingAccount = suite.testAccounts["remote_account_1"]
    523 		targetAccount     = suite.testAccounts["local_account_1"]
    524 		activity          = suite.testActivities["reply_to_turtle_for_turtle"]
    525 		statusURI         = "http://fossbros-anonymous.io/users/foss_satan/statuses/2f1195a6-5cb0-4475-adf5-92ab9a0147fe"
    526 	)
    527 
    528 	// Post an reply to turtle to ZORK from remote account.
    529 	// Turtle blocks the remote account but is only tangentially
    530 	// related to this POST request. The response will indicate
    531 	// accepted but the post won't actually be processed.
    532 	suite.inboxPost(
    533 		activity.Activity,
    534 		requestingAccount,
    535 		targetAccount,
    536 		http.StatusAccepted,
    537 		`{"status":"Accepted"}`,
    538 		suite.signatureCheck,
    539 	)
    540 
    541 	_, err := suite.state.DB.GetStatusByURI(context.Background(), statusURI)
    542 	suite.ErrorIs(err, db.ErrNoEntries)
    543 }
    544 
    545 func (suite *InboxPostTestSuite) TestPostUnauthorized() {
    546 	var (
    547 		requestingAccount = suite.testAccounts["remote_account_1"]
    548 		targetAccount     = suite.testAccounts["local_account_1"]
    549 	)
    550 
    551 	// Post an empty create.
    552 	create := streams.NewActivityStreamsCreate()
    553 
    554 	suite.inboxPost(
    555 		create,
    556 		requestingAccount,
    557 		targetAccount,
    558 		http.StatusUnauthorized,
    559 		`{"error":"Unauthorized"}`,
    560 		// Omit signature check middleware.
    561 	)
    562 }
    563 
    564 func TestInboxPostTestSuite(t *testing.T) {
    565 	suite.Run(t, &InboxPostTestSuite{})
    566 }