[bugfix] Ensure pre renders as expected, fix orderedCollectionPage (#2434)

This commit is contained in:
tobi 2023-12-10 12:36:00 +01:00 committed by GitHub
parent cc91ea057d
commit d60edf7ec6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 130 additions and 78 deletions

View file

@ -35,13 +35,15 @@
// Currently, the following things will be custom serialized: // Currently, the following things will be custom serialized:
// //
// - OrderedCollection: 'orderedItems' property will always be made into an array. // - OrderedCollection: 'orderedItems' property will always be made into an array.
// - OrderedCollectionPage: 'orderedItems' property will always be made into an array.
// - Any Accountable type: 'attachment' property will always be made into an array. // - Any Accountable type: 'attachment' property will always be made into an array.
// - Any Statusable type: 'attachment' property will always be made into an array; 'content' and 'contentMap' will be normalized. // - Any Statusable type: 'attachment' property will always be made into an array; 'content' and 'contentMap' will be normalized.
// - Any Activityable type: any 'object's set on an activity will be custom serialized as above. // - Any Activityable type: any 'object's set on an activity will be custom serialized as above.
func Serialize(t vocab.Type) (m map[string]interface{}, e error) { func Serialize(t vocab.Type) (m map[string]interface{}, e error) {
switch tn := t.GetTypeName(); { switch tn := t.GetTypeName(); {
case tn == ObjectOrderedCollection: case tn == ObjectOrderedCollection ||
return serializeOrderedCollection(t) tn == ObjectOrderedCollectionPage:
return serializeWithOrderedItems(t)
case IsAccountable(tn): case IsAccountable(tn):
return serializeAccountable(t, true) return serializeAccountable(t, true)
case IsStatusable(tn): case IsStatusable(tn):
@ -54,16 +56,17 @@ func Serialize(t vocab.Type) (m map[string]interface{}, e error) {
} }
} }
// serializeOrderedCollection is a custom serializer for an ActivityStreamsOrderedCollection. // serializeWithOrderedItems is a custom serializer
// Unlike the standard streams.Serialize function, this serializer normalizes the orderedItems // for any type that has an `orderedItems` property.
// value to always be an array/slice, regardless of how many items are contained therein. // Unlike the standard streams.Serialize function,
// // this serializer normalizes the orderedItems
// TODO: Remove this function if we can fix the underlying issue in Go-Fed. // value to always be an array/slice, regardless
// of how many items are contained therein.
// //
// See: // See:
// - https://github.com/go-fed/activity/issues/139 // - https://github.com/go-fed/activity/issues/139
// - https://github.com/mastodon/mastodon/issues/24225 // - https://github.com/mastodon/mastodon/issues/24225
func serializeOrderedCollection(t vocab.Type) (map[string]interface{}, error) { func serializeWithOrderedItems(t vocab.Type) (map[string]interface{}, error) {
data, err := streams.Serialize(t) data, err := streams.Serialize(t)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -140,16 +140,26 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": "http://localhost:8080/users/the_mighty_zork/outbox?page=true", "id": "http://localhost:8080/users/the_mighty_zork/outbox?page=true",
"next": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026max_id=01F8MHAMCHF6Y650WCRSCP4WMY", "next": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026max_id=01F8MHAMCHF6Y650WCRSCP4WMY",
"orderedItems": { "orderedItems": [
"actor": "http://localhost:8080/users/the_mighty_zork", {
"cc": "http://localhost:8080/users/the_mighty_zork/followers", "actor": "http://localhost:8080/users/the_mighty_zork",
"id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/activity#Create", "cc": "http://localhost:8080/users/the_mighty_zork/followers",
"object": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY", "id": "http://localhost:8080/users/the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40/activity#Create",
"to": "https://www.w3.org/ns/activitystreams#Public", "object": "http://localhost:8080/users/the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40",
"type": "Create" "to": "https://www.w3.org/ns/activitystreams#Public",
}, "type": "Create"
},
{
"actor": "http://localhost:8080/users/the_mighty_zork",
"cc": "http://localhost:8080/users/the_mighty_zork/followers",
"id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/activity#Create",
"object": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
"to": "https://www.w3.org/ns/activitystreams#Public",
"type": "Create"
}
],
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox", "partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
"prev": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026min_id=01F8MHAMCHF6Y650WCRSCP4WMY", "prev": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026min_id=01HH9KYNQPA416TNJ53NSATP40",
"type": "OrderedCollectionPage" "type": "OrderedCollectionPage"
}`, dst.String()) }`, dst.String())
@ -237,22 +247,32 @@ func checkDropPublished(t *testing.T, b []byte, at ...string) []byte {
if err := json.Unmarshal(b, &m); err != nil { if err := json.Unmarshal(b, &m); err != nil {
t.Fatalf("error unmarshaling json into map: %v", err) t.Fatalf("error unmarshaling json into map: %v", err)
} }
mm := m
entries := make([]map[string]any, 0)
for _, key := range at { for _, key := range at {
switch vt := mm[key].(type) { switch vt := m[key].(type) {
case map[string]any: case []interface{}:
mm = vt for _, t := range vt {
if entry, ok := t.(map[string]any); ok {
entries = append(entries, entry)
}
}
} }
} }
if s, ok := mm["published"].(string); !ok {
t.Fatal("missing published data on json") for _, entry := range entries {
} else if _, err := time.Parse(time.RFC3339, s); err != nil { if s, ok := entry["published"].(string); !ok {
t.Fatalf("error parsing published time: %v", err) t.Fatal("missing published data on json")
} else if _, err := time.Parse(time.RFC3339, s); err != nil {
t.Fatalf("error parsing published time: %v", err)
}
delete(entry, "published")
} }
delete(mm, "published")
b, err := json.Marshal(m) b, err := json.Marshal(m)
if err != nil { if err != nil {
t.Fatalf("error remarshaling json: %v", err) t.Fatalf("error remarshaling json: %v", err)
} }
return b return b
} }

View file

@ -163,7 +163,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
"partOf": targetStatus.URI + "/replies?only_other_accounts=false", "partOf": targetStatus.URI + "/replies?only_other_accounts=false",
"next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&min_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false", "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&min_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
"prev": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&max_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false", "prev": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&max_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
"orderedItems": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0", "orderedItems": []string{"http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0"},
"totalItems": 1, "totalItems": 1,
}) })
assert.Equal(suite.T(), expect, string(b)) assert.Equal(suite.T(), expect, string(b))

