// 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 . package workers_test import ( "context" "sync" "testing" "time" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/processing/workers" ) type SurfaceNotifyTestSuite struct { WorkersTestSuite } func (suite *SurfaceNotifyTestSuite) TestSpamNotifs() { testStructs := suite.SetupTestStructs() defer suite.TearDownTestStructs(testStructs) surface := &workers.Surface{ State: testStructs.State, Converter: testStructs.TypeConverter, Stream: testStructs.Processor.Stream(), Filter: visibility.NewFilter(testStructs.State), EmailSender: testStructs.EmailSender, Conversations: testStructs.Processor.Conversations(), } var ( ctx = context.Background() notificationType = gtsmodel.NotificationFollow targetAccount = suite.testAccounts["local_account_1"] originAccount = suite.testAccounts["local_account_2"] ) // Set up a bunch of goroutines to surface // a notification at exactly the same time. wg := sync.WaitGroup{} wg.Add(20) startAt := time.Now().Add(2 * time.Second) for i := 0; i < 20; i++ { go func() { defer wg.Done() // Wait for it.... untilTick := time.Until(startAt) <-time.Tick(untilTick) // ...Go! if err := surface.Notify(ctx, notificationType, targetAccount, originAccount, "", ); err != nil { suite.FailNow(err.Error()) } }() } // Wait for all notif creation // attempts to complete. wg.Wait() // Get all notifs for this account. notifs, err := testStructs.State.DB.GetAccountNotifications( gtscontext.SetBarebones(ctx), targetAccount.ID, "", "", "", 0, nil, nil, ) if err != nil { suite.FailNow(err.Error()) } var gotOne bool for _, notif := range notifs { if notif.NotificationType == notificationType && notif.TargetAccountID == targetAccount.ID && notif.OriginAccountID == originAccount.ID { // This is the notif... if gotOne { // We already had // the notif, d'oh! suite.FailNow("already had notif") } else { gotOne = true } } } } func TestSurfaceNotifyTestSuite(t *testing.T) { suite.Run(t, new(SurfaceNotifyTestSuite)) }