gtsocial-umbx

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

fromclientapi_test.go (10479B)


      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 processing_test
     19 
     20 import (
     21 	"context"
     22 	"encoding/json"
     23 	"errors"
     24 	"testing"
     25 
     26 	"github.com/stretchr/testify/suite"
     27 	"github.com/superseriousbusiness/gotosocial/internal/ap"
     28 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
     29 	"github.com/superseriousbusiness/gotosocial/internal/db"
     30 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     31 	"github.com/superseriousbusiness/gotosocial/internal/messages"
     32 	"github.com/superseriousbusiness/gotosocial/internal/stream"
     33 	"github.com/superseriousbusiness/gotosocial/testrig"
     34 )
     35 
     36 type FromClientAPITestSuite struct {
     37 	ProcessingStandardTestSuite
     38 }
     39 
     40 // This test ensures that when admin_account posts a new
     41 // status, it ends up in the correct streaming timelines
     42 // of local_account_1, which follows it.
     43 func (suite *FromClientAPITestSuite) TestProcessStreamNewStatus() {
     44 	var (
     45 		ctx              = context.Background()
     46 		postingAccount   = suite.testAccounts["admin_account"]
     47 		receivingAccount = suite.testAccounts["local_account_1"]
     48 		testList         = suite.testLists["local_account_1_list_1"]
     49 		streams          = suite.openStreams(ctx, receivingAccount, []string{testList.ID})
     50 		homeStream       = streams[stream.TimelineHome]
     51 		listStream       = streams[stream.TimelineList+":"+testList.ID]
     52 	)
     53 
     54 	// Make a new status from admin account.
     55 	newStatus := &gtsmodel.Status{
     56 		ID:                       "01FN4B2F88TF9676DYNXWE1WSS",
     57 		URI:                      "http://localhost:8080/users/admin/statuses/01FN4B2F88TF9676DYNXWE1WSS",
     58 		URL:                      "http://localhost:8080/@admin/statuses/01FN4B2F88TF9676DYNXWE1WSS",
     59 		Content:                  "this status should stream :)",
     60 		AttachmentIDs:            []string{},
     61 		TagIDs:                   []string{},
     62 		MentionIDs:               []string{},
     63 		EmojiIDs:                 []string{},
     64 		CreatedAt:                testrig.TimeMustParse("2021-10-20T11:36:45Z"),
     65 		UpdatedAt:                testrig.TimeMustParse("2021-10-20T11:36:45Z"),
     66 		Local:                    testrig.TrueBool(),
     67 		AccountURI:               "http://localhost:8080/users/admin",
     68 		AccountID:                "01F8MH17FWEB39HZJ76B6VXSKF",
     69 		InReplyToID:              "",
     70 		BoostOfID:                "",
     71 		ContentWarning:           "",
     72 		Visibility:               gtsmodel.VisibilityFollowersOnly,
     73 		Sensitive:                testrig.FalseBool(),
     74 		Language:                 "en",
     75 		CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
     76 		Federated:                testrig.FalseBool(),
     77 		Boostable:                testrig.TrueBool(),
     78 		Replyable:                testrig.TrueBool(),
     79 		Likeable:                 testrig.TrueBool(),
     80 		ActivityStreamsType:      ap.ObjectNote,
     81 	}
     82 
     83 	// Put the status in the db first, to mimic what
     84 	// would have already happened earlier up the flow.
     85 	if err := suite.db.PutStatus(ctx, newStatus); err != nil {
     86 		suite.FailNow(err.Error())
     87 	}
     88 
     89 	// Process the new status.
     90 	if err := suite.processor.ProcessFromClientAPI(ctx, messages.FromClientAPI{
     91 		APObjectType:   ap.ObjectNote,
     92 		APActivityType: ap.ActivityCreate,
     93 		GTSModel:       newStatus,
     94 		OriginAccount:  postingAccount,
     95 	}); err != nil {
     96 		suite.FailNow(err.Error())
     97 	}
     98 
     99 	// Check message in home stream.
    100 	homeMsg := <-homeStream.Messages
    101 	suite.Equal(stream.EventTypeUpdate, homeMsg.Event)
    102 	suite.EqualValues([]string{stream.TimelineHome}, homeMsg.Stream)
    103 	suite.Empty(homeStream.Messages) // Stream should now be empty.
    104 
    105 	// Check status from home stream.
    106 	homeStreamStatus := &apimodel.Status{}
    107 	if err := json.Unmarshal([]byte(homeMsg.Payload), homeStreamStatus); err != nil {
    108 		suite.FailNow(err.Error())
    109 	}
    110 	suite.Equal(newStatus.ID, homeStreamStatus.ID)
    111 	suite.Equal(newStatus.Content, homeStreamStatus.Content)
    112 
    113 	// Check message in list stream.
    114 	listMsg := <-listStream.Messages
    115 	suite.Equal(stream.EventTypeUpdate, listMsg.Event)
    116 	suite.EqualValues([]string{stream.TimelineList + ":" + testList.ID}, listMsg.Stream)
    117 	suite.Empty(listStream.Messages) // Stream should now be empty.
    118 
    119 	// Check status from list stream.
    120 	listStreamStatus := &apimodel.Status{}
    121 	if err := json.Unmarshal([]byte(listMsg.Payload), listStreamStatus); err != nil {
    122 		suite.FailNow(err.Error())
    123 	}
    124 	suite.Equal(newStatus.ID, listStreamStatus.ID)
    125 	suite.Equal(newStatus.Content, listStreamStatus.Content)
    126 }
    127 
    128 func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
    129 	var (
    130 		ctx                  = context.Background()
    131 		deletingAccount      = suite.testAccounts["local_account_1"]
    132 		receivingAccount     = suite.testAccounts["local_account_2"]
    133 		deletedStatus        = suite.testStatuses["local_account_1_status_1"]
    134 		boostOfDeletedStatus = suite.testStatuses["admin_account_status_4"]
    135 		streams              = suite.openStreams(ctx, receivingAccount, nil)
    136 		homeStream           = streams[stream.TimelineHome]
    137 	)
    138 
    139 	// Delete the status from the db first, to mimic what
    140 	// would have already happened earlier up the flow
    141 	if err := suite.db.DeleteStatusByID(ctx, deletedStatus.ID); err != nil {
    142 		suite.FailNow(err.Error())
    143 	}
    144 
    145 	// Process the status delete.
    146 	if err := suite.processor.ProcessFromClientAPI(ctx, messages.FromClientAPI{
    147 		APObjectType:   ap.ObjectNote,
    148 		APActivityType: ap.ActivityDelete,
    149 		GTSModel:       deletedStatus,
    150 		OriginAccount:  deletingAccount,
    151 	}); err != nil {
    152 		suite.FailNow(err.Error())
    153 	}
    154 
    155 	// Stream should have the delete of admin's boost in it now.
    156 	msg := <-homeStream.Messages
    157 	suite.Equal(stream.EventTypeDelete, msg.Event)
    158 	suite.Equal(boostOfDeletedStatus.ID, msg.Payload)
    159 	suite.EqualValues([]string{stream.TimelineHome}, msg.Stream)
    160 
    161 	// Stream should also have the delete of the message itself in it.
    162 	msg = <-homeStream.Messages
    163 	suite.Equal(stream.EventTypeDelete, msg.Event)
    164 	suite.Equal(deletedStatus.ID, msg.Payload)
    165 	suite.EqualValues([]string{stream.TimelineHome}, msg.Stream)
    166 
    167 	// Stream should now be empty.
    168 	suite.Empty(homeStream.Messages)
    169 
    170 	// Boost should no longer be in the database.
    171 	if !testrig.WaitFor(func() bool {
    172 		_, err := suite.db.GetStatusByID(ctx, boostOfDeletedStatus.ID)
    173 		return errors.Is(err, db.ErrNoEntries)
    174 	}) {
    175 		suite.FailNow("timed out waiting for status delete")
    176 	}
    177 }
    178 
    179 func (suite *FromClientAPITestSuite) TestProcessNewStatusWithNotification() {
    180 	var (
    181 		ctx              = context.Background()
    182 		postingAccount   = suite.testAccounts["admin_account"]
    183 		receivingAccount = suite.testAccounts["local_account_1"]
    184 		streams          = suite.openStreams(ctx, receivingAccount, nil)
    185 		notifStream      = streams[stream.TimelineNotifications]
    186 	)
    187 
    188 	// Update the follow from receiving account -> posting account so
    189 	// that receiving account wants notifs when posting account posts.
    190 	follow := &gtsmodel.Follow{}
    191 	*follow = *suite.testFollows["local_account_1_admin_account"]
    192 	follow.Notify = testrig.TrueBool()
    193 	if err := suite.db.UpdateFollow(ctx, follow); err != nil {
    194 		suite.FailNow(err.Error())
    195 	}
    196 
    197 	// Make a new status from admin account.
    198 	newStatus := &gtsmodel.Status{
    199 		ID:                       "01FN4B2F88TF9676DYNXWE1WSS",
    200 		URI:                      "http://localhost:8080/users/admin/statuses/01FN4B2F88TF9676DYNXWE1WSS",
    201 		URL:                      "http://localhost:8080/@admin/statuses/01FN4B2F88TF9676DYNXWE1WSS",
    202 		Content:                  "this status should create a notification",
    203 		AttachmentIDs:            []string{},
    204 		TagIDs:                   []string{},
    205 		MentionIDs:               []string{},
    206 		EmojiIDs:                 []string{},
    207 		CreatedAt:                testrig.TimeMustParse("2021-10-20T11:36:45Z"),
    208 		UpdatedAt:                testrig.TimeMustParse("2021-10-20T11:36:45Z"),
    209 		Local:                    testrig.TrueBool(),
    210 		AccountURI:               "http://localhost:8080/users/admin",
    211 		AccountID:                "01F8MH17FWEB39HZJ76B6VXSKF",
    212 		InReplyToID:              "",
    213 		BoostOfID:                "",
    214 		ContentWarning:           "",
    215 		Visibility:               gtsmodel.VisibilityFollowersOnly,
    216 		Sensitive:                testrig.FalseBool(),
    217 		Language:                 "en",
    218 		CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
    219 		Federated:                testrig.FalseBool(),
    220 		Boostable:                testrig.TrueBool(),
    221 		Replyable:                testrig.TrueBool(),
    222 		Likeable:                 testrig.TrueBool(),
    223 		ActivityStreamsType:      ap.ObjectNote,
    224 	}
    225 
    226 	// Put the status in the db first, to mimic what
    227 	// would have already happened earlier up the flow.
    228 	if err := suite.db.PutStatus(ctx, newStatus); err != nil {
    229 		suite.FailNow(err.Error())
    230 	}
    231 
    232 	// Process the new status.
    233 	if err := suite.processor.ProcessFromClientAPI(ctx, messages.FromClientAPI{
    234 		APObjectType:   ap.ObjectNote,
    235 		APActivityType: ap.ActivityCreate,
    236 		GTSModel:       newStatus,
    237 		OriginAccount:  postingAccount,
    238 	}); err != nil {
    239 		suite.FailNow(err.Error())
    240 	}
    241 
    242 	// Wait for a notification to appear for the status.
    243 	if !testrig.WaitFor(func() bool {
    244 		_, err := suite.db.GetNotification(
    245 			ctx,
    246 			gtsmodel.NotificationStatus,
    247 			receivingAccount.ID,
    248 			postingAccount.ID,
    249 			newStatus.ID,
    250 		)
    251 		return err == nil
    252 	}) {
    253 		suite.FailNow("timed out waiting for new status notification")
    254 	}
    255 
    256 	// Check message in notification stream.
    257 	notifMsg := <-notifStream.Messages
    258 	suite.Equal(stream.EventTypeNotification, notifMsg.Event)
    259 	suite.EqualValues([]string{stream.TimelineNotifications}, notifMsg.Stream)
    260 	suite.Empty(notifStream.Messages) // Stream should now be empty.
    261 
    262 	// Check notif.
    263 	notif := &apimodel.Notification{}
    264 	if err := json.Unmarshal([]byte(notifMsg.Payload), notif); err != nil {
    265 		suite.FailNow(err.Error())
    266 	}
    267 	suite.Equal(newStatus.ID, notif.Status.ID)
    268 }
    269 
    270 func TestFromClientAPITestSuite(t *testing.T) {
    271 	suite.Run(t, &FromClientAPITestSuite{})
    272 }