View file

@ -79,7 +79,7 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
suite.Equal("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", apimodelAccount.HeaderStatic) suite.Equal("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", apimodelAccount.HeaderStatic)
suite.Equal(2, apimodelAccount.FollowersCount) suite.Equal(2, apimodelAccount.FollowersCount)
suite.Equal(2, apimodelAccount.FollowingCount) suite.Equal(2, apimodelAccount.FollowingCount)
suite.Equal(6, apimodelAccount.StatusesCount) suite.Equal(7, apimodelAccount.StatusesCount)
suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy) suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)
suite.Equal(testAccount.Language, apimodelAccount.Source.Language) suite.Equal(testAccount.Language, apimodelAccount.Source.Language)
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note) suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)

View file

@ -133,7 +133,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",
@ -250,7 +250,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",
@ -367,7 +367,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",
@ -535,7 +535,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",
@ -674,7 +674,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+` "thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+`
@ -828,7 +828,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",

View file

@ -877,7 +877,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() {
} }
suite.Len(searchResult.Accounts, 5) suite.Len(searchResult.Accounts, 5)
suite.Len(searchResult.Statuses, 5) suite.Len(searchResult.Statuses, 6)
suite.Len(searchResult.Hashtags, 0) suite.Len(searchResult.Hashtags, 0)
} }
@ -918,7 +918,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() {
} }
suite.Len(searchResult.Accounts, 2) suite.Len(searchResult.Accounts, 2)
suite.Len(searchResult.Statuses, 5) suite.Len(searchResult.Statuses, 6)
suite.Len(searchResult.Hashtags, 0) suite.Len(searchResult.Hashtags, 0)
} }
@ -959,7 +959,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {
} }
suite.Len(searchResult.Accounts, 0) suite.Len(searchResult.Accounts, 0)
suite.Len(searchResult.Statuses, 5) suite.Len(searchResult.Statuses, 6)
suite.Len(searchResult.Hashtags, 0) suite.Len(searchResult.Hashtags, 0)
} }

