fix timeline tests now that timelines are returned in page order

This commit is contained in:
kim 2025-04-07 19:22:35 +01:00
commit 72fcf8f15f
2 changed files with 94 additions and 110 deletions

View file

@ -20,10 +20,8 @@ package bundb_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
@ -35,38 +33,6 @@ type TimelineTestSuite struct {
BunDBStandardTestSuite
}
func getFutureStatus() *gtsmodel.Status {
theDistantFuture := time.Now().Add(876600 * time.Hour)
id := id.NewULIDFromTime(theDistantFuture)
return &gtsmodel.Status{
ID: id,
URI: "http://localhost:8080/users/admin/statuses/" + id,
URL: "http://localhost:8080/@admin/statuses/" + id,
Content: "it's the future, wooooooooooooooooooooooooooooooooo",
Text: "it's the future, wooooooooooooooooooooooooooooooooo",
ContentType: gtsmodel.StatusContentTypePlain,
AttachmentIDs: []string{},
TagIDs: []string{},
MentionIDs: []string{},
EmojiIDs: []string{},
CreatedAt: theDistantFuture,
Local: util.Ptr(true),
AccountURI: "http://localhost:8080/users/admin",
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
InReplyToID: "",
BoostOfID: "",
ContentWarning: "",
Visibility: gtsmodel.VisibilityPublic,
Sensitive: util.Ptr(false),
Language: "en",
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
Federated: util.Ptr(true),
InteractionPolicy: gtsmodel.DefaultInteractionPolicyPublic(),
ActivityStreamsType: ap.ObjectNote,
}
}
func (suite *TimelineTestSuite) publicCount() int {
var publicCount int
for _, status := range suite.testStatuses {
@ -92,7 +58,7 @@ func (suite *TimelineTestSuite) localCount() int {
return localCount
}
func (suite *TimelineTestSuite) checkStatuses(statuses []*gtsmodel.Status, maxID string, minID string, expectedLength int) {
func (suite *TimelineTestSuite) checkStatuses(statuses []*gtsmodel.Status, maxID string, minID string, expectedOrder paging.Order, expectedLength int) {
if l := len(statuses); l != expectedLength {
suite.FailNowf("", "expected %d statuses in slice, got %d", expectedLength, l)
} else if l == 0 {
@ -100,6 +66,27 @@ func (suite *TimelineTestSuite) checkStatuses(statuses []*gtsmodel.Status, maxID
return
}
if expectedOrder.Ascending() {
// Check ordering + bounds of statuses.
lowest := statuses[0].ID
for _, status := range statuses {
id := status.ID
if id >= maxID {
suite.FailNowf("", "%s greater than maxID %s", id, maxID)
}
if id <= minID {
suite.FailNowf("", "%s smaller than minID %s", id, minID)
}
if id < lowest {
suite.FailNowf("", "statuses in slice were not ordered lowest -> highest ID")
}
lowest = id
}
} else {
// Check ordering + bounds of statuses.
highest := statuses[0].ID
for _, status := range statuses {
@ -119,28 +106,33 @@ func (suite *TimelineTestSuite) checkStatuses(statuses []*gtsmodel.Status, maxID
highest = id
}
}
}
func (suite *TimelineTestSuite) TestGetPublicTimeline() {
ctx := context.Background()
s, err := suite.db.GetPublicTimeline(ctx, toPage("", "", "", 20))
page := toPage("", "", "", 20)
s, err := suite.db.GetPublicTimeline(ctx, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, suite.publicCount())
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), suite.publicCount())
}
func (suite *TimelineTestSuite) TestGetPublicTimelineLocal() {
ctx := context.Background()
s, err := suite.db.GetLocalTimeline(ctx, toPage("", "", "", 20))
page := toPage("", "", "", 20)
s, err := suite.db.GetLocalTimeline(ctx, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, suite.localCount())
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), suite.localCount())
}
func (suite *TimelineTestSuite) TestGetHomeTimeline() {
@ -149,12 +141,14 @@ func (suite *TimelineTestSuite) TestGetHomeTimeline() {
viewingAccount = suite.testAccounts["local_account_1"]
)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", "", 20))
page := toPage("", "", "", 20)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 20)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 20)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineIgnoreExclusive() {
@ -174,13 +168,15 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineIgnoreExclusive() {
suite.FailNow(err.Error())
}
page := toPage("", "", "", 20)
// First try with list just set to exclusive.
// We should only get zork's own statuses.
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", "", 20))
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 9)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 9)
// Remove admin account from the exclusive list.
listEntry := suite.testListEntries["local_account_1_list_1_entry_2"]
@ -190,11 +186,11 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineIgnoreExclusive() {
// Zork should only see their own
// statuses and admin's statuses now.
s, err = suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", "", 20))
s, err = suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 13)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 13)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
@ -219,36 +215,16 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
}
}
page := toPage("", "", "", 20)
// Query should work fine; though far
// fewer statuses will be returned ofc.
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", "", 20))
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 9)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {
var (
ctx = context.Background()
viewingAccount = suite.testAccounts["local_account_1"]
)
// Insert a status set far in the
// future, it shouldn't be retrieved.
futureStatus := getFutureStatus()
if err := suite.db.PutStatus(ctx, futureStatus); err != nil {
suite.FailNow(err.Error())
}
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", "", 20))
if err != nil {
suite.FailNow(err.Error())
}
suite.NotContains(s, futureStatus)
suite.checkStatuses(s, id.Highest, id.Lowest, 20)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 9)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() {
@ -257,14 +233,16 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() {
viewingAccount = suite.testAccounts["local_account_1"]
)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage("", "", id.Lowest, 5))
page := toPage("", "", id.Lowest, 5)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 5)
suite.Equal("01F8MHAYFKS4KMXF8K5Y1C0KRN", s[0].ID)
suite.Equal("01F8MH75CBF9JFX4ZAD54N0W0R", s[len(s)-1].ID)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 5)
suite.Equal("01F8MHAYFKS4KMXF8K5Y1C0KRN", s[len(s)-1].ID)
suite.Equal("01F8MH75CBF9JFX4ZAD54N0W0R", s[0].ID)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineFromHighest() {
@ -273,12 +251,14 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineFromHighest() {
viewingAccount = suite.testAccounts["local_account_1"]
)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, toPage(id.Highest, "", "", 5))
page := toPage(id.Highest, "", "", 5)
s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 5)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 5)
suite.Equal("01JDPZEZ77X1NX0TY9M10BK1HM", s[0].ID)
suite.Equal("01HEN2RZ8BG29Y5Z9VJC73HZW7", s[len(s)-1].ID)
}
@ -289,12 +269,14 @@ func (suite *TimelineTestSuite) TestGetListTimelineNoParams() {
list = suite.testLists["local_account_1_list_1"]
)
s, err := suite.db.GetListTimeline(ctx, list.ID, toPage("", "", "", 20))
page := toPage("", "", "", 20)
s, err := suite.db.GetListTimeline(ctx, list.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 13)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 13)
}
func (suite *TimelineTestSuite) TestGetListTimelineMaxID() {
@ -303,12 +285,14 @@ func (suite *TimelineTestSuite) TestGetListTimelineMaxID() {
list = suite.testLists["local_account_1_list_1"]
)
s, err := suite.db.GetListTimeline(ctx, list.ID, toPage(id.Highest, "", "", 5))
page := toPage(id.Highest, "", "", 5)
s, err := suite.db.GetListTimeline(ctx, list.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 5)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 5)
suite.Equal("01JDPZEZ77X1NX0TY9M10BK1HM", s[0].ID)
suite.Equal("01FN3VJGFH10KR7S2PB0GFJZYG", s[len(s)-1].ID)
}
@ -319,14 +303,16 @@ func (suite *TimelineTestSuite) TestGetListTimelineMinID() {
list = suite.testLists["local_account_1_list_1"]
)
s, err := suite.db.GetListTimeline(ctx, list.ID, toPage("", "", id.Lowest, 5))
page := toPage("", "", id.Lowest, 5)
s, err := suite.db.GetListTimeline(ctx, list.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 5)
suite.Equal("01F8MHC8VWDRBQR0N1BATDDEM5", s[0].ID)
suite.Equal("01F8MH75CBF9JFX4ZAD54N0W0R", s[len(s)-1].ID)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 5)
suite.Equal("01F8MHC8VWDRBQR0N1BATDDEM5", s[len(s)-1].ID)
suite.Equal("01F8MH75CBF9JFX4ZAD54N0W0R", s[0].ID)
}
func (suite *TimelineTestSuite) TestGetListTimelineMinIDPagingUp() {
@ -335,14 +321,16 @@ func (suite *TimelineTestSuite) TestGetListTimelineMinIDPagingUp() {
list = suite.testLists["local_account_1_list_1"]
)
s, err := suite.db.GetListTimeline(ctx, list.ID, toPage("", "", "01F8MHC8VWDRBQR0N1BATDDEM5", 5))
page := toPage("", "", "01F8MHC8VWDRBQR0N1BATDDEM5", 5)
s, err := suite.db.GetListTimeline(ctx, list.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, "01F8MHC8VWDRBQR0N1BATDDEM5", 5)
suite.Equal("01G20ZM733MGN8J344T4ZDDFY1", s[0].ID)
suite.Equal("01F8MHCP5P2NWYQ416SBA0XSEV", s[len(s)-1].ID)
suite.checkStatuses(s, id.Highest, "01F8MHC8VWDRBQR0N1BATDDEM5", page.Order(), 5)
suite.Equal("01G20ZM733MGN8J344T4ZDDFY1", s[len(s)-1].ID)
suite.Equal("01F8MHCP5P2NWYQ416SBA0XSEV", s[0].ID)
}
func (suite *TimelineTestSuite) TestGetTagTimelineNoParams() {
@ -351,12 +339,14 @@ func (suite *TimelineTestSuite) TestGetTagTimelineNoParams() {
tag = suite.testTags["welcome"]
)
s, err := suite.db.GetTagTimeline(ctx, tag.ID, toPage("", "", "", 1))
page := toPage("", "", "", 1)
s, err := suite.db.GetTagTimeline(ctx, tag.ID, page)
if err != nil {
suite.FailNow(err.Error())
}
suite.checkStatuses(s, id.Highest, id.Lowest, 1)
suite.checkStatuses(s, id.Highest, id.Lowest, page.Order(), 1)
suite.Equal("01F8MH75CBF9JFX4ZAD54N0W0R", s[0].ID)
}

