diff options
| -rw-r--r-- | internal/ap/extract.go | 29 | ||||
| -rw-r--r-- | internal/api/client/admin/reportsget_test.go | 14 | ||||
| -rw-r--r-- | internal/api/client/reports/reportget_test.go | 2 | ||||
| -rw-r--r-- | internal/api/client/reports/reportsget_test.go | 8 | ||||
| -rw-r--r-- | internal/db/bundb/basic_test.go | 2 | ||||
| -rw-r--r-- | internal/db/bundb/instance_test.go | 2 | ||||
| -rw-r--r-- | internal/db/bundb/migrations/20231110142330_small_poll_table_tweaks.go | 84 | ||||
| -rw-r--r-- | internal/federation/dereferencing/status.go | 13 | ||||
| -rw-r--r-- | internal/federation/dereferencing/util.go | 27 | ||||
| -rw-r--r-- | internal/gtsmodel/poll.go | 4 | ||||
| -rw-r--r-- | internal/processing/stream/notification_test.go | 2 | ||||
| -rw-r--r-- | internal/timeline/prune_test.go | 8 | ||||
| -rw-r--r-- | internal/typeutils/internaltoas.go | 6 | ||||
| -rw-r--r-- | internal/typeutils/internaltofrontend.go | 8 | ||||
| -rw-r--r-- | internal/typeutils/internaltofrontend_test.go | 10 | ||||
| -rw-r--r-- | testrig/testmodels.go | 45 | 
16 files changed, 212 insertions, 52 deletions
diff --git a/internal/ap/extract.go b/internal/ap/extract.go index b412f251d..424f77409 100644 --- a/internal/ap/extract.go +++ b/internal/ap/extract.go @@ -1125,26 +1125,31 @@ func ExtractPoll(poll Pollable) (*gtsmodel.Poll, error) {  	// Check if counts have been hidden from us.  	hideCounts := len(options) != len(votes) -	if hideCounts { -		// Zero out all votes. -		for i := range votes { -			votes[i] = 0 -		} -	} - -	// Extract the poll end time. -	endTime := GetEndTime(poll) -	if endTime.IsZero() { -		return nil, errors.New("no poll end time specified") +	if hideCounts { +		// Simply provide zeroed slice. +		votes = make([]int, len(options))  	} -	// Extract the poll closed time. +	// Extract the poll closed time, +	// it's okay for this to be zero.  	closedSlice := GetClosed(poll)  	if len(closedSlice) == 1 {  		closed = closedSlice[0]  	} +	// Extract the poll end time, again +	// this isn't necessarily set as some +	// servers support "endless" polls. +	endTime := GetEndTime(poll) + +	if endTime.IsZero() && !closed.IsZero() { +		// If no endTime is provided, but the +		// poll is marked as closed, infer the +		// endTime from the closed time. +		endTime = closed +	} +  	// Extract the number of voters.  	voters := GetVotersCount(poll) diff --git a/internal/api/client/admin/reportsget_test.go b/internal/api/client/admin/reportsget_test.go index ecef1d465..18f10e489 100644 --- a/internal/api/client/admin/reportsget_test.go +++ b/internal/api/client/admin/reportsget_test.go @@ -180,7 +180,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {          "header_static": "http://localhost:8080/assets/default_header.png",          "followers_count": 0,          "following_count": 0, -        "statuses_count": 2, +        "statuses_count": 3,          "last_status_at": "2021-09-11T09:40:37.000Z",          "emojis": [],          "fields": [] @@ -438,7 +438,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {          "header_static": "http://localhost:8080/assets/default_header.png",          "followers_count": 0,          "following_count": 0, -        "statuses_count": 2, +        "statuses_count": 3,          "last_status_at": "2021-09-11T09:40:37.000Z",          "emojis": [],          "fields": [] @@ -485,7 +485,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {            "header_static": "http://localhost:8080/assets/default_header.png",            "followers_count": 0,            "following_count": 0, -          "statuses_count": 2, +          "statuses_count": 3,            "last_status_at": "2021-09-11T09:40:37.000Z",            "emojis": [],            "fields": [] @@ -659,7 +659,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetCreatedByAccount() {          "header_static": "http://localhost:8080/assets/default_header.png",          "followers_count": 0,          "following_count": 0, -        "statuses_count": 2, +        "statuses_count": 3,          "last_status_at": "2021-09-11T09:40:37.000Z",          "emojis": [],          "fields": [] @@ -706,7 +706,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetCreatedByAccount() {            "header_static": "http://localhost:8080/assets/default_header.png",            "followers_count": 0,            "following_count": 0, -          "statuses_count": 2, +          "statuses_count": 3,            "last_status_at": "2021-09-11T09:40:37.000Z",            "emojis": [],            "fields": [] @@ -880,7 +880,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetTargetAccount() {          "header_static": "http://localhost:8080/assets/default_header.png",          "followers_count": 0,          "following_count": 0, -        "statuses_count": 2, +        "statuses_count": 3,          "last_status_at": "2021-09-11T09:40:37.000Z",          "emojis": [],          "fields": [] @@ -927,7 +927,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetTargetAccount() {            "header_static": "http://localhost:8080/assets/default_header.png",            "followers_count": 0,            "following_count": 0, -          "statuses_count": 2, +          "statuses_count": 3,            "last_status_at": "2021-09-11T09:40:37.000Z",            "emojis": [],            "fields": [] diff --git a/internal/api/client/reports/reportget_test.go b/internal/api/client/reports/reportget_test.go index bc9fcaa7b..5b6c406f7 100644 --- a/internal/api/client/reports/reportget_test.go +++ b/internal/api/client/reports/reportget_test.go @@ -129,7 +129,7 @@ func (suite *ReportGetTestSuite) TestGetReport1() {      "header_static": "http://localhost:8080/assets/default_header.png",      "followers_count": 0,      "following_count": 0, -    "statuses_count": 2, +    "statuses_count": 3,      "last_status_at": "2021-09-11T09:40:37.000Z",      "emojis": [],      "fields": [] diff --git a/internal/api/client/reports/reportsget_test.go b/internal/api/client/reports/reportsget_test.go index 64e79ea00..c63d6c894 100644 --- a/internal/api/client/reports/reportsget_test.go +++ b/internal/api/client/reports/reportsget_test.go @@ -154,7 +154,7 @@ func (suite *ReportsGetTestSuite) TestGetReports() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] @@ -244,7 +244,7 @@ func (suite *ReportsGetTestSuite) TestGetReports4() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] @@ -318,7 +318,7 @@ func (suite *ReportsGetTestSuite) TestGetReports6() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] @@ -376,7 +376,7 @@ func (suite *ReportsGetTestSuite) TestGetReports7() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] diff --git a/internal/db/bundb/basic_test.go b/internal/db/bundb/basic_test.go index 403a3888a..67973a171 100644 --- a/internal/db/bundb/basic_test.go +++ b/internal/db/bundb/basic_test.go @@ -121,7 +121,7 @@ func (suite *BasicTestSuite) TestGetAllStatuses() {  	s := []*gtsmodel.Status{}  	err := suite.db.GetAll(context.Background(), &s)  	suite.NoError(err) -	suite.Len(s, 21) +	suite.Len(s, 22)  }  func (suite *BasicTestSuite) TestGetAllNotNull() { diff --git a/internal/db/bundb/instance_test.go b/internal/db/bundb/instance_test.go index d88825a33..f2ac202ef 100644 --- a/internal/db/bundb/instance_test.go +++ b/internal/db/bundb/instance_test.go @@ -53,7 +53,7 @@ func (suite *InstanceTestSuite) TestCountInstanceStatuses() {  func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {  	count, err := suite.db.CountInstanceStatuses(context.Background(), "fossbros-anonymous.io")  	suite.NoError(err) -	suite.Equal(2, count) +	suite.Equal(3, count)  }  func (suite *InstanceTestSuite) TestCountInstanceDomains() { diff --git a/internal/db/bundb/migrations/20231110142330_small_poll_table_tweaks.go b/internal/db/bundb/migrations/20231110142330_small_poll_table_tweaks.go new file mode 100644 index 000000000..c9f2b3d0f --- /dev/null +++ b/internal/db/bundb/migrations/20231110142330_small_poll_table_tweaks.go @@ -0,0 +1,84 @@ +// 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 migrations + +import ( +	"context" + +	"github.com/uptrace/bun" +) + +func init() { +	up := func(ctx context.Context, db *bun.DB) error { +		return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { +			// Add new poll expiry column WITHOUT +			// the previously set NULL constraint. +			if _, err := tx.NewAddColumn(). +				Table("polls"). +				ColumnExpr("? TIMESTAMPTZ", bun.Ident("expires_at_new")). +				Exec(ctx); err != nil { +				return err +			} + +			// Copy all data from old to new, +			// this won't cause anyn issues as +			// old column is NOT NULL and it's +			// only the new column that drops +			// this constraint to allow NULL. +			if _, err := tx.NewUpdate(). +				Table("polls"). +				Column("expires_at_new"). +				Set("? = ?", bun.Ident("expires_at_new"), bun.Ident("expires_at")). +				Where("1"). // bun gets angry performing update over all rows +				Exec(ctx); err != nil { +				return err +			} + +			// Drop the old poll expiry column. +			if _, err := tx.NewDropColumn(). +				Table("polls"). +				ColumnExpr("?", bun.Ident("expires_at")). +				Exec(ctx); err != nil { +				return err +			} + +			// Rename the new expiry column +			// to the correct name (of the old). +			if _, err := tx.NewRaw( +				"ALTER TABLE ? RENAME COLUMN ? TO ?", +				bun.Ident("polls"), +				bun.Ident("expires_at_new"), +				bun.Ident("expires_at"), +			).Exec(ctx); err != nil { +				return err +			} + +			return nil +		}) +	} + +	down := func(ctx context.Context, db *bun.DB) error { +		return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { +			return nil +		}) +	} + +	if err := Migrations.Register(up, down); err != nil { +		panic(err) +	} +} diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go index 032238bd5..d05875edb 100644 --- a/internal/federation/dereferencing/status.go +++ b/internal/federation/dereferencing/status.go @@ -737,27 +737,22 @@ func (d *Dereferencer) fetchStatusPoll(ctx context.Context, existing, status *gt  		// no previous poll, insert new poll!  		return insertStatusPoll(ctx, status) -	case /*existing.Poll != nil &&*/ status.Poll == nil: +	case status.Poll == nil:  		// existing poll has been deleted, remove this.  		return deleteStatusPoll(ctx, existing.PollID) -	case /*existing.Poll != nil && status.Poll != nil && */ -		!slices.Equal(existing.Poll.Options, status.Poll.Options) || -			!existing.Poll.ExpiresAt.Equal(status.Poll.ExpiresAt): +	case pollChanged(existing.Poll, status.Poll):  		// poll has changed since original, delete and reinsert new.  		if err := deleteStatusPoll(ctx, existing.PollID); err != nil {  			return err  		}  		return insertStatusPoll(ctx, status) -	case /*existing.Poll != nil && status.Poll != nil && */ -		!existing.Poll.ClosedAt.Equal(status.Poll.ClosedAt) || -			!slices.Equal(existing.Poll.Votes, status.Poll.Votes) || -			existing.Poll.Voters != status.Poll.Voters: +	case pollUpdated(existing.Poll, status.Poll):  		// Since we last saw it, the poll has updated!  		// Whether that be stats, or close time.  		poll := existing.Poll -		poll.Closing = (!poll.Closed() && status.Poll.Closed()) +		poll.Closing = pollJustClosed(existing.Poll, status.Poll)  		poll.ClosedAt = status.Poll.ClosedAt  		poll.Voters = status.Poll.Voters  		poll.Votes = status.Poll.Votes diff --git a/internal/federation/dereferencing/util.go b/internal/federation/dereferencing/util.go index e69aeec3b..c37f2d82d 100644 --- a/internal/federation/dereferencing/util.go +++ b/internal/federation/dereferencing/util.go @@ -17,6 +17,12 @@  package dereferencing +import ( +	"slices" + +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) +  // doOnce wraps a function to only perform it once.  func doOnce(fn func()) func() {  	var once int32 @@ -27,3 +33,24 @@ func doOnce(fn func()) func() {  		}  	}  } + +// pollChanged returns whether a poll has changed in way that +// indicates that this should be an entirely new poll. i.e. if +// the available options have changed, or the expiry has increased. +func pollChanged(existing, latest *gtsmodel.Poll) bool { +	return !slices.Equal(existing.Options, latest.Options) || +		!existing.ExpiresAt.Equal(latest.ExpiresAt) +} + +// pollUpdated returns whether a poll has updated, i.e. if the +// vote counts have changed, or if it has expired / been closed. +func pollUpdated(existing, latest *gtsmodel.Poll) bool { +	return *existing.Voters != *latest.Voters || +		!slices.Equal(existing.Votes, latest.Votes) || +		!existing.ClosedAt.Equal(latest.ClosedAt) +} + +// pollJustClosed returns whether a poll has *just* closed. +func pollJustClosed(existing, latest *gtsmodel.Poll) bool { +	return existing.ClosedAt.IsZero() && latest.Closed() +} diff --git a/internal/gtsmodel/poll.go b/internal/gtsmodel/poll.go index 7e131ebba..35a9ddf59 100644 --- a/internal/gtsmodel/poll.go +++ b/internal/gtsmodel/poll.go @@ -32,8 +32,8 @@ type Poll struct {  	Voters     *int      `bun:",nullzero,notnull"`                        // Total no. voters count.  	StatusID   string    `bun:"type:CHAR(26),nullzero,notnull,unique"`    // Status ID of which this Poll is attached to.  	Status     *Status   `bun:"-"`                                        // The related Status for StatusID (not always set). -	ExpiresAt  time.Time `bun:"type:timestamptz,nullzero,notnull"`        // The expiry date of this Poll. -	ClosedAt   time.Time `bun:"type:timestamptz,nullzero"`                // The closure date of this poll, will be zerotime until set. +	ExpiresAt  time.Time `bun:"type:timestamptz,nullzero"`                // The expiry date of this Poll, will be zerotime until set. (local polls ALWAYS have this set). +	ClosedAt   time.Time `bun:"type:timestamptz,nullzero"`                // The closure date of this poll, anything other than zerotime indicates closed.  	Closing    bool      `bun:"-"`                                        // An ephemeral field only set on Polls in the middle of closing.  	// no creation date, use attached Status.CreatedAt.  } diff --git a/internal/processing/stream/notification_test.go b/internal/processing/stream/notification_test.go index 8a66b68a4..2138f0025 100644 --- a/internal/processing/stream/notification_test.go +++ b/internal/processing/stream/notification_test.go @@ -77,7 +77,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() {      "header_static": "http://localhost:8080/assets/default_header.png",      "followers_count": 0,      "following_count": 0, -    "statuses_count": 2, +    "statuses_count": 3,      "last_status_at": "2021-09-11T09:40:37.000Z",      "emojis": [],      "fields": [] diff --git a/internal/timeline/prune_test.go b/internal/timeline/prune_test.go index 46bd652b7..4cd677953 100644 --- a/internal/timeline/prune_test.go +++ b/internal/timeline/prune_test.go @@ -40,7 +40,7 @@ func (suite *PruneTestSuite) TestPrune() {  	pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)  	suite.NoError(err) -	suite.Equal(16, pruned) +	suite.Equal(17, pruned)  	suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))  } @@ -56,7 +56,7 @@ func (suite *PruneTestSuite) TestPruneTwice() {  	pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)  	suite.NoError(err) -	suite.Equal(16, pruned) +	suite.Equal(17, pruned)  	suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))  	// Prune same again, nothing should be pruned this time. @@ -78,7 +78,7 @@ func (suite *PruneTestSuite) TestPruneTo0() {  	pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)  	suite.NoError(err) -	suite.Equal(21, pruned) +	suite.Equal(22, pruned)  	suite.Equal(0, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))  } @@ -95,7 +95,7 @@ func (suite *PruneTestSuite) TestPruneToInfinityAndBeyond() {  	pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)  	suite.NoError(err)  	suite.Equal(0, pruned) -	suite.Equal(21, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID)) +	suite.Equal(22, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))  }  func TestPruneTestSuite(t *testing.T) { diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index 541e2f4d1..16467be40 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -708,8 +708,10 @@ func (c *Converter) addPollToAS(ctx context.Context, poll *gtsmodel.Poll, dst ap  		optionsProp.AppendActivityStreamsNote(note)  	} -	// Set poll endTime property. -	ap.SetEndTime(dst, poll.ExpiresAt) +	if !poll.ExpiresAt.IsZero() { +		// Set poll endTime property. +		ap.SetEndTime(dst, poll.ExpiresAt) +	}  	if !poll.ClosedAt.IsZero() {  		// Poll is closed, set closed property. diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index a7bcddac6..1a2c321f5 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -1367,6 +1367,7 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou  		voted       *bool  		ownChoices  *[]int  		isAuthor    bool +		expiresAt   string  		emojis      []apimodel.Emoji  	) @@ -1428,6 +1429,11 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou  		}  	} +	if !poll.ExpiresAt.IsZero() { +		// Calculate poll expiry string (if set). +		expiresAt = util.FormatISO8601(poll.ExpiresAt) +	} +  	// TODO: emojis used in poll options.  	// For now init to empty slice to serialize as `[]`.  	// In future inherit from parent status. @@ -1435,7 +1441,7 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou  	return &apimodel.Poll{  		ID:          poll.ID, -		ExpiresAt:   util.FormatISO8601(poll.ExpiresAt), +		ExpiresAt:   expiresAt,  		Expired:     poll.Closed(),  		Multiple:    (*poll.Multiple),  		VotesCount:  totalVotes, diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 0e5d3a45b..d386c180e 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1010,7 +1010,7 @@ func (suite *InternalToFrontendTestSuite) TestReportToFrontend1() {      "header_static": "http://localhost:8080/assets/default_header.png",      "followers_count": 0,      "following_count": 0, -    "statuses_count": 2, +    "statuses_count": 3,      "last_status_at": "2021-09-11T09:40:37.000Z",      "emojis": [],      "fields": [] @@ -1127,7 +1127,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend1() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] @@ -1395,7 +1395,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend2() {        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] @@ -1442,7 +1442,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend2() {          "header_static": "http://localhost:8080/assets/default_header.png",          "followers_count": 0,          "following_count": 0, -        "statuses_count": 2, +        "statuses_count": 3,          "last_status_at": "2021-09-11T09:40:37.000Z",          "emojis": [],          "fields": [] @@ -1573,7 +1573,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontendSuspendedLoca        "header_static": "http://localhost:8080/assets/default_header.png",        "followers_count": 0,        "following_count": 0, -      "statuses_count": 2, +      "statuses_count": 3,        "last_status_at": "2021-09-11T09:40:37.000Z",        "emojis": [],        "fields": [] diff --git a/testrig/testmodels.go b/testrig/testmodels.go index d27f91e11..248508013 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -1916,8 +1916,8 @@ func NewTestStatuses() map[string]*gtsmodel.Status {  		},  		"remote_account_1_status_2": {  			ID:                       "01HEN2QRFA8H3C6QPN7RD4KSR6", -			URI:                      "http://fossbros-anonymous.io/users/foss_satan/statuses/065TKDN4BX1PC8N19TSY9SD2N4", -			URL:                      "http://fossbros-anonymous.io/@foss_satan/statuses/065TKDN4BX1PC8N19TSY9SD2N4", +			URI:                      "http://fossbros-anonymous.io/users/foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6", +			URL:                      "http://fossbros-anonymous.io/@foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",  			Content:                  "what products should i buy at the grocery store?",  			AttachmentIDs:            []string{"01FVW7RXPQ8YJHTEXYPE7Q8ZY0"},  			CreatedAt:                TimeMustParse("2021-09-11T11:40:37+02:00"), @@ -1941,6 +1941,33 @@ func NewTestStatuses() map[string]*gtsmodel.Status {  			ActivityStreamsType:      ap.ActivityQuestion,  			PollID:                   "01HEN2R65468ZG657C4ZPHJ4EX",  		}, +		"remote_account_1_status_3": { +			ID:                       "01HEWV37MHV8BAC8ANFGVRRM5D", +			URI:                      "http://fossbros-anonymous.io/users/foss_satan/statuses/01HEWV37MHV8BAC8ANFGVRRM5D", +			URL:                      "http://fossbros-anonymous.io/@foss_satan/statuses/01HEWV37MHV8BAC8ANFGVRRM5D", +			Content:                  "what products should i buy at the grocery store? (now an endless poll!)", +			AttachmentIDs:            []string{"01FVW7RXPQ8YJHTEXYPE7Q8ZY0"}, +			CreatedAt:                TimeMustParse("2021-09-11T11:40:37+02:00"), +			UpdatedAt:                TimeMustParse("2021-09-11T11:40:37+02:00"), +			Local:                    util.Ptr(false), +			AccountURI:               "http://fossbros-anonymous.io/users/foss_satan", +			AccountID:                "01F8MH5ZK5VRH73AKHQM6Y9VNX", +			InReplyToID:              "", +			InReplyToAccountID:       "", +			InReplyToURI:             "", +			BoostOfID:                "", +			ContentWarning:           "", +			Visibility:               gtsmodel.VisibilityUnlocked, +			Sensitive:                util.Ptr(false), +			Language:                 "en", +			CreatedWithApplicationID: "", +			Federated:                util.Ptr(true), +			Boostable:                util.Ptr(true), +			Replyable:                util.Ptr(true), +			Likeable:                 util.Ptr(true), +			ActivityStreamsType:      ap.ActivityQuestion, +			PollID:                   "01HEWV1GW2D49R919NPEDXPTZ5", +		},  		"remote_account_2_status_1": {  			ID:                       "01HE7XJ1CG84TBKH5V9XKBVGF5",  			URI:                      "http://example.org/users/Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5", @@ -2012,6 +2039,20 @@ func NewTestPolls() map[string]*gtsmodel.Poll {  			ClosedAt:   TimeMustParse("2021-09-11T12:40:37+02:00"),  			Closing:    false,  		}, +		"remote_account_1_status_3_poll": { +			ID:         "01HEWV1GW2D49R919NPEDXPTZ5", +			Multiple:   util.Ptr(true), +			HideCounts: util.Ptr(false), +			Options:    []string{"vaseline", "tissues", "financial times"}, +			Votes:      []int{0, 0, 0}, +			Voters:     util.Ptr(0), +			StatusID:   "01HEWV37MHV8BAC8ANFGVRRM5D", +			Status:     nil, +			// nil expiry AND closed date, i.e. no end +			ExpiresAt: time.Time{}, +			ClosedAt:  time.Time{}, +			Closing:   false, +		},  	}  }  | 