View file

@ -130,8 +130,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [], "emojis": [],
"fields": [], "fields": [],
"enable_rss": true, "enable_rss": true,
@ -193,8 +193,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [], "emojis": [],
"fields": [], "fields": [],
"enable_rss": true, "enable_rss": true,

View file

@ -42,33 +42,33 @@ type AccountTestSuite struct {
func (suite *AccountTestSuite) TestGetAccountStatuses() { func (suite *AccountTestSuite) TestGetAccountStatuses() {
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false) statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false)
suite.NoError(err) suite.NoError(err)
suite.Len(statuses, 6) suite.Len(statuses, 7)
} }
func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() { func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {
// get the first page // get the first page
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 2, false, false, "", "", false, false) statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, "", "", false, false)
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.Len(statuses, 2) suite.Len(statuses, 3)
// get the second page // get the second page
statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 2, false, false, statuses[len(statuses)-1].ID, "", false, false) statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, statuses[len(statuses)-1].ID, "", false, false)
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.Len(statuses, 2) suite.Len(statuses, 3)
// get the third page // get the third page
statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 2, false, false, statuses[len(statuses)-1].ID, "", false, false) statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, statuses[len(statuses)-1].ID, "", false, false)
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.Len(statuses, 2) suite.Len(statuses, 1)
// try to get the last page (should be empty) // try to get the last page (should be empty)
statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 2, false, false, statuses[len(statuses)-1].ID, "", false, false) statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, statuses[len(statuses)-1].ID, "", false, false)
suite.ErrorIs(err, db.ErrNoEntries) suite.ErrorIs(err, db.ErrNoEntries)
suite.Empty(statuses) suite.Empty(statuses)
} }
@ -76,13 +76,13 @@ func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {
func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogs() { func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogs() {
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false) statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false)
suite.NoError(err) suite.NoError(err)
suite.Len(statuses, 6) suite.Len(statuses, 7)
} }
func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() { func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() {
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true) statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true)
suite.NoError(err) suite.NoError(err)
suite.Len(statuses, 1) suite.Len(statuses, 2)
} }
func (suite *AccountTestSuite) TestGetAccountStatusesMediaOnly() { func (suite *AccountTestSuite) TestGetAccountStatusesMediaOnly() {
@ -306,13 +306,13 @@ func (suite *AccountTestSuite) TestUpdateAccount() {
func (suite *AccountTestSuite) TestGetAccountLastPosted() { func (suite *AccountTestSuite) TestGetAccountLastPosted() {
lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, false) lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, false)
suite.NoError(err) suite.NoError(err)
suite.EqualValues(1653046870, lastPosted.Unix()) suite.EqualValues(1702200240, lastPosted.Unix())
} }
func (suite *AccountTestSuite) TestGetAccountLastPostedWebOnly() { func (suite *AccountTestSuite) TestGetAccountLastPostedWebOnly() {
lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, true) lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, true)
suite.NoError(err) suite.NoError(err)
suite.EqualValues(1634726437, lastPosted.Unix()) suite.EqualValues(1702200240, lastPosted.Unix())
} }
func (suite *AccountTestSuite) TestInsertAccountWithDefaults() { func (suite *AccountTestSuite) TestInsertAccountWithDefaults() {

View file

@ -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, 22) suite.Len(s, 23)
} }
func (suite *BasicTestSuite) TestGetAllNotNull() { func (suite *BasicTestSuite) TestGetAllNotNull() {

View file

@ -47,7 +47,7 @@ func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {
func (suite *InstanceTestSuite) TestCountInstanceStatuses() { func (suite *InstanceTestSuite) TestCountInstanceStatuses() {
count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost()) count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost())
suite.NoError(err) suite.NoError(err)
suite.Equal(18, count) suite.Equal(19, count)
} }
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() { func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {

View file

@ -157,7 +157,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimeline() {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.checkStatuses(s, id.Highest, id.Lowest, 18) suite.checkStatuses(s, id.Highest, id.Lowest, 19)
} }
func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() { func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
@ -189,7 +189,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.checkStatuses(s, id.Highest, id.Lowest, 6) suite.checkStatuses(s, id.Highest, id.Lowest, 7)
} }
func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() { func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {
@ -211,7 +211,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {
} }
suite.NotContains(s, futureStatus) suite.NotContains(s, futureStatus)
suite.checkStatuses(s, id.Highest, id.Lowest, 18) suite.checkStatuses(s, id.Highest, id.Lowest, 19)
} }
func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() { func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() {
@ -242,8 +242,8 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineFromHighest() {
} }
suite.checkStatuses(s, id.Highest, id.Lowest, 5) suite.checkStatuses(s, id.Highest, id.Lowest, 5)
suite.Equal("01HEN2RZ8BG29Y5Z9VJC73HZW7", s[0].ID) suite.Equal("01HH9KYNQPA416TNJ53NSATP40", s[0].ID)
suite.Equal("01FN3VJGFH10KR7S2PB0GFJZYG", s[len(s)-1].ID) suite.Equal("01G20ZM733MGN8J344T4ZDDFY1", s[len(s)-1].ID)
} }
func (suite *TimelineTestSuite) TestGetListTimelineNoParams() { func (suite *TimelineTestSuite) TestGetListTimelineNoParams() {

View file

@ -45,14 +45,14 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() {
func (suite *GetRSSTestSuite) TestGetAccountRSSZork() { func (suite *GetRSSTestSuite) TestGetAccountRSSZork() {
getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "the_mighty_zork") getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "the_mighty_zork")
suite.NoError(err) suite.NoError(err)
suite.EqualValues(1634726437, lastModified.Unix()) suite.EqualValues(1702200240, lastModified.Unix())
feed, err := getFeed() feed, err := getFeed()
suite.NoError(err) suite.NoError(err)
fmt.Println(feed) fmt.Println(feed)
suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>Posts from @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n <description>Posts from @the_mighty_zork@localhost:8080</description>\n <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n <lastBuildDate>Wed, 20 Oct 2021 10:40:37 +0000</lastBuildDate>\n <image>\n <url>http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpg</url>\n <title>Avatar for @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n </image>\n <item>\n <title>introduction post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;hello everyone!&#34;</description>\n <content:encoded><![CDATA[hello everyone!]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>\n <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n </channel>\n</rss>", feed) suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>Posts from @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n <description>Posts from @the_mighty_zork@localhost:8080</description>\n <pubDate>Sun, 10 Dec 2023 09:24:00 +0000</pubDate>\n <lastBuildDate>Sun, 10 Dec 2023 09:24:00 +0000</lastBuildDate>\n <image>\n <url>http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpg</url>\n <title>Avatar for @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n </image>\n <item>\n <title>HTML in post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;Here&#39;s a bunch of HTML, read it and weep, weep then!&#xA;&#xA;```html&#xA;&lt;section class=&#34;about-user&#34;&gt;&#xA; &lt;div class=&#34;col-header&#34;&gt;&#xA; &lt;h2&gt;About&lt;/h2&gt;&#xA; &lt;/div&gt; &#xA; &lt;div class=&#34;fields&#34;&gt;&#xA; &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;&#xA; &lt;dl&gt;&#xA;...</description>\n <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\">&lt;section class=&#34;about-user&#34;&gt;\n &lt;div class=&#34;col-header&#34;&gt;\n &lt;h2&gt;About&lt;/h2&gt;\n &lt;/div&gt; \n &lt;div class=&#34;fields&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;\n &lt;dl&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;should you follow me?&lt;/dt&gt;\n &lt;dd&gt;maybe!&lt;/dd&gt;\n &lt;/div&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;age&lt;/dt&gt;\n &lt;dd&gt;120&lt;/dd&gt;\n &lt;/div&gt;\n &lt;/dl&gt;\n &lt;/div&gt;\n &lt;div class=&#34;bio&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Bio&lt;/h3&gt;\n &lt;p&gt;i post about things that concern me&lt;/p&gt;\n &lt;/div&gt;\n &lt;div class=&#34;sr-only&#34; role=&#34;group&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Stats&lt;/h3&gt;\n &lt;span&gt;Joined in Jun, 2022.&lt;/span&gt;\n &lt;span&gt;8 posts.&lt;/span&gt;\n &lt;span&gt;Followed by 1.&lt;/span&gt;\n &lt;span&gt;Following 1.&lt;/span&gt;\n &lt;/div&gt;\n &lt;div class=&#34;accountstats&#34; aria-hidden=&#34;true&#34;&gt;\n &lt;b&gt;Joined&lt;/b&gt;&lt;time datetime=&#34;2022-06-04T13:12:00.000Z&#34;&gt;Jun, 2022&lt;/time&gt;\n &lt;b&gt;Posts&lt;/b&gt;&lt;span&gt;8&lt;/span&gt;\n &lt;b&gt;Followed by&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;b&gt;Following&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;/div&gt;\n&lt;/section&gt;\n</code></pre><p>There, hope you liked that!</p>]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid>http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</guid>\n <pubDate>Sun, 10 Dec 2023 09:24:00 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n <item>\n <title>introduction post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;hello everyone!&#34;</description>\n <content:encoded><![CDATA[hello everyone!]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>\n <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n </channel>\n</rss>", feed)
} }
func (suite *GetRSSTestSuite) TestGetAccountRSSZorkNoPosts() { func (suite *GetRSSTestSuite) TestGetAccountRSSZorkNoPosts() {

View file

@ -228,7 +228,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossible() {
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.checkStatuses(statuses, id.Highest, id.Lowest, 18) suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)
} }
func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() { func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {
@ -255,7 +255,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.checkStatuses(statuses, id.Highest, id.Lowest, 18) suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)
} }
func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() { func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {
@ -284,7 +284,7 @@ func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {
if err != nil { if err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.checkStatuses(statuses, id.Highest, id.Lowest, 6) suite.checkStatuses(statuses, id.Highest, id.Lowest, 7)
for _, s := range statuses { for _, s := range statuses {
if s.GetAccountID() != testAccount.ID { if s.GetAccountID() != testAccount.ID {

View file

@ -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(17, pruned) suite.Equal(18, 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(17, pruned) suite.Equal(18, 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(22, pruned) suite.Equal(23, 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(22, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID)) suite.Equal(23, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
} }
func TestPruneTestSuite(t *testing.T) { func TestPruneTestSuite(t *testing.T) {

View file

@ -58,8 +58,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontend() {
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [], "emojis": [],
"fields": [], "fields": [],
"enable_rss": true, "enable_rss": true,
@ -100,8 +100,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiStruct()
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [ "emojis": [
{ {
"shortcode": "rainbow", "shortcode": "rainbow",
@ -148,8 +148,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiIDs() {
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [ "emojis": [
{ {
"shortcode": "rainbow", "shortcode": "rainbow",
@ -192,8 +192,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendSensitive() {
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2, "followers_count": 2,
"following_count": 2, "following_count": 2,
"statuses_count": 6, "statuses_count": 7,
"last_status_at": "2022-05-20T11:41:10.000Z", "last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [], "emojis": [],
"fields": [], "fields": [],
"source": { "source": {
@ -896,7 +896,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
}, },
"stats": { "stats": {
"domain_count": 2, "domain_count": 2,
"status_count": 18, "status_count": 19,
"user_count": 4 "user_count": 4
}, },
"thumbnail": "http://localhost:8080/assets/logo.png", "thumbnail": "http://localhost:8080/assets/logo.png",

View file

@ -1676,6 +1676,31 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
ActivityStreamsType: ap.ActivityQuestion, ActivityStreamsType: ap.ActivityQuestion,
PollID: "01HEN2RKT1YTEZ80SA8HGP105F", PollID: "01HEN2RKT1YTEZ80SA8HGP105F",
}, },
"local_account_1_status_7": {
ID: "01HH9KYNQPA416TNJ53NSATP40",
URI: "http://localhost:8080/users/the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40",
URL: "http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40",
Content: "<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\">&lt;section class=&#34;about-user&#34;&gt;\n &lt;div class=&#34;col-header&#34;&gt;\n &lt;h2&gt;About&lt;/h2&gt;\n &lt;/div&gt; \n &lt;div class=&#34;fields&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;\n &lt;dl&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;should you follow me?&lt;/dt&gt;\n &lt;dd&gt;maybe!&lt;/dd&gt;\n &lt;/div&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;age&lt;/dt&gt;\n &lt;dd&gt;120&lt;/dd&gt;\n &lt;/div&gt;\n &lt;/dl&gt;\n &lt;/div&gt;\n &lt;div class=&#34;bio&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Bio&lt;/h3&gt;\n &lt;p&gt;i post about things that concern me&lt;/p&gt;\n &lt;/div&gt;\n &lt;div class=&#34;sr-only&#34; role=&#34;group&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Stats&lt;/h3&gt;\n &lt;span&gt;Joined in Jun, 2022.&lt;/span&gt;\n &lt;span&gt;8 posts.&lt;/span&gt;\n &lt;span&gt;Followed by 1.&lt;/span&gt;\n &lt;span&gt;Following 1.&lt;/span&gt;\n &lt;/div&gt;\n &lt;div class=&#34;accountstats&#34; aria-hidden=&#34;true&#34;&gt;\n &lt;b&gt;Joined&lt;/b&gt;&lt;time datetime=&#34;2022-06-04T13:12:00.000Z&#34;&gt;Jun, 2022&lt;/time&gt;\n &lt;b&gt;Posts&lt;/b&gt;&lt;span&gt;8&lt;/span&gt;\n &lt;b&gt;Followed by&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;b&gt;Following&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;/div&gt;\n&lt;/section&gt;\n</code></pre><p>There, hope you liked that!</p>",
Text: "Here's a bunch of HTML, read it and weep, weep then!\n\n```html\n<section class=\"about-user\">\n <div class=\"col-header\">\n <h2>About</h2>\n </div> \n <div class=\"fields\">\n <h3 class=\"sr-only\">Fields</h3>\n <dl>\n <div class=\"field\">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class=\"field\">\n <dt>age</dt>\n <dd>120</dd>\n </div>… <h3 class=\"sr-only\">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class=\"accountstats\" aria-hidden=\"true\">\n <b>Joined</b><time datetime=\"2022-06-04T13:12:00.000Z\">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\n```\n\nThere, hope you liked that!",
CreatedAt: TimeMustParse("2023-12-10T11:24:00+02:00"),
UpdatedAt: TimeMustParse("2023-12-10T11:24:00+02:00"),
Local: util.Ptr(true),
AccountURI: "http://localhost:8080/users/the_mighty_zork",
AccountID: "01F8MH1H7YV1Z7D2C8K2730QBF",
InReplyToID: "",
BoostOfID: "",
ThreadID: "01HH9M3FVSF5J7120X9T6PG4GF",
ContentWarning: "HTML in post",
Visibility: gtsmodel.VisibilityPublic,
Sensitive: util.Ptr(true),
Language: "en",
CreatedWithApplicationID: "01F8MGY43H3N2C8EWPR2FPYEXG",
Federated: util.Ptr(true),
Boostable: util.Ptr(true),
Replyable: util.Ptr(true),
Likeable: util.Ptr(true),
ActivityStreamsType: ap.ObjectNote,
},
"local_account_2_status_1": { "local_account_2_status_1": {
ID: "01F8MHBQCBTDKN6X5VHGMMN4MA", ID: "01F8MHBQCBTDKN6X5VHGMMN4MA",
URI: "http://localhost:8080/users/1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA", URI: "http://localhost:8080/users/1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA",
@ -2169,6 +2194,9 @@ func NewTestThreads() map[string]*gtsmodel.Thread {
"local_account_1_status_5": { "local_account_1_status_5": {
ID: "01HCWE1ERQSMMVWDD0BE491E2P", ID: "01HCWE1ERQSMMVWDD0BE491E2P",
}, },
"local_account_1_status_7": {
ID: "01HH9M3FVSF5J7120X9T6PG4GF",
},
"local_account_2_status_1": { "local_account_2_status_1": {
ID: "01HCWE2Q24FWCZE41AS77SDFRZ", ID: "01HCWE2Q24FWCZE41AS77SDFRZ",
}, },

View file

@ -155,6 +155,7 @@ main {
.content { .content {
word-break: break-word; word-break: break-word;
line-height: 1.6rem; line-height: 1.6rem;
width: 100%;
h1 { h1 {
margin: 0; margin: 0;