View file

@ -27,17 +27,13 @@ import (
// Timeline contains functionality for retrieving home/public/faved etc timelines for an account.
type Timeline interface {
// GetHomeTimeline returns a slice of statuses from accounts that are followed by the given account id.
//
// Statuses should be returned in descending order of when they were created (newest first).
GetHomeTimeline(ctx context.Context, accountID string, page *paging.Page) ([]*gtsmodel.Status, error)
// GetPublicTimeline fetches the account's PUBLIC timeline -- ie., posts and replies that are public.
// It will use the given filters and try to return as many statuses as possible up to the limit.
//
// Statuses should be returned in descending order of when they were created (newest first).
GetPublicTimeline(ctx context.Context, page *paging.Page) ([]*gtsmodel.Status, error)
// GetLocalTimeline ...
// GetLocalTimeline fetches the account's LOCAL timeline -- i.e. PUBLIC posts by LOCAL users.
GetLocalTimeline(ctx context.Context, page *paging.Page) ([]*gtsmodel.Status, error)
// GetFavedTimeline fetches the account's FAVED timeline -- ie., posts and replies that the requesting account has faved.
@ -50,10 +46,8 @@ type Timeline interface {
GetFavedTimeline(ctx context.Context, accountID string, maxID string, minID string, limit int) ([]*gtsmodel.Status, string, string, error)
// GetListTimeline returns a slice of statuses from followed accounts collected within the list with the given listID.
// Statuses should be returned in descending order of when they were created (newest first).
GetListTimeline(ctx context.Context, listID string, page *paging.Page) ([]*gtsmodel.Status, error)
// GetTagTimeline returns a slice of public-visibility statuses that use the given tagID.
// Statuses should be returned in descending order of when they were created (newest first).
GetTagTimeline(ctx context.Context, tagID string, page *paging.Page) ([]*gtsmodel.Status, error)
}