diff options
| -rw-r--r-- | internal/ap/serialize.go | 19 | ||||
| -rw-r--r-- | internal/api/activitypub/users/outboxget_test.go | 56 | ||||
| -rw-r--r-- | internal/api/activitypub/users/repliesget_test.go | 2 | ||||
| -rw-r--r-- | internal/api/client/accounts/accountverify_test.go | 2 | ||||
| -rw-r--r-- | internal/api/client/instance/instancepatch_test.go | 12 | ||||
| -rw-r--r-- | internal/api/client/search/searchget_test.go | 6 | ||||
| -rw-r--r-- | internal/api/client/statuses/statusmute_test.go | 8 | ||||
| -rw-r--r-- | internal/db/bundb/account_test.go | 24 | ||||
| -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/timeline_test.go | 10 | ||||
| -rw-r--r-- | internal/processing/account/rss_test.go | 4 | ||||
| -rw-r--r-- | internal/timeline/get_test.go | 6 | ||||
| -rw-r--r-- | internal/timeline/prune_test.go | 8 | ||||
| -rw-r--r-- | internal/typeutils/internaltofrontend_test.go | 18 | ||||
| -rw-r--r-- | testrig/testmodels.go | 28 | ||||
| -rw-r--r-- | web/source/css/status.css | 1 | 
17 files changed, 130 insertions, 78 deletions
| diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go index 944e67407..774e95f2d 100644 --- a/internal/ap/serialize.go +++ b/internal/ap/serialize.go @@ -35,13 +35,15 @@ import (  // Currently, the following things will be custom serialized:  //  //   - 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 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.  func Serialize(t vocab.Type) (m map[string]interface{}, e error) {  	switch tn := t.GetTypeName(); { -	case tn == ObjectOrderedCollection: -		return serializeOrderedCollection(t) +	case tn == ObjectOrderedCollection || +		tn == ObjectOrderedCollectionPage: +		return serializeWithOrderedItems(t)  	case IsAccountable(tn):  		return serializeAccountable(t, true)  	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. -// Unlike the standard streams.Serialize function, this serializer normalizes the orderedItems -// value to always be an array/slice, regardless of how many items are contained therein. -// -// TODO: Remove this function if we can fix the underlying issue in Go-Fed. +// serializeWithOrderedItems is a custom serializer +// for any type that has an `orderedItems` property. +// Unlike the standard streams.Serialize function, +// this serializer normalizes the orderedItems +// value to always be an array/slice, regardless +// of how many items are contained therein.  //  // See:  //   - https://github.com/go-fed/activity/issues/139  //   - 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)  	if err != nil {  		return nil, err diff --git a/internal/api/activitypub/users/outboxget_test.go b/internal/api/activitypub/users/outboxget_test.go index 31b7e8e9b..55e9f2f78 100644 --- a/internal/api/activitypub/users/outboxget_test.go +++ b/internal/api/activitypub/users/outboxget_test.go @@ -140,16 +140,26 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {    "@context": "https://www.w3.org/ns/activitystreams",    "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", -  "orderedItems": { -    "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" -  }, +  "orderedItems": [ +    { +      "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/01HH9KYNQPA416TNJ53NSATP40/activity#Create", +      "object": "http://localhost:8080/users/the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40", +      "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", -  "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"  }`, dst.String()) @@ -237,22 +247,32 @@ func checkDropPublished(t *testing.T, b []byte, at ...string) []byte {  	if err := json.Unmarshal(b, &m); err != nil {  		t.Fatalf("error unmarshaling json into map: %v", err)  	} -	mm := m + +	entries := make([]map[string]any, 0)  	for _, key := range at { -		switch vt := mm[key].(type) { -		case map[string]any: -			mm = vt +		switch vt := m[key].(type) { +		case []interface{}: +			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") -	} else if _, err := time.Parse(time.RFC3339, s); err != nil { -		t.Fatalf("error parsing published time: %v", err) + +	for _, entry := range entries { +		if s, ok := entry["published"].(string); !ok { +			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)  	if err != nil {  		t.Fatalf("error remarshaling json: %v", err)  	} +  	return b  } diff --git a/internal/api/activitypub/users/repliesget_test.go b/internal/api/activitypub/users/repliesget_test.go index d20d8c6c0..757d0bc83 100644 --- a/internal/api/activitypub/users/repliesget_test.go +++ b/internal/api/activitypub/users/repliesget_test.go @@ -163,7 +163,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {  		"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",  		"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,  	})  	assert.Equal(suite.T(), expect, string(b)) diff --git a/internal/api/client/accounts/accountverify_test.go b/internal/api/client/accounts/accountverify_test.go index 7827cc732..744488bf3 100644 --- a/internal/api/client/accounts/accountverify_test.go +++ b/internal/api/client/accounts/accountverify_test.go @@ -79,7 +79,7 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {  	suite.Equal("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", apimodelAccount.HeaderStatic)  	suite.Equal(2, apimodelAccount.FollowersCount)  	suite.Equal(2, apimodelAccount.FollowingCount) -	suite.Equal(6, apimodelAccount.StatusesCount) +	suite.Equal(7, apimodelAccount.StatusesCount)  	suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)  	suite.Equal(testAccount.Language, apimodelAccount.Source.Language)  	suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note) diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 0c270de21..420bcd79e 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -133,7 +133,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", @@ -250,7 +250,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", @@ -367,7 +367,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", @@ -535,7 +535,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", @@ -674,7 +674,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+` @@ -828,7 +828,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", diff --git a/internal/api/client/search/searchget_test.go b/internal/api/client/search/searchget_test.go index 307b928bd..1b4b92c21 100644 --- a/internal/api/client/search/searchget_test.go +++ b/internal/api/client/search/searchget_test.go @@ -877,7 +877,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() {  	}  	suite.Len(searchResult.Accounts, 5) -	suite.Len(searchResult.Statuses, 5) +	suite.Len(searchResult.Statuses, 6)  	suite.Len(searchResult.Hashtags, 0)  } @@ -918,7 +918,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() {  	}  	suite.Len(searchResult.Accounts, 2) -	suite.Len(searchResult.Statuses, 5) +	suite.Len(searchResult.Statuses, 6)  	suite.Len(searchResult.Hashtags, 0)  } @@ -959,7 +959,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {  	}  	suite.Len(searchResult.Accounts, 0) -	suite.Len(searchResult.Statuses, 5) +	suite.Len(searchResult.Statuses, 6)  	suite.Len(searchResult.Hashtags, 0)  } diff --git a/internal/api/client/statuses/statusmute_test.go b/internal/api/client/statuses/statusmute_test.go index 91a361bd6..a83720a20 100644 --- a/internal/api/client/statuses/statusmute_test.go +++ b/internal/api/client/statuses/statusmute_test.go @@ -130,8 +130,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {      "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",      "followers_count": 2,      "following_count": 2, -    "statuses_count": 6, -    "last_status_at": "2022-05-20T11:41:10.000Z", +    "statuses_count": 7, +    "last_status_at": "2023-12-10T09:24:00.000Z",      "emojis": [],      "fields": [],      "enable_rss": true, @@ -193,8 +193,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {      "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",      "followers_count": 2,      "following_count": 2, -    "statuses_count": 6, -    "last_status_at": "2022-05-20T11:41:10.000Z", +    "statuses_count": 7, +    "last_status_at": "2023-12-10T09:24:00.000Z",      "emojis": [],      "fields": [],      "enable_rss": true, diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go index 8c2de5519..e03300700 100644 --- a/internal/db/bundb/account_test.go +++ b/internal/db/bundb/account_test.go @@ -42,33 +42,33 @@ type AccountTestSuite struct {  func (suite *AccountTestSuite) TestGetAccountStatuses() {  	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false)  	suite.NoError(err) -	suite.Len(statuses, 6) +	suite.Len(statuses, 7)  }  func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {  	// 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 {  		suite.FailNow(err.Error())  	} -	suite.Len(statuses, 2) +	suite.Len(statuses, 3)  	// 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 {  		suite.FailNow(err.Error())  	} -	suite.Len(statuses, 2) +	suite.Len(statuses, 3)  	// 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 {  		suite.FailNow(err.Error())  	} -	suite.Len(statuses, 2) +	suite.Len(statuses, 1)  	// 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.Empty(statuses)  } @@ -76,13 +76,13 @@ func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {  func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogs() {  	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false)  	suite.NoError(err) -	suite.Len(statuses, 6) +	suite.Len(statuses, 7)  }  func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() {  	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true)  	suite.NoError(err) -	suite.Len(statuses, 1) +	suite.Len(statuses, 2)  }  func (suite *AccountTestSuite) TestGetAccountStatusesMediaOnly() { @@ -306,13 +306,13 @@ func (suite *AccountTestSuite) TestUpdateAccount() {  func (suite *AccountTestSuite) TestGetAccountLastPosted() {  	lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, false)  	suite.NoError(err) -	suite.EqualValues(1653046870, lastPosted.Unix()) +	suite.EqualValues(1702200240, lastPosted.Unix())  }  func (suite *AccountTestSuite) TestGetAccountLastPostedWebOnly() {  	lastPosted, err := suite.db.GetAccountLastPosted(context.Background(), suite.testAccounts["local_account_1"].ID, true)  	suite.NoError(err) -	suite.EqualValues(1634726437, lastPosted.Unix()) +	suite.EqualValues(1702200240, lastPosted.Unix())  }  func (suite *AccountTestSuite) TestInsertAccountWithDefaults() { diff --git a/internal/db/bundb/basic_test.go b/internal/db/bundb/basic_test.go index 67973a171..fc601f2a5 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, 22) +	suite.Len(s, 23)  }  func (suite *BasicTestSuite) TestGetAllNotNull() { diff --git a/internal/db/bundb/instance_test.go b/internal/db/bundb/instance_test.go index f2ac202ef..4b8ec9962 100644 --- a/internal/db/bundb/instance_test.go +++ b/internal/db/bundb/instance_test.go @@ -47,7 +47,7 @@ func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {  func (suite *InstanceTestSuite) TestCountInstanceStatuses() {  	count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost())  	suite.NoError(err) -	suite.Equal(18, count) +	suite.Equal(19, count)  }  func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() { diff --git a/internal/db/bundb/timeline_test.go b/internal/db/bundb/timeline_test.go index b3ce91755..98ae6b20f 100644 --- a/internal/db/bundb/timeline_test.go +++ b/internal/db/bundb/timeline_test.go @@ -157,7 +157,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimeline() {  		suite.FailNow(err.Error())  	} -	suite.checkStatuses(s, id.Highest, id.Lowest, 18) +	suite.checkStatuses(s, id.Highest, id.Lowest, 19)  }  func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() { @@ -189,7 +189,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {  		suite.FailNow(err.Error())  	} -	suite.checkStatuses(s, id.Highest, id.Lowest, 6) +	suite.checkStatuses(s, id.Highest, id.Lowest, 7)  }  func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() { @@ -211,7 +211,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {  	}  	suite.NotContains(s, futureStatus) -	suite.checkStatuses(s, id.Highest, id.Lowest, 18) +	suite.checkStatuses(s, id.Highest, id.Lowest, 19)  }  func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() { @@ -242,8 +242,8 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineFromHighest() {  	}  	suite.checkStatuses(s, id.Highest, id.Lowest, 5) -	suite.Equal("01HEN2RZ8BG29Y5Z9VJC73HZW7", s[0].ID) -	suite.Equal("01FN3VJGFH10KR7S2PB0GFJZYG", s[len(s)-1].ID) +	suite.Equal("01HH9KYNQPA416TNJ53NSATP40", s[0].ID) +	suite.Equal("01G20ZM733MGN8J344T4ZDDFY1", s[len(s)-1].ID)  }  func (suite *TimelineTestSuite) TestGetListTimelineNoParams() { diff --git a/internal/processing/account/rss_test.go b/internal/processing/account/rss_test.go index 80f86211f..e528c27e0 100644 --- a/internal/processing/account/rss_test.go +++ b/internal/processing/account/rss_test.go @@ -45,14 +45,14 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() {  func (suite *GetRSSTestSuite) TestGetAccountRSSZork() {  	getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "the_mighty_zork")  	suite.NoError(err) -	suite.EqualValues(1634726437, lastModified.Unix()) +	suite.EqualValues(1702200240, lastModified.Unix())  	feed, err := getFeed()  	suite.NoError(err)  	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: "hello everyone!"</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: "Here's a bunch of HTML, read it and weep, weep then!

```html
<section class="about-user">
 <div class="col-header">
 <h2>About</h2>
 </div> 
 <div class="fields">
 <h3 class="sr-only">Fields</h3>
 <dl>
...</description>\n      <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\"><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>\n        </dl>\n    </div>\n    <div class="bio">\n        <h3 class="sr-only">Bio</h3>\n        <p>i post about things that concern me</p>\n    </div>\n    <div class="sr-only" role="group">\n        <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</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: "hello everyone!"</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() { diff --git a/internal/timeline/get_test.go b/internal/timeline/get_test.go index 360eac7b6..02256054e 100644 --- a/internal/timeline/get_test.go +++ b/internal/timeline/get_test.go @@ -228,7 +228,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossible() {  	if err != nil {  		suite.FailNow(err.Error())  	} -	suite.checkStatuses(statuses, id.Highest, id.Lowest, 18) +	suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)  }  func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() { @@ -255,7 +255,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {  	if err != nil {  		suite.FailNow(err.Error())  	} -	suite.checkStatuses(statuses, id.Highest, id.Lowest, 18) +	suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)  }  func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() { @@ -284,7 +284,7 @@ func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {  	if err != nil {  		suite.FailNow(err.Error())  	} -	suite.checkStatuses(statuses, id.Highest, id.Lowest, 6) +	suite.checkStatuses(statuses, id.Highest, id.Lowest, 7)  	for _, s := range statuses {  		if s.GetAccountID() != testAccount.ID { diff --git a/internal/timeline/prune_test.go b/internal/timeline/prune_test.go index 4cd677953..047166e9e 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(17, pruned) +	suite.Equal(18, 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(17, pruned) +	suite.Equal(18, 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(22, pruned) +	suite.Equal(23, 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(22, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID)) +	suite.Equal(23, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))  }  func TestPruneTestSuite(t *testing.T) { diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 6152bf77f..d65d47bf1 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -58,8 +58,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontend() {    "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",    "followers_count": 2,    "following_count": 2, -  "statuses_count": 6, -  "last_status_at": "2022-05-20T11:41:10.000Z", +  "statuses_count": 7, +  "last_status_at": "2023-12-10T09:24:00.000Z",    "emojis": [],    "fields": [],    "enable_rss": true, @@ -100,8 +100,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiStruct()    "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",    "followers_count": 2,    "following_count": 2, -  "statuses_count": 6, -  "last_status_at": "2022-05-20T11:41:10.000Z", +  "statuses_count": 7, +  "last_status_at": "2023-12-10T09:24:00.000Z",    "emojis": [      {        "shortcode": "rainbow", @@ -148,8 +148,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiIDs() {    "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",    "followers_count": 2,    "following_count": 2, -  "statuses_count": 6, -  "last_status_at": "2022-05-20T11:41:10.000Z", +  "statuses_count": 7, +  "last_status_at": "2023-12-10T09:24:00.000Z",    "emojis": [      {        "shortcode": "rainbow", @@ -192,8 +192,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendSensitive() {    "header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",    "followers_count": 2,    "following_count": 2, -  "statuses_count": 6, -  "last_status_at": "2022-05-20T11:41:10.000Z", +  "statuses_count": 7, +  "last_status_at": "2023-12-10T09:24:00.000Z",    "emojis": [],    "fields": [],    "source": { @@ -896,7 +896,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {    },    "stats": {      "domain_count": 2, -    "status_count": 18, +    "status_count": 19,      "user_count": 4    },    "thumbnail": "http://localhost:8080/assets/logo.png", diff --git a/testrig/testmodels.go b/testrig/testmodels.go index e039a7c16..e7c1f0c02 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -1676,6 +1676,31 @@ func NewTestStatuses() map[string]*gtsmodel.Status {  			ActivityStreamsType:      ap.ActivityQuestion,  			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\"><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>\n        </dl>\n    </div>\n    <div class="bio">\n        <h3 class="sr-only">Bio</h3>\n        <p>i post about things that concern me</p>\n    </div>\n    <div class="sr-only" role="group">\n        <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</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": {  			ID:                       "01F8MHBQCBTDKN6X5VHGMMN4MA",  			URI:                      "http://localhost:8080/users/1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA", @@ -2169,6 +2194,9 @@ func NewTestThreads() map[string]*gtsmodel.Thread {  		"local_account_1_status_5": {  			ID: "01HCWE1ERQSMMVWDD0BE491E2P",  		}, +		"local_account_1_status_7": { +			ID: "01HH9M3FVSF5J7120X9T6PG4GF", +		},  		"local_account_2_status_1": {  			ID: "01HCWE2Q24FWCZE41AS77SDFRZ",  		}, diff --git a/web/source/css/status.css b/web/source/css/status.css index d2aa658f1..cbe8c04d2 100644 --- a/web/source/css/status.css +++ b/web/source/css/status.css @@ -155,6 +155,7 @@ main {  		.content {  			word-break: break-word;  			line-height: 1.6rem; +			width: 100%;  			h1 {  				margin: 0; | 
