You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
685 lines
24 KiB
685 lines
24 KiB
// 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 workers_test |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"fmt" |
|
"io" |
|
"testing" |
|
"time" |
|
|
|
"github.com/stretchr/testify/suite" |
|
"github.com/superseriousbusiness/gotosocial/internal/ap" |
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" |
|
"github.com/superseriousbusiness/gotosocial/internal/db" |
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" |
|
"github.com/superseriousbusiness/gotosocial/internal/messages" |
|
"github.com/superseriousbusiness/gotosocial/internal/stream" |
|
"github.com/superseriousbusiness/gotosocial/internal/util" |
|
"github.com/superseriousbusiness/gotosocial/testrig" |
|
) |
|
|
|
type FromFediAPITestSuite struct { |
|
WorkersTestSuite |
|
} |
|
|
|
// remote_account_1 boosts the first status of local_account_1 |
|
func (suite *FromFediAPITestSuite) TestProcessFederationAnnounce() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
boostedStatus := >smodel.Status{} |
|
*boostedStatus = *suite.testStatuses["local_account_1_status_1"] |
|
|
|
boostingAccount := >smodel.Account{} |
|
*boostingAccount = *suite.testAccounts["remote_account_1"] |
|
|
|
announceStatus := >smodel.Status{} |
|
announceStatus.URI = "https://example.org/some-announce-uri" |
|
announceStatus.BoostOfURI = boostedStatus.URI |
|
announceStatus.CreatedAt = time.Now() |
|
announceStatus.UpdatedAt = time.Now() |
|
announceStatus.AccountID = boostingAccount.ID |
|
announceStatus.AccountURI = boostingAccount.URI |
|
announceStatus.Account = boostingAccount |
|
announceStatus.Visibility = boostedStatus.Visibility |
|
|
|
err := testStructs.Processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{ |
|
APObjectType: ap.ActivityAnnounce, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: announceStatus, |
|
Receiving: suite.testAccounts["local_account_1"], |
|
Requesting: boostingAccount, |
|
}) |
|
if err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Wait for side effects to trigger: |
|
// 1. status should have an ID, and be in the database |
|
if !testrig.WaitFor(func() bool { |
|
if announceStatus.ID == "" { |
|
return false |
|
} |
|
|
|
_, err = testStructs.State.DB.GetStatusByID( |
|
context.Background(), |
|
announceStatus.ID, |
|
) |
|
return err == nil |
|
}) { |
|
suite.FailNow("timed out waiting for announce to be in the database") |
|
} |
|
|
|
// 2. a notification should exist for the announce |
|
where := []db.Where{ |
|
{ |
|
Key: "status_id", |
|
Value: announceStatus.ID, |
|
}, |
|
} |
|
notif := >smodel.Notification{} |
|
err = testStructs.State.DB.GetWhere(context.Background(), where, notif) |
|
suite.NoError(err) |
|
suite.Equal(gtsmodel.NotificationReblog, notif.NotificationType) |
|
suite.Equal(boostedStatus.AccountID, notif.TargetAccountID) |
|
suite.Equal(announceStatus.AccountID, notif.OriginAccountID) |
|
suite.Equal(announceStatus.ID, notif.StatusID) |
|
suite.False(*notif.Read) |
|
} |
|
|
|
func (suite *FromFediAPITestSuite) TestProcessReplyMention() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
repliedAccount := >smodel.Account{} |
|
*repliedAccount = *suite.testAccounts["local_account_1"] |
|
|
|
repliedStatus := >smodel.Status{} |
|
*repliedStatus = *suite.testStatuses["local_account_1_status_1"] |
|
|
|
replyingAccount := >smodel.Account{} |
|
*replyingAccount = *suite.testAccounts["remote_account_1"] |
|
|
|
// Set the replyingAccount's last fetched_at |
|
// date to something recent so no refresh is attempted, |
|
// and ensure it isn't a suspended account. |
|
replyingAccount.FetchedAt = time.Now() |
|
replyingAccount.SuspendedAt = time.Time{} |
|
replyingAccount.SuspensionOrigin = "" |
|
err := testStructs.State.DB.UpdateAccount(context.Background(), |
|
replyingAccount, |
|
"fetched_at", |
|
"suspended_at", |
|
"suspension_origin", |
|
) |
|
suite.NoError(err) |
|
|
|
// Get replying statusable to use from remote test statuses. |
|
const replyingURI = "http://fossbros-anonymous.io/users/foss_satan/statuses/106221634728637552" |
|
replyingStatusable := testrig.NewTestFediStatuses()[replyingURI] |
|
ap.AppendInReplyTo(replyingStatusable, testrig.URLMustParse(repliedStatus.URI)) |
|
|
|
// Open a websocket stream to later test the streamed status reply. |
|
wssStream, errWithCode := testStructs.Processor.Stream().Open(context.Background(), repliedAccount, stream.TimelineHome) |
|
suite.NoError(errWithCode) |
|
|
|
// Send the replied status off to the fedi worker to be further processed. |
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{ |
|
APObjectType: ap.ObjectNote, |
|
APActivityType: ap.ActivityCreate, |
|
APObject: replyingStatusable, |
|
Receiving: repliedAccount, |
|
Requesting: replyingAccount, |
|
}) |
|
if err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Wait for side effects to trigger: |
|
// 1. status should be in the database |
|
var replyingStatus *gtsmodel.Status |
|
if !testrig.WaitFor(func() bool { |
|
replyingStatus, err = testStructs.State.DB.GetStatusByURI(context.Background(), replyingURI) |
|
return err == nil |
|
}) { |
|
suite.FailNow("timed out waiting for replying status to be in the database") |
|
} |
|
|
|
// 2. a notification should exist for the mention |
|
var notif gtsmodel.Notification |
|
err = testStructs.State.DB.GetWhere(context.Background(), []db.Where{ |
|
{Key: "status_id", Value: replyingStatus.ID}, |
|
}, ¬if) |
|
suite.NoError(err) |
|
suite.Equal(gtsmodel.NotificationMention, notif.NotificationType) |
|
suite.Equal(replyingStatus.InReplyToAccountID, notif.TargetAccountID) |
|
suite.Equal(replyingStatus.AccountID, notif.OriginAccountID) |
|
suite.Equal(replyingStatus.ID, notif.StatusID) |
|
suite.False(*notif.Read) |
|
|
|
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) |
|
msg, ok := wssStream.Recv(ctx) |
|
suite.True(ok) |
|
|
|
suite.Equal(stream.EventTypeNotification, msg.Event) |
|
suite.NotEmpty(msg.Payload) |
|
suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) |
|
notifStreamed := &apimodel.Notification{} |
|
err = json.Unmarshal([]byte(msg.Payload), notifStreamed) |
|
suite.NoError(err) |
|
suite.Equal("mention", notifStreamed.Type) |
|
suite.Equal(replyingAccount.ID, notifStreamed.Account.ID) |
|
} |
|
|
|
func (suite *FromFediAPITestSuite) TestProcessFave() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
favedAccount := suite.testAccounts["local_account_1"] |
|
favedStatus := suite.testStatuses["local_account_1_status_1"] |
|
favingAccount := suite.testAccounts["remote_account_1"] |
|
|
|
wssStream, errWithCode := testStructs.Processor.Stream().Open(context.Background(), favedAccount, stream.TimelineNotifications) |
|
suite.NoError(errWithCode) |
|
|
|
fave := >smodel.StatusFave{ |
|
ID: "01FGKJPXFTVQPG9YSSZ95ADS7Q", |
|
CreatedAt: time.Now(), |
|
UpdatedAt: time.Now(), |
|
AccountID: favingAccount.ID, |
|
Account: favingAccount, |
|
TargetAccountID: favedAccount.ID, |
|
TargetAccount: favedAccount, |
|
StatusID: favedStatus.ID, |
|
Status: favedStatus, |
|
URI: favingAccount.URI + "/faves/aaaaaaaaaaaa", |
|
} |
|
|
|
err := testStructs.State.DB.Put(context.Background(), fave) |
|
suite.NoError(err) |
|
|
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{ |
|
APObjectType: ap.ActivityLike, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: fave, |
|
Receiving: favedAccount, |
|
Requesting: favingAccount, |
|
}) |
|
suite.NoError(err) |
|
|
|
// side effects should be triggered |
|
// 1. a notification should exist for the fave |
|
where := []db.Where{ |
|
{ |
|
Key: "status_id", |
|
Value: favedStatus.ID, |
|
}, |
|
{ |
|
Key: "origin_account_id", |
|
Value: favingAccount.ID, |
|
}, |
|
} |
|
|
|
notif := >smodel.Notification{} |
|
err = testStructs.State.DB.GetWhere(context.Background(), where, notif) |
|
suite.NoError(err) |
|
suite.Equal(gtsmodel.NotificationFave, notif.NotificationType) |
|
suite.Equal(fave.TargetAccountID, notif.TargetAccountID) |
|
suite.Equal(fave.AccountID, notif.OriginAccountID) |
|
suite.Equal(fave.StatusID, notif.StatusID) |
|
suite.False(*notif.Read) |
|
|
|
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) |
|
msg, ok := wssStream.Recv(ctx) |
|
suite.True(ok) |
|
|
|
suite.Equal(stream.EventTypeNotification, msg.Event) |
|
suite.NotEmpty(msg.Payload) |
|
suite.EqualValues([]string{stream.TimelineNotifications}, msg.Stream) |
|
} |
|
|
|
// TestProcessFaveWithDifferentReceivingAccount ensures that when an account receives a fave that's for |
|
// another account in their AP inbox, a notification isn't streamed to the receiving account. |
|
// |
|
// This tests for an issue we were seeing where Misskey sends out faves to inboxes of people that don't own |
|
// the fave, but just follow the actor who received the fave. |
|
func (suite *FromFediAPITestSuite) TestProcessFaveWithDifferentReceivingAccount() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
receivingAccount := suite.testAccounts["local_account_2"] |
|
favedAccount := suite.testAccounts["local_account_1"] |
|
favedStatus := suite.testStatuses["local_account_1_status_1"] |
|
favingAccount := suite.testAccounts["remote_account_1"] |
|
|
|
wssStream, errWithCode := testStructs.Processor.Stream().Open(context.Background(), receivingAccount, stream.TimelineHome) |
|
suite.NoError(errWithCode) |
|
|
|
fave := >smodel.StatusFave{ |
|
ID: "01FGKJPXFTVQPG9YSSZ95ADS7Q", |
|
CreatedAt: time.Now(), |
|
UpdatedAt: time.Now(), |
|
AccountID: favingAccount.ID, |
|
Account: favingAccount, |
|
TargetAccountID: favedAccount.ID, |
|
TargetAccount: favedAccount, |
|
StatusID: favedStatus.ID, |
|
Status: favedStatus, |
|
URI: favingAccount.URI + "/faves/aaaaaaaaaaaa", |
|
} |
|
|
|
err := testStructs.State.DB.Put(context.Background(), fave) |
|
suite.NoError(err) |
|
|
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{ |
|
APObjectType: ap.ActivityLike, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: fave, |
|
Receiving: receivingAccount, |
|
Requesting: favingAccount, |
|
}) |
|
suite.NoError(err) |
|
|
|
// side effects should be triggered |
|
// 1. a notification should exist for the fave |
|
where := []db.Where{ |
|
{ |
|
Key: "status_id", |
|
Value: favedStatus.ID, |
|
}, |
|
{ |
|
Key: "origin_account_id", |
|
Value: favingAccount.ID, |
|
}, |
|
} |
|
|
|
notif := >smodel.Notification{} |
|
err = testStructs.State.DB.GetWhere(context.Background(), where, notif) |
|
suite.NoError(err) |
|
suite.Equal(gtsmodel.NotificationFave, notif.NotificationType) |
|
suite.Equal(fave.TargetAccountID, notif.TargetAccountID) |
|
suite.Equal(fave.AccountID, notif.OriginAccountID) |
|
suite.Equal(fave.StatusID, notif.StatusID) |
|
suite.False(*notif.Read) |
|
|
|
// 2. no notification should be streamed to the account that received the fave message, because they weren't the target |
|
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) |
|
_, ok := wssStream.Recv(ctx) |
|
suite.False(ok) |
|
} |
|
|
|
func (suite *FromFediAPITestSuite) TestProcessAccountDelete() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
ctx := context.Background() |
|
|
|
deletedAccount := >smodel.Account{} |
|
*deletedAccount = *suite.testAccounts["remote_account_1"] |
|
|
|
receivingAccount := >smodel.Account{} |
|
*receivingAccount = *suite.testAccounts["local_account_1"] |
|
|
|
// before doing the delete.... |
|
// make local_account_1 and remote_account_1 into mufos |
|
zorkFollowSatan := >smodel.Follow{ |
|
ID: "01FGRY72ASHBSET64353DPHK9T", |
|
CreatedAt: time.Now().Add(-1 * time.Hour), |
|
UpdatedAt: time.Now().Add(-1 * time.Hour), |
|
AccountID: deletedAccount.ID, |
|
TargetAccountID: receivingAccount.ID, |
|
ShowReblogs: util.Ptr(true), |
|
URI: fmt.Sprintf("%s/follows/01FGRY72ASHBSET64353DPHK9T", deletedAccount.URI), |
|
Notify: util.Ptr(false), |
|
} |
|
err := testStructs.State.DB.Put(ctx, zorkFollowSatan) |
|
suite.NoError(err) |
|
|
|
satanFollowZork := >smodel.Follow{ |
|
ID: "01FGRYAVAWWPP926J175QGM0WV", |
|
CreatedAt: time.Now().Add(-1 * time.Hour), |
|
UpdatedAt: time.Now().Add(-1 * time.Hour), |
|
AccountID: receivingAccount.ID, |
|
TargetAccountID: deletedAccount.ID, |
|
ShowReblogs: util.Ptr(true), |
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", receivingAccount.URI), |
|
Notify: util.Ptr(false), |
|
} |
|
err = testStructs.State.DB.Put(ctx, satanFollowZork) |
|
suite.NoError(err) |
|
|
|
// now they are mufos! |
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{ |
|
APObjectType: ap.ActorPerson, |
|
APActivityType: ap.ActivityDelete, |
|
GTSModel: deletedAccount, |
|
Receiving: receivingAccount, |
|
Requesting: deletedAccount, |
|
}) |
|
suite.NoError(err) |
|
|
|
// local account 2 blocked foss_satan, that block should be gone now |
|
testBlock := suite.testBlocks["local_account_2_block_remote_account_1"] |
|
dbBlock := >smodel.Block{} |
|
err = testStructs.State.DB.GetByID(ctx, testBlock.ID, dbBlock) |
|
suite.ErrorIs(err, db.ErrNoEntries) |
|
|
|
// the mufos should be gone now too |
|
satanFollowsZork, err := testStructs.State.DB.IsFollowing(ctx, deletedAccount.ID, receivingAccount.ID) |
|
suite.NoError(err) |
|
suite.False(satanFollowsZork) |
|
zorkFollowsSatan, err := testStructs.State.DB.IsFollowing(ctx, receivingAccount.ID, deletedAccount.ID) |
|
suite.NoError(err) |
|
suite.False(zorkFollowsSatan) |
|
|
|
// no statuses from foss satan should be left in the database |
|
if !testrig.WaitFor(func() bool { |
|
s, err := testStructs.State.DB.GetAccountStatuses(ctx, deletedAccount.ID, 0, false, false, "", "", false, false) |
|
return s == nil && err == db.ErrNoEntries |
|
}) { |
|
suite.FailNow("timeout waiting for statuses to be deleted") |
|
} |
|
|
|
var dbAccount *gtsmodel.Account |
|
|
|
// account data should be zeroed. |
|
if !testrig.WaitFor(func() bool { |
|
dbAccount, err = testStructs.State.DB.GetAccountByID(ctx, deletedAccount.ID) |
|
return err == nil && dbAccount.DisplayName == "" |
|
}) { |
|
suite.FailNow("timeout waiting for statuses to be deleted") |
|
} |
|
|
|
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 *FromFediAPITestSuite) TestProcessFollowRequestLocked() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
ctx := context.Background() |
|
|
|
originAccount := suite.testAccounts["remote_account_1"] |
|
|
|
// target is a locked account |
|
targetAccount := suite.testAccounts["local_account_2"] |
|
|
|
wssStream, errWithCode := testStructs.Processor.Stream().Open(context.Background(), targetAccount, stream.TimelineHome) |
|
suite.NoError(errWithCode) |
|
|
|
// put the follow request in the database as though it had passed through the federating db already |
|
satanFollowRequestTurtle := >smodel.FollowRequest{ |
|
ID: "01FGRYAVAWWPP926J175QGM0WV", |
|
CreatedAt: time.Now(), |
|
UpdatedAt: time.Now(), |
|
AccountID: originAccount.ID, |
|
Account: originAccount, |
|
TargetAccountID: targetAccount.ID, |
|
TargetAccount: targetAccount, |
|
ShowReblogs: util.Ptr(true), |
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI), |
|
Notify: util.Ptr(false), |
|
} |
|
|
|
err := testStructs.State.DB.Put(ctx, satanFollowRequestTurtle) |
|
suite.NoError(err) |
|
|
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{ |
|
APObjectType: ap.ActivityFollow, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: satanFollowRequestTurtle, |
|
Receiving: targetAccount, |
|
Requesting: originAccount, |
|
}) |
|
suite.NoError(err) |
|
|
|
ctx, _ = context.WithTimeout(ctx, time.Second*5) |
|
msg, ok := wssStream.Recv(context.Background()) |
|
suite.True(ok) |
|
|
|
suite.Equal(stream.EventTypeNotification, msg.Event) |
|
suite.NotEmpty(msg.Payload) |
|
suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) |
|
notif := &apimodel.Notification{} |
|
err = json.Unmarshal([]byte(msg.Payload), notif) |
|
suite.NoError(err) |
|
suite.Equal("follow_request", notif.Type) |
|
suite.Equal(originAccount.ID, notif.Account.ID) |
|
|
|
// no messages should have been sent out, since we didn't need to federate an accept |
|
suite.Empty(testStructs.HTTPClient.SentMessages) |
|
} |
|
|
|
func (suite *FromFediAPITestSuite) TestProcessFollowRequestUnlocked() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
ctx := context.Background() |
|
|
|
originAccount := suite.testAccounts["remote_account_1"] |
|
|
|
// target is an unlocked account |
|
targetAccount := suite.testAccounts["local_account_1"] |
|
|
|
wssStream, errWithCode := testStructs.Processor.Stream().Open(context.Background(), targetAccount, stream.TimelineHome) |
|
suite.NoError(errWithCode) |
|
|
|
// put the follow request in the database as though it had passed through the federating db already |
|
satanFollowRequestTurtle := >smodel.FollowRequest{ |
|
ID: "01FGRYAVAWWPP926J175QGM0WV", |
|
CreatedAt: time.Now(), |
|
UpdatedAt: time.Now(), |
|
AccountID: originAccount.ID, |
|
Account: originAccount, |
|
TargetAccountID: targetAccount.ID, |
|
TargetAccount: targetAccount, |
|
ShowReblogs: util.Ptr(true), |
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI), |
|
Notify: util.Ptr(false), |
|
} |
|
|
|
err := testStructs.State.DB.Put(ctx, satanFollowRequestTurtle) |
|
suite.NoError(err) |
|
|
|
err = testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{ |
|
APObjectType: ap.ActivityFollow, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: satanFollowRequestTurtle, |
|
Receiving: targetAccount, |
|
Requesting: originAccount, |
|
}) |
|
suite.NoError(err) |
|
|
|
accept := &struct { |
|
Actor string `json:"actor"` |
|
ID string `json:"id"` |
|
Object struct { |
|
Actor string `json:"actor"` |
|
ID string `json:"id"` |
|
Object string `json:"object"` |
|
To string `json:"to"` |
|
Type string `json:"type"` |
|
} |
|
To string `json:"to"` |
|
Type string `json:"type"` |
|
}{} |
|
|
|
// an accept message should be sent to satan's inbox |
|
var sent []byte |
|
if !testrig.WaitFor(func() bool { |
|
delivery, ok := testStructs.State.Workers.Delivery.Queue.Pop() |
|
if !ok { |
|
return false |
|
} |
|
if !testrig.EqualRequestURIs(delivery.Request.URL, *originAccount.SharedInboxURI) { |
|
panic("differing request uris") |
|
} |
|
sent, err = io.ReadAll(delivery.Request.Body) |
|
if err != nil { |
|
panic("error reading body: " + err.Error()) |
|
} |
|
err = json.Unmarshal(sent, accept) |
|
if err != nil { |
|
panic("error unmarshaling json: " + err.Error()) |
|
} |
|
return true |
|
}) { |
|
suite.FailNow("timed out waiting for message") |
|
} |
|
|
|
suite.Equal(targetAccount.URI, accept.Actor) |
|
suite.Equal(originAccount.URI, accept.Object.Actor) |
|
suite.Equal(satanFollowRequestTurtle.URI, accept.Object.ID) |
|
suite.Equal(targetAccount.URI, accept.Object.Object) |
|
suite.Equal(targetAccount.URI, accept.Object.To) |
|
suite.Equal("Follow", accept.Object.Type) |
|
suite.Equal(originAccount.URI, accept.To) |
|
suite.Equal("Accept", accept.Type) |
|
|
|
ctx, _ = context.WithTimeout(ctx, time.Second*5) |
|
msg, ok := wssStream.Recv(context.Background()) |
|
suite.True(ok) |
|
|
|
suite.Equal(stream.EventTypeNotification, msg.Event) |
|
suite.NotEmpty(msg.Payload) |
|
suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) |
|
notif := &apimodel.Notification{} |
|
err = json.Unmarshal([]byte(msg.Payload), notif) |
|
suite.NoError(err) |
|
suite.Equal("follow", notif.Type) |
|
suite.Equal(originAccount.ID, notif.Account.ID) |
|
} |
|
|
|
// TestCreateStatusFromIRI checks if a forwarded status can be dereferenced by the processor. |
|
func (suite *FromFediAPITestSuite) TestCreateStatusFromIRI() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
ctx := context.Background() |
|
|
|
receivingAccount := suite.testAccounts["local_account_1"] |
|
statusCreator := suite.testAccounts["remote_account_2"] |
|
|
|
err := testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{ |
|
APObjectType: ap.ObjectNote, |
|
APActivityType: ap.ActivityCreate, |
|
GTSModel: nil, // gtsmodel is nil because this is a forwarded status -- we want to dereference it using the iri |
|
Receiving: receivingAccount, |
|
Requesting: statusCreator, |
|
APIRI: testrig.URLMustParse("http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1"), |
|
}) |
|
suite.NoError(err) |
|
|
|
// status should now be in the database, attributed to remote_account_2 |
|
s, err := testStructs.State.DB.GetStatusByURI(context.Background(), "http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1") |
|
suite.NoError(err) |
|
suite.Equal(statusCreator.URI, s.AccountURI) |
|
} |
|
|
|
func (suite *FromFediAPITestSuite) TestMoveAccount() { |
|
testStructs := suite.SetupTestStructs() |
|
defer suite.TearDownTestStructs(testStructs) |
|
|
|
// We're gonna migrate foss_satan to our local admin account. |
|
ctx := context.Background() |
|
receivingAcct := suite.testAccounts["local_account_1"] |
|
|
|
// Copy requesting and target accounts |
|
// since we'll be changing these. |
|
requestingAcct := >smodel.Account{} |
|
*requestingAcct = *suite.testAccounts["remote_account_1"] |
|
targetAcct := >smodel.Account{} |
|
*targetAcct = *suite.testAccounts["admin_account"] |
|
|
|
// Set alsoKnownAs on the admin account. |
|
targetAcct.AlsoKnownAsURIs = []string{requestingAcct.URI} |
|
if err := testStructs.State.DB.UpdateAccount(ctx, targetAcct, "also_known_as_uris"); err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Remove existing follow from zork to admin account. |
|
if err := testStructs.State.DB.DeleteFollowByID( |
|
ctx, |
|
suite.testFollows["local_account_1_admin_account"].ID, |
|
); err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Have Zork follow foss_satan instead. |
|
if err := testStructs.State.DB.PutFollow(ctx, >smodel.Follow{ |
|
ID: "01HRA0XZYFZC5MNWTKEBR58SSE", |
|
URI: "http://localhost:8080/users/the_mighty_zork/follows/01HRA0XZYFZC5MNWTKEBR58SSE", |
|
AccountID: receivingAcct.ID, |
|
TargetAccountID: requestingAcct.ID, |
|
}); err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Process the Move. |
|
err := testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{ |
|
APObjectType: ap.ActorPerson, |
|
APActivityType: ap.ActivityMove, |
|
GTSModel: >smodel.Move{ |
|
OriginURI: requestingAcct.URI, |
|
Origin: testrig.URLMustParse(requestingAcct.URI), |
|
TargetURI: targetAcct.URI, |
|
Target: testrig.URLMustParse(targetAcct.URI), |
|
URI: "https://fossbros-anonymous.io/users/foss_satan/moves/01HRA064871MR8HGVSAFJ333GM", |
|
}, |
|
Receiving: receivingAcct, |
|
Requesting: requestingAcct, |
|
}) |
|
suite.NoError(err) |
|
|
|
// Wait for side effects to trigger: |
|
// Zork should now be following admin account. |
|
if !testrig.WaitFor(func() bool { |
|
follows, err := testStructs.State.DB.IsFollowing(ctx, receivingAcct.ID, targetAcct.ID) |
|
if err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
return follows |
|
}) { |
|
suite.FailNow("timed out waiting for zork to follow admin account") |
|
} |
|
|
|
// Move should be in the DB. |
|
move, err := testStructs.State.DB.GetMoveByURI(ctx, "https://fossbros-anonymous.io/users/foss_satan/moves/01HRA064871MR8HGVSAFJ333GM") |
|
if err != nil { |
|
suite.FailNow(err.Error()) |
|
} |
|
|
|
// Move should be marked as completed. |
|
suite.WithinDuration(time.Now(), move.SucceededAt, 1*time.Minute) |
|
} |
|
|
|
func TestFromFederatorTestSuite(t *testing.T) { |
|
suite.Run(t, &FromFediAPITestSuite{}) |
|
}
|
|
|