[bugfix] support endless polls, and misskey's' method of inferring expiry in closed polls (#2349)
This commit is contained in:
parent
ba9d6b467a
commit
deaea100c3
|
@ -1125,26 +1125,31 @@ func ExtractPoll(poll Pollable) (*gtsmodel.Poll, error) {
|
||||||
|
|
||||||
// Check if counts have been hidden from us.
|
// Check if counts have been hidden from us.
|
||||||
hideCounts := len(options) != len(votes)
|
hideCounts := len(options) != len(votes)
|
||||||
|
|
||||||
if hideCounts {
|
if hideCounts {
|
||||||
|
// Simply provide zeroed slice.
|
||||||
// Zero out all votes.
|
votes = make([]int, len(options))
|
||||||
for i := range votes {
|
|
||||||
votes[i] = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the poll end time.
|
// Extract the poll closed time,
|
||||||
endTime := GetEndTime(poll)
|
// it's okay for this to be zero.
|
||||||
if endTime.IsZero() {
|
|
||||||
return nil, errors.New("no poll end time specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the poll closed time.
|
|
||||||
closedSlice := GetClosed(poll)
|
closedSlice := GetClosed(poll)
|
||||||
if len(closedSlice) == 1 {
|
if len(closedSlice) == 1 {
|
||||||
closed = closedSlice[0]
|
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.
|
// Extract the number of voters.
|
||||||
voters := GetVotersCount(poll)
|
voters := GetVotersCount(poll)
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -438,7 +438,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -485,7 +485,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetAll() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -659,7 +659,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetCreatedByAccount() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -706,7 +706,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetCreatedByAccount() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -880,7 +880,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetTargetAccount() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -927,7 +927,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetTargetAccount() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
|
|
@ -129,7 +129,7 @@ func (suite *ReportGetTestSuite) TestGetReport1() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
|
|
@ -154,7 +154,7 @@ func (suite *ReportsGetTestSuite) TestGetReports() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -244,7 +244,7 @@ func (suite *ReportsGetTestSuite) TestGetReports4() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -318,7 +318,7 @@ func (suite *ReportsGetTestSuite) TestGetReports6() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -376,7 +376,7 @@ func (suite *ReportsGetTestSuite) TestGetReports7() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (suite *BasicTestSuite) TestGetAllStatuses() {
|
||||||
s := []*gtsmodel.Status{}
|
s := []*gtsmodel.Status{}
|
||||||
err := suite.db.GetAll(context.Background(), &s)
|
err := suite.db.GetAll(context.Background(), &s)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Len(s, 21)
|
suite.Len(s, 22)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *BasicTestSuite) TestGetAllNotNull() {
|
func (suite *BasicTestSuite) TestGetAllNotNull() {
|
||||||
|
|
|
@ -53,7 +53,7 @@ func (suite *InstanceTestSuite) TestCountInstanceStatuses() {
|
||||||
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {
|
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {
|
||||||
count, err := suite.db.CountInstanceStatuses(context.Background(), "fossbros-anonymous.io")
|
count, err := suite.db.CountInstanceStatuses(context.Background(), "fossbros-anonymous.io")
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(2, count)
|
suite.Equal(3, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *InstanceTestSuite) TestCountInstanceDomains() {
|
func (suite *InstanceTestSuite) TestCountInstanceDomains() {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -737,27 +737,22 @@ func (d *Dereferencer) fetchStatusPoll(ctx context.Context, existing, status *gt
|
||||||
// no previous poll, insert new poll!
|
// no previous poll, insert new poll!
|
||||||
return insertStatusPoll(ctx, status)
|
return insertStatusPoll(ctx, status)
|
||||||
|
|
||||||
case /*existing.Poll != nil &&*/ status.Poll == nil:
|
case status.Poll == nil:
|
||||||
// existing poll has been deleted, remove this.
|
// existing poll has been deleted, remove this.
|
||||||
return deleteStatusPoll(ctx, existing.PollID)
|
return deleteStatusPoll(ctx, existing.PollID)
|
||||||
|
|
||||||
case /*existing.Poll != nil && status.Poll != nil && */
|
case pollChanged(existing.Poll, status.Poll):
|
||||||
!slices.Equal(existing.Poll.Options, status.Poll.Options) ||
|
|
||||||
!existing.Poll.ExpiresAt.Equal(status.Poll.ExpiresAt):
|
|
||||||
// poll has changed since original, delete and reinsert new.
|
// poll has changed since original, delete and reinsert new.
|
||||||
if err := deleteStatusPoll(ctx, existing.PollID); err != nil {
|
if err := deleteStatusPoll(ctx, existing.PollID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return insertStatusPoll(ctx, status)
|
return insertStatusPoll(ctx, status)
|
||||||
|
|
||||||
case /*existing.Poll != nil && status.Poll != nil && */
|
case pollUpdated(existing.Poll, status.Poll):
|
||||||
!existing.Poll.ClosedAt.Equal(status.Poll.ClosedAt) ||
|
|
||||||
!slices.Equal(existing.Poll.Votes, status.Poll.Votes) ||
|
|
||||||
existing.Poll.Voters != status.Poll.Voters:
|
|
||||||
// Since we last saw it, the poll has updated!
|
// Since we last saw it, the poll has updated!
|
||||||
// Whether that be stats, or close time.
|
// Whether that be stats, or close time.
|
||||||
poll := existing.Poll
|
poll := existing.Poll
|
||||||
poll.Closing = (!poll.Closed() && status.Poll.Closed())
|
poll.Closing = pollJustClosed(existing.Poll, status.Poll)
|
||||||
poll.ClosedAt = status.Poll.ClosedAt
|
poll.ClosedAt = status.Poll.ClosedAt
|
||||||
poll.Voters = status.Poll.Voters
|
poll.Voters = status.Poll.Voters
|
||||||
poll.Votes = status.Poll.Votes
|
poll.Votes = status.Poll.Votes
|
||||||
|
|
|
@ -17,6 +17,12 @@
|
||||||
|
|
||||||
package dereferencing
|
package dereferencing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// doOnce wraps a function to only perform it once.
|
// doOnce wraps a function to only perform it once.
|
||||||
func doOnce(fn func()) func() {
|
func doOnce(fn func()) func() {
|
||||||
var once int32
|
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()
|
||||||
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ type Poll struct {
|
||||||
Voters *int `bun:",nullzero,notnull"` // Total no. voters count.
|
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.
|
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).
|
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.
|
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, will be zerotime until 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.
|
Closing bool `bun:"-"` // An ephemeral field only set on Polls in the middle of closing.
|
||||||
// no creation date, use attached Status.CreatedAt.
|
// no creation date, use attached Status.CreatedAt.
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (suite *PruneTestSuite) TestPrune() {
|
||||||
|
|
||||||
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
|
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(16, pruned)
|
suite.Equal(17, pruned)
|
||||||
suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
|
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)
|
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(16, pruned)
|
suite.Equal(17, pruned)
|
||||||
suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
|
suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
|
||||||
|
|
||||||
// Prune same again, nothing should be pruned this time.
|
// 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)
|
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(21, pruned)
|
suite.Equal(22, pruned)
|
||||||
suite.Equal(0, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
|
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)
|
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(0, pruned)
|
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) {
|
func TestPruneTestSuite(t *testing.T) {
|
||||||
|
|
|
@ -708,8 +708,10 @@ func (c *Converter) addPollToAS(ctx context.Context, poll *gtsmodel.Poll, dst ap
|
||||||
optionsProp.AppendActivityStreamsNote(note)
|
optionsProp.AppendActivityStreamsNote(note)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set poll endTime property.
|
if !poll.ExpiresAt.IsZero() {
|
||||||
ap.SetEndTime(dst, poll.ExpiresAt)
|
// Set poll endTime property.
|
||||||
|
ap.SetEndTime(dst, poll.ExpiresAt)
|
||||||
|
}
|
||||||
|
|
||||||
if !poll.ClosedAt.IsZero() {
|
if !poll.ClosedAt.IsZero() {
|
||||||
// Poll is closed, set closed property.
|
// Poll is closed, set closed property.
|
||||||
|
|
|
@ -1367,6 +1367,7 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou
|
||||||
voted *bool
|
voted *bool
|
||||||
ownChoices *[]int
|
ownChoices *[]int
|
||||||
isAuthor bool
|
isAuthor bool
|
||||||
|
expiresAt string
|
||||||
emojis []apimodel.Emoji
|
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.
|
// TODO: emojis used in poll options.
|
||||||
// For now init to empty slice to serialize as `[]`.
|
// For now init to empty slice to serialize as `[]`.
|
||||||
// In future inherit from parent status.
|
// In future inherit from parent status.
|
||||||
|
@ -1435,7 +1441,7 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou
|
||||||
|
|
||||||
return &apimodel.Poll{
|
return &apimodel.Poll{
|
||||||
ID: poll.ID,
|
ID: poll.ID,
|
||||||
ExpiresAt: util.FormatISO8601(poll.ExpiresAt),
|
ExpiresAt: expiresAt,
|
||||||
Expired: poll.Closed(),
|
Expired: poll.Closed(),
|
||||||
Multiple: (*poll.Multiple),
|
Multiple: (*poll.Multiple),
|
||||||
VotesCount: totalVotes,
|
VotesCount: totalVotes,
|
||||||
|
|
|
@ -1010,7 +1010,7 @@ func (suite *InternalToFrontendTestSuite) TestReportToFrontend1() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -1127,7 +1127,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend1() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -1395,7 +1395,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend2() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -1442,7 +1442,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontend2() {
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
@ -1573,7 +1573,7 @@ func (suite *InternalToFrontendTestSuite) TestAdminReportToFrontendSuspendedLoca
|
||||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 2,
|
"statuses_count": 3,
|
||||||
"last_status_at": "2021-09-11T09:40:37.000Z",
|
"last_status_at": "2021-09-11T09:40:37.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
|
|
|
@ -1916,8 +1916,8 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
},
|
},
|
||||||
"remote_account_1_status_2": {
|
"remote_account_1_status_2": {
|
||||||
ID: "01HEN2QRFA8H3C6QPN7RD4KSR6",
|
ID: "01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||||
URI: "http://fossbros-anonymous.io/users/foss_satan/statuses/065TKDN4BX1PC8N19TSY9SD2N4",
|
URI: "http://fossbros-anonymous.io/users/foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||||
URL: "http://fossbros-anonymous.io/@foss_satan/statuses/065TKDN4BX1PC8N19TSY9SD2N4",
|
URL: "http://fossbros-anonymous.io/@foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||||
Content: "what products should i buy at the grocery store?",
|
Content: "what products should i buy at the grocery store?",
|
||||||
AttachmentIDs: []string{"01FVW7RXPQ8YJHTEXYPE7Q8ZY0"},
|
AttachmentIDs: []string{"01FVW7RXPQ8YJHTEXYPE7Q8ZY0"},
|
||||||
CreatedAt: TimeMustParse("2021-09-11T11:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-09-11T11:40:37+02:00"),
|
||||||
|
@ -1941,6 +1941,33 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: ap.ActivityQuestion,
|
ActivityStreamsType: ap.ActivityQuestion,
|
||||||
PollID: "01HEN2R65468ZG657C4ZPHJ4EX",
|
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": {
|
"remote_account_2_status_1": {
|
||||||
ID: "01HE7XJ1CG84TBKH5V9XKBVGF5",
|
ID: "01HE7XJ1CG84TBKH5V9XKBVGF5",
|
||||||
URI: "http://example.org/users/Some_User/statuses/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"),
|
ClosedAt: TimeMustParse("2021-09-11T12:40:37+02:00"),
|
||||||
Closing: false,
|
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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue