diff options
| author | 2024-07-04 19:29:28 -0700 | |
|---|---|---|
| committer | 2024-07-04 19:29:28 -0700 | |
| commit | be5e532cd21fdbdfd1589186ed66a495ed5b8b35 (patch) | |
| tree | d70eb05166f08225c484474bbef4bbbdc04bdf2b /internal/processing/timeline | |
| parent | [feature] Set some security related headers (#3065) (diff) | |
| download | gotosocial-be5e532cd21fdbdfd1589186ed66a495ed5b8b35.tar.xz | |
[bugfix] Handle ErrHideStatus when preparing timeline statuses (#3071)
Diffstat (limited to 'internal/processing/timeline')
| -rw-r--r-- | internal/processing/timeline/home_test.go | 154 | ||||
| -rw-r--r-- | internal/processing/timeline/public_test.go | 91 | ||||
| -rw-r--r-- | internal/processing/timeline/timeline_test.go | 2 | 
3 files changed, 247 insertions, 0 deletions
| diff --git a/internal/processing/timeline/home_test.go b/internal/processing/timeline/home_test.go new file mode 100644 index 000000000..c73c209a3 --- /dev/null +++ b/internal/processing/timeline/home_test.go @@ -0,0 +1,154 @@ +// 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 timeline_test + +import ( +	"context" +	"testing" + +	"github.com/stretchr/testify/suite" +	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/superseriousbusiness/gotosocial/internal/id" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" +	tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" +	"github.com/superseriousbusiness/gotosocial/internal/timeline" +	"github.com/superseriousbusiness/gotosocial/internal/typeutils" +	"github.com/superseriousbusiness/gotosocial/internal/util" +) + +type HomeTestSuite struct { +	TimelineStandardTestSuite +} + +func (suite *HomeTestSuite) SetupTest() { +	suite.TimelineStandardTestSuite.SetupTest() + +	suite.state.Timelines.Home = timeline.NewManager( +		tlprocessor.HomeTimelineGrab(&suite.state), +		tlprocessor.HomeTimelineFilter(&suite.state, visibility.NewFilter(&suite.state)), +		tlprocessor.HomeTimelineStatusPrepare(&suite.state, typeutils.NewConverter(&suite.state)), +		tlprocessor.SkipInsert(), +	) +	if err := suite.state.Timelines.Home.Start(); err != nil { +		suite.FailNow(err.Error()) +	} +} + +func (suite *HomeTestSuite) TearDownTest() { +	if err := suite.state.Timelines.Home.Stop(); err != nil { +		suite.FailNow(err.Error()) +	} + +	suite.TimelineStandardTestSuite.TearDownTest() +} + +// A timeline containing a status hidden due to filtering should return other statuses with no error. +func (suite *HomeTestSuite) TestHomeTimelineGetHideFiltered() { +	var ( +		ctx                 = context.Background() +		requester           = suite.testAccounts["local_account_1"] +		authed              = &oauth.Auth{Account: requester} +		maxID               = "" +		sinceID             = "" +		minID               = "01F8MHAAY43M6RJ473VQFCVH36" // 1 before filteredStatus +		limit               = 40 +		local               = false +		filteredStatus      = suite.testStatuses["admin_account_status_2"] +		filteredStatusFound = false +		filterID            = id.NewULID() +		filter              = >smodel.Filter{ +			ID:        filterID, +			AccountID: requester.ID, +			Title:     "timeline filtering test", +			Action:    gtsmodel.FilterActionHide, +			Statuses: []*gtsmodel.FilterStatus{ +				{ +					ID:        id.NewULID(), +					AccountID: requester.ID, +					FilterID:  filterID, +					StatusID:  filteredStatus.ID, +				}, +			}, +			ContextHome:          util.Ptr(true), +			ContextNotifications: util.Ptr(false), +			ContextPublic:        util.Ptr(false), +			ContextThread:        util.Ptr(false), +			ContextAccount:       util.Ptr(false), +		} +	) + +	// Fetch the timeline to make sure the status we're going to filter is in that section of it. +	resp, errWithCode := suite.timeline.HomeTimelineGet( +		ctx, +		authed, +		maxID, +		sinceID, +		minID, +		limit, +		local, +	) +	suite.NoError(errWithCode) +	for _, item := range resp.Items { +		if item.(*apimodel.Status).ID == filteredStatus.ID { +			filteredStatusFound = true +			break +		} +	} +	if !filteredStatusFound { +		suite.FailNow("precondition failed: status we would filter isn't present in unfiltered timeline") +	} +	// Prune the timeline to drop cached prepared statuses, a side effect of this precondition check. +	if _, err := suite.state.Timelines.Home.Prune(ctx, requester.ID, 0, 0); err != nil { +		suite.FailNow(err.Error()) +	} + +	// Create a filter to hide one status on the timeline. +	if err := suite.db.PutFilter(ctx, filter); err != nil { +		suite.FailNow(err.Error()) +	} + +	// Fetch the timeline again with the filter in place. +	resp, errWithCode = suite.timeline.HomeTimelineGet( +		ctx, +		authed, +		maxID, +		sinceID, +		minID, +		limit, +		local, +	) + +	// We should have some statuses even though one status was filtered out. +	suite.NoError(errWithCode) +	suite.NotEmpty(resp.Items) +	// The filtered status should not be there. +	filteredStatusFound = false +	for _, item := range resp.Items { +		if item.(*apimodel.Status).ID == filteredStatus.ID { +			filteredStatusFound = true +			break +		} +	} +	suite.False(filteredStatusFound) +} + +func TestHomeTestSuite(t *testing.T) { +	suite.Run(t, new(HomeTestSuite)) +} diff --git a/internal/processing/timeline/public_test.go b/internal/processing/timeline/public_test.go index f14fee1b9..6b01c9849 100644 --- a/internal/processing/timeline/public_test.go +++ b/internal/processing/timeline/public_test.go @@ -22,6 +22,10 @@ import (  	"testing"  	"github.com/stretchr/testify/suite" +	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/superseriousbusiness/gotosocial/internal/id" +	"github.com/superseriousbusiness/gotosocial/internal/util"  )  type PublicTestSuite struct { @@ -91,6 +95,93 @@ func (suite *PublicTestSuite) TestPublicTimelineGetNotEmpty() {  	suite.Equal(`http://localhost:8080/api/v1/timelines/public?limit=1&min_id=01HE7XJ1CG84TBKH5V9XKBVGF5&local=false`, resp.PrevLink)  } +// A timeline containing a status hidden due to filtering should return other statuses with no error. +func (suite *PublicTestSuite) TestPublicTimelineGetHideFiltered() { +	var ( +		ctx                 = context.Background() +		requester           = suite.testAccounts["local_account_1"] +		maxID               = "" +		sinceID             = "" +		minID               = "01F8MHAAY43M6RJ473VQFCVH36" // 1 before filteredStatus +		limit               = 10 +		local               = false +		filteredStatus      = suite.testStatuses["admin_account_status_2"] +		filteredStatusFound = false +		filterID            = id.NewULID() +		filter              = >smodel.Filter{ +			ID:        filterID, +			AccountID: requester.ID, +			Title:     "timeline filtering test", +			Action:    gtsmodel.FilterActionHide, +			Statuses: []*gtsmodel.FilterStatus{ +				{ +					ID:        id.NewULID(), +					AccountID: requester.ID, +					FilterID:  filterID, +					StatusID:  filteredStatus.ID, +				}, +			}, +			ContextHome:          util.Ptr(false), +			ContextNotifications: util.Ptr(false), +			ContextPublic:        util.Ptr(true), +			ContextThread:        util.Ptr(false), +			ContextAccount:       util.Ptr(false), +		} +	) + +	// Fetch the timeline to make sure the status we're going to filter is in that section of it. +	resp, errWithCode := suite.timeline.PublicTimelineGet( +		ctx, +		requester, +		maxID, +		sinceID, +		minID, +		limit, +		local, +	) +	suite.NoError(errWithCode) +	for _, item := range resp.Items { +		if item.(*apimodel.Status).ID == filteredStatus.ID { +			filteredStatusFound = true +			break +		} +	} +	if !filteredStatusFound { +		suite.FailNow("precondition failed: status we would filter isn't present in unfiltered timeline") +	} +	// The public timeline has no prepared status cache and doesn't need to be pruned, +	// as in the home timeline version of this test. + +	// Create a filter to hide one status on the timeline. +	if err := suite.db.PutFilter(ctx, filter); err != nil { +		suite.FailNow(err.Error()) +	} + +	// Fetch the timeline again with the filter in place. +	resp, errWithCode = suite.timeline.PublicTimelineGet( +		ctx, +		requester, +		maxID, +		sinceID, +		minID, +		limit, +		local, +	) + +	// We should have some statuses even though one status was filtered out. +	suite.NoError(errWithCode) +	suite.NotEmpty(resp.Items) +	// The filtered status should not be there. +	filteredStatusFound = false +	for _, item := range resp.Items { +		if item.(*apimodel.Status).ID == filteredStatus.ID { +			filteredStatusFound = true +			break +		} +	} +	suite.False(filteredStatusFound) +} +  func TestPublicTestSuite(t *testing.T) {  	suite.Run(t, new(PublicTestSuite))  } diff --git a/internal/processing/timeline/timeline_test.go b/internal/processing/timeline/timeline_test.go index 79626830e..593bfb8f3 100644 --- a/internal/processing/timeline/timeline_test.go +++ b/internal/processing/timeline/timeline_test.go @@ -35,6 +35,7 @@ type TimelineStandardTestSuite struct {  	// standard suite models  	testAccounts map[string]*gtsmodel.Account +	testStatuses map[string]*gtsmodel.Status  	// module being tested  	timeline timeline.Processor @@ -42,6 +43,7 @@ type TimelineStandardTestSuite struct {  func (suite *TimelineStandardTestSuite) SetupSuite() {  	suite.testAccounts = testrig.NewTestAccounts() +	suite.testStatuses = testrig.NewTestStatuses()  }  func (suite *TimelineStandardTestSuite) SetupTest() { | 
