mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 08:02:26 -05:00
[chore] move status filtering from type converter (#4306)
This finalizes the moving status filtering out of the type converter, and into its own `./internal/filter/` subpkg :) Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4306 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
79914bdbf7
commit
66e1ec14aa
38 changed files with 565 additions and 846 deletions
|
|
@ -27,7 +27,6 @@ import (
|
|||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/interaction"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/status"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
|
|
@ -38,7 +37,6 @@ type Converter struct {
|
|||
defaultAvatars []string
|
||||
randAvatars sync.Map
|
||||
visFilter *visibility.Filter
|
||||
statusFilter *status.Filter
|
||||
intFilter *interaction.Filter
|
||||
randStats atomic.Pointer[apimodel.RandomStats]
|
||||
}
|
||||
|
|
@ -48,7 +46,6 @@ func NewConverter(state *state.State) *Converter {
|
|||
state: state,
|
||||
defaultAvatars: populateDefaultAvatars(),
|
||||
visFilter: visibility.NewFilter(state),
|
||||
statusFilter: status.NewFilter(state),
|
||||
intFilter: interaction.NewFilter(state),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,10 +51,6 @@ const (
|
|||
instanceMastodonVersion = "3.5.3"
|
||||
)
|
||||
|
||||
// ErrHideStatus indicates that a status has
|
||||
// been filtered and should not be returned at all.
|
||||
var ErrHideStatus = errors.New("hide status")
|
||||
|
||||
var instanceStatusesSupportedMimeTypes = []string{
|
||||
string(apimodel.StatusContentTypePlain),
|
||||
string(apimodel.StatusContentTypeMarkdown),
|
||||
|
|
@ -850,13 +846,11 @@ func (c *Converter) StatusToAPIStatus(
|
|||
ctx context.Context,
|
||||
status *gtsmodel.Status,
|
||||
requestingAccount *gtsmodel.Account,
|
||||
filterCtx gtsmodel.FilterContext,
|
||||
) (*apimodel.Status, error) {
|
||||
return c.statusToAPIStatus(
|
||||
ctx,
|
||||
status,
|
||||
requestingAccount,
|
||||
filterCtx,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
|
|
@ -870,7 +864,6 @@ func (c *Converter) statusToAPIStatus(
|
|||
ctx context.Context,
|
||||
status *gtsmodel.Status,
|
||||
requestingAccount *gtsmodel.Account,
|
||||
filterCtx gtsmodel.FilterContext,
|
||||
placeholdAttachments bool,
|
||||
addPendingNote bool,
|
||||
) (*apimodel.Status, error) {
|
||||
|
|
@ -878,7 +871,6 @@ func (c *Converter) statusToAPIStatus(
|
|||
ctx,
|
||||
status,
|
||||
requestingAccount, // Can be nil.
|
||||
filterCtx, // Can be empty.
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -945,8 +937,7 @@ func (c *Converter) StatusToWebStatus(
|
|||
s *gtsmodel.Status,
|
||||
) (*apimodel.WebStatus, error) {
|
||||
apiStatus, err := c.statusToFrontend(ctx, s,
|
||||
nil, // No authed requester.
|
||||
gtsmodel.FilterContextNone, // No filters.
|
||||
nil, // No authed requester.
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1115,7 +1106,6 @@ func (c *Converter) statusToFrontend(
|
|||
ctx context.Context,
|
||||
status *gtsmodel.Status,
|
||||
requestingAccount *gtsmodel.Account,
|
||||
filterCtx gtsmodel.FilterContext,
|
||||
) (
|
||||
*apimodel.Status,
|
||||
error,
|
||||
|
|
@ -1123,7 +1113,6 @@ func (c *Converter) statusToFrontend(
|
|||
apiStatus, err := c.baseStatusToFrontend(ctx,
|
||||
status,
|
||||
requestingAccount,
|
||||
filterCtx,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1133,12 +1122,8 @@ func (c *Converter) statusToFrontend(
|
|||
reblog, err := c.baseStatusToFrontend(ctx,
|
||||
status.BoostOf,
|
||||
requestingAccount,
|
||||
filterCtx,
|
||||
)
|
||||
if errors.Is(err, ErrHideStatus) {
|
||||
// If we'd hide the original status, hide the boost.
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("error converting boosted status: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -1165,7 +1150,6 @@ func (c *Converter) baseStatusToFrontend(
|
|||
ctx context.Context,
|
||||
status *gtsmodel.Status,
|
||||
requester *gtsmodel.Account,
|
||||
filterCtx gtsmodel.FilterContext,
|
||||
) (
|
||||
*apimodel.Status,
|
||||
error,
|
||||
|
|
@ -1340,20 +1324,6 @@ func (c *Converter) baseStatusToFrontend(
|
|||
apiStatus.URL = apiStatus.URI
|
||||
}
|
||||
|
||||
var hide bool
|
||||
|
||||
// Pass the status through any stored filters of requesting account's, in context.
|
||||
apiStatus.Filtered, hide, err = c.statusFilter.StatusFilterResultsInContext(ctx,
|
||||
requester,
|
||||
status,
|
||||
filterCtx,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("error filtering status %s: %w", status.URI, err)
|
||||
} else if hide {
|
||||
return nil, ErrHideStatus
|
||||
}
|
||||
|
||||
return apiStatus, nil
|
||||
}
|
||||
|
||||
|
|
@ -1866,7 +1836,6 @@ func (c *Converter) RelationshipToAPIRelationship(ctx context.Context, r *gtsmod
|
|||
func (c *Converter) NotificationToAPINotification(
|
||||
ctx context.Context,
|
||||
notif *gtsmodel.Notification,
|
||||
filter bool,
|
||||
) (*apimodel.Notification, error) {
|
||||
// Ensure notif populated.
|
||||
if err := c.state.DB.PopulateNotification(ctx, notif); err != nil {
|
||||
|
|
@ -1882,27 +1851,14 @@ func (c *Converter) NotificationToAPINotification(
|
|||
// Get status that triggered this notif, if set.
|
||||
var apiStatus *apimodel.Status
|
||||
if notif.Status != nil {
|
||||
var filterCtx gtsmodel.FilterContext
|
||||
|
||||
if filter {
|
||||
filterCtx = gtsmodel.FilterContextNotifications
|
||||
}
|
||||
|
||||
apiStatus, err = c.StatusToAPIStatus(ctx,
|
||||
notif.Status,
|
||||
notif.TargetAccount,
|
||||
filterCtx,
|
||||
)
|
||||
if err != nil && !errors.Is(err, ErrHideStatus) {
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("error converting status to api: %w", err)
|
||||
}
|
||||
|
||||
if apiStatus == nil {
|
||||
// Notif filtered for this
|
||||
// status, nothing to do.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if apiStatus.Reblog != nil {
|
||||
// Use the actual reblog status
|
||||
// for the notifications endpoint.
|
||||
|
|
@ -1926,7 +1882,6 @@ func (c *Converter) ConversationToAPIConversation(
|
|||
ctx context.Context,
|
||||
conversation *gtsmodel.Conversation,
|
||||
requester *gtsmodel.Account,
|
||||
filters []*gtsmodel.Filter,
|
||||
) (*apimodel.Conversation, error) {
|
||||
apiConversation := &apimodel.Conversation{
|
||||
ID: conversation.ID,
|
||||
|
|
@ -1941,9 +1896,8 @@ func (c *Converter) ConversationToAPIConversation(
|
|||
ctx,
|
||||
conversation.LastStatus,
|
||||
requester,
|
||||
gtsmodel.FilterContextNotifications,
|
||||
)
|
||||
if err != nil && !errors.Is(err, ErrHideStatus) {
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf(
|
||||
"error converting status %s to API representation: %w",
|
||||
conversation.LastStatus.ID,
|
||||
|
|
@ -2209,7 +2163,6 @@ func (c *Converter) ReportToAdminAPIReport(ctx context.Context, r *gtsmodel.Repo
|
|||
ctx,
|
||||
s,
|
||||
requestingAccount,
|
||||
gtsmodel.FilterContextNone,
|
||||
true, // Placehold unknown attachments.
|
||||
|
||||
// Don't add note about
|
||||
|
|
@ -2913,7 +2866,6 @@ func (c *Converter) InteractionReqToAPIInteractionReq(
|
|||
ctx,
|
||||
req.Status,
|
||||
requestingAcct,
|
||||
gtsmodel.FilterContextNone,
|
||||
)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("error converting interacted status: %w", err)
|
||||
|
|
@ -2926,7 +2878,6 @@ func (c *Converter) InteractionReqToAPIInteractionReq(
|
|||
ctx,
|
||||
req.Reply,
|
||||
requestingAcct,
|
||||
gtsmodel.FilterContextNone,
|
||||
true, // Placehold unknown attachments.
|
||||
|
||||
// Don't add note about pending;
|
||||
|
|
|
|||
|
|
@ -24,12 +24,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/id"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"code.superseriousbusiness.org/gotosocial/testrig"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
|
@ -466,7 +463,7 @@ func (suite *InternalToFrontendTestSuite) TestLocalInstanceAccountToFrontendBloc
|
|||
func (suite *InternalToFrontendTestSuite) TestStatusToFrontend() {
|
||||
testStatus := suite.testStatuses["admin_account_status_1"]
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -629,7 +626,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendHTMLContentWarning
|
|||
testStatus.ContentWarning = `<p>First paragraph of content warning</p><h4>Here's the title!</h4><p></p><p>Big boobs<br>Tee hee!<br><br>Some more text<br>And a bunch more<br><br>Hasta la victoria siempre!</p>`
|
||||
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -795,7 +792,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendApplicationDeleted
|
|||
}
|
||||
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(ctx, testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(ctx, testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -950,625 +947,11 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendApplicationDeleted
|
|||
}`, string(b))
|
||||
}
|
||||
|
||||
// Modify a fixture status into a status that should be filtered,
|
||||
// and then filter it, returning the API status or any error from converting it.
|
||||
func (suite *InternalToFrontendTestSuite) filteredStatusToFrontend(action gtsmodel.FilterAction, boost bool) (*apimodel.Status, error) {
|
||||
ctx := suite.T().Context()
|
||||
|
||||
testStatus := suite.testStatuses["admin_account_status_1"]
|
||||
testStatus.Content += " fnord"
|
||||
testStatus.Text += " fnord"
|
||||
|
||||
if boost {
|
||||
// Modify a fixture boost into a boost of the above status.
|
||||
boostStatus := suite.testStatuses["admin_account_status_4"]
|
||||
boostStatus.BoostOf = testStatus
|
||||
boostStatus.BoostOfID = testStatus.ID
|
||||
testStatus = boostStatus
|
||||
}
|
||||
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
expectedMatchingFilter := suite.testFilters["local_account_1_filter_1"]
|
||||
expectedMatchingFilter.Action = action
|
||||
|
||||
err := suite.state.DB.UpdateFilter(ctx, expectedMatchingFilter, "action")
|
||||
suite.NoError(err)
|
||||
|
||||
return suite.typeconverter.StatusToAPIStatus(
|
||||
suite.T().Context(),
|
||||
testStatus,
|
||||
requestingAccount,
|
||||
gtsmodel.FilterContextHome,
|
||||
)
|
||||
}
|
||||
|
||||
// Test that a status which is filtered with a warn filter by the requesting user has `filtered` set correctly.
|
||||
func (suite *InternalToFrontendTestSuite) TestWarnFilteredStatusToFrontend() {
|
||||
apiStatus, err := suite.filteredStatusToFrontend(gtsmodel.FilterActionWarn, false)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"id": "01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"created_at": "2021-10-20T11:36:45.000Z",
|
||||
"edited_at": null,
|
||||
"in_reply_to_id": null,
|
||||
"in_reply_to_account_id": null,
|
||||
"sensitive": false,
|
||||
"spoiler_text": "",
|
||||
"visibility": "public",
|
||||
"language": "en",
|
||||
"uri": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"url": "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"replies_count": 1,
|
||||
"reblogs_count": 0,
|
||||
"favourites_count": 1,
|
||||
"favourited": true,
|
||||
"reblogged": false,
|
||||
"muted": false,
|
||||
"bookmarked": true,
|
||||
"pinned": false,
|
||||
"content": "\u003cp\u003ehello world! \u003ca href=\"http://localhost:8080/tags/welcome\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\"\u003e#\u003cspan\u003ewelcome\u003c/span\u003e\u003c/a\u003e ! first post on the instance :rainbow: !\u003c/p\u003e fnord",
|
||||
"reblog": null,
|
||||
"application": {
|
||||
"name": "superseriousbusiness",
|
||||
"website": "https://superserious.business"
|
||||
},
|
||||
"account": {
|
||||
"id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"username": "admin",
|
||||
"acct": "admin",
|
||||
"display_name": "",
|
||||
"locked": false,
|
||||
"discoverable": true,
|
||||
"bot": false,
|
||||
"created_at": "2022-05-17T13:10:59.000Z",
|
||||
"note": "",
|
||||
"url": "http://localhost:8080/@admin",
|
||||
"avatar": "",
|
||||
"avatar_static": "",
|
||||
"header": "http://localhost:8080/assets/default_header.webp",
|
||||
"header_static": "http://localhost:8080/assets/default_header.webp",
|
||||
"header_description": "Flat gray background (default header).",
|
||||
"followers_count": 1,
|
||||
"following_count": 1,
|
||||
"statuses_count": 4,
|
||||
"last_status_at": "2021-10-20",
|
||||
"emojis": [],
|
||||
"fields": [],
|
||||
"enable_rss": true,
|
||||
"roles": [
|
||||
{
|
||||
"id": "admin",
|
||||
"name": "admin",
|
||||
"color": ""
|
||||
}
|
||||
],
|
||||
"group": false
|
||||
},
|
||||
"media_attachments": [
|
||||
{
|
||||
"id": "01F8MH6NEM8D7527KZAECTCR76",
|
||||
"type": "image",
|
||||
"url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg",
|
||||
"text_url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg",
|
||||
"preview_url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.webp",
|
||||
"remote_url": null,
|
||||
"preview_remote_url": null,
|
||||
"meta": {
|
||||
"original": {
|
||||
"width": 1200,
|
||||
"height": 630,
|
||||
"size": "1200x630",
|
||||
"aspect": 1.9047619
|
||||
},
|
||||
"small": {
|
||||
"width": 512,
|
||||
"height": 268,
|
||||
"size": "512x268",
|
||||
"aspect": 1.9104477
|
||||
},
|
||||
"focus": {
|
||||
"x": -0.5,
|
||||
"y": 0.5
|
||||
}
|
||||
},
|
||||
"description": "Black and white image of some 50's style text saying: Welcome On Board",
|
||||
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj"
|
||||
}
|
||||
],
|
||||
"mentions": [],
|
||||
"tags": [
|
||||
{
|
||||
"name": "welcome",
|
||||
"url": "http://localhost:8080/tags/welcome"
|
||||
}
|
||||
],
|
||||
"emojis": [
|
||||
{
|
||||
"shortcode": "rainbow",
|
||||
"url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png",
|
||||
"static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/static/01F8MH9H8E4VG3KDYJR9EGPXCQ.png",
|
||||
"visible_in_picker": true,
|
||||
"category": "reactions"
|
||||
}
|
||||
],
|
||||
"card": null,
|
||||
"poll": null,
|
||||
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
||||
"content_type": "text/plain",
|
||||
"filtered": [
|
||||
{
|
||||
"filter": {
|
||||
"id": "01HN26VM6KZTW1ANNRVSBMA461",
|
||||
"title": "fnord",
|
||||
"context": [
|
||||
"home",
|
||||
"public"
|
||||
],
|
||||
"expires_at": null,
|
||||
"filter_action": "warn",
|
||||
"keywords": [
|
||||
{
|
||||
"id": "01HN272TAVWAXX72ZX4M8JZ0PS",
|
||||
"keyword": "fnord",
|
||||
"whole_word": true
|
||||
}
|
||||
],
|
||||
"statuses": []
|
||||
},
|
||||
"keyword_matches": [
|
||||
"fnord"
|
||||
],
|
||||
"status_matches": []
|
||||
}
|
||||
],
|
||||
"interaction_policy": {
|
||||
"can_favourite": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reply": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reblog": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
}
|
||||
}
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
// Test that a status which is filtered with a warn filter by the requesting user has `filtered` set correctly when boosted.
|
||||
func (suite *InternalToFrontendTestSuite) TestWarnFilteredBoostToFrontend() {
|
||||
apiStatus, err := suite.filteredStatusToFrontend(gtsmodel.FilterActionWarn, true)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"id": "01G36SF3V6Y6V5BF9P4R7PQG7G",
|
||||
"created_at": "2021-10-20T10:41:37.000Z",
|
||||
"edited_at": null,
|
||||
"in_reply_to_id": null,
|
||||
"in_reply_to_account_id": null,
|
||||
"sensitive": false,
|
||||
"spoiler_text": "",
|
||||
"visibility": "public",
|
||||
"language": null,
|
||||
"uri": "http://localhost:8080/users/admin/statuses/01G36SF3V6Y6V5BF9P4R7PQG7G",
|
||||
"url": "http://localhost:8080/@admin/statuses/01G36SF3V6Y6V5BF9P4R7PQG7G",
|
||||
"replies_count": 0,
|
||||
"reblogs_count": 0,
|
||||
"favourites_count": 0,
|
||||
"favourited": true,
|
||||
"reblogged": false,
|
||||
"muted": false,
|
||||
"bookmarked": true,
|
||||
"pinned": false,
|
||||
"content": "",
|
||||
"reblog": {
|
||||
"id": "01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"created_at": "2021-10-20T11:36:45.000Z",
|
||||
"edited_at": null,
|
||||
"in_reply_to_id": null,
|
||||
"in_reply_to_account_id": null,
|
||||
"sensitive": false,
|
||||
"spoiler_text": "",
|
||||
"visibility": "public",
|
||||
"language": "en",
|
||||
"uri": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"url": "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"replies_count": 1,
|
||||
"reblogs_count": 0,
|
||||
"favourites_count": 1,
|
||||
"favourited": true,
|
||||
"reblogged": false,
|
||||
"muted": false,
|
||||
"bookmarked": true,
|
||||
"pinned": false,
|
||||
"content": "\u003cp\u003ehello world! \u003ca href=\"http://localhost:8080/tags/welcome\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\"\u003e#\u003cspan\u003ewelcome\u003c/span\u003e\u003c/a\u003e ! first post on the instance :rainbow: !\u003c/p\u003e fnord",
|
||||
"reblog": null,
|
||||
"application": {
|
||||
"name": "superseriousbusiness",
|
||||
"website": "https://superserious.business"
|
||||
},
|
||||
"account": {
|
||||
"id": "01F8MH1H7YV1Z7D2C8K2730QBF",
|
||||
"username": "the_mighty_zork",
|
||||
"acct": "the_mighty_zork",
|
||||
"display_name": "original zork (he/they)",
|
||||
"locked": false,
|
||||
"discoverable": true,
|
||||
"bot": false,
|
||||
"created_at": "2022-05-20T11:09:18.000Z",
|
||||
"note": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||
"url": "http://localhost:8080/@the_mighty_zork",
|
||||
"avatar": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg",
|
||||
"avatar_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.webp",
|
||||
"avatar_description": "a green goblin looking nasty",
|
||||
"avatar_media_id": "01F8MH58A357CV5K7R7TJMSH6S",
|
||||
"header": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
|
||||
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.webp",
|
||||
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
|
||||
"header_media_id": "01PFPMWK2FF0D9WMHEJHR07C3Q",
|
||||
"followers_count": 2,
|
||||
"following_count": 2,
|
||||
"statuses_count": 9,
|
||||
"last_status_at": "2024-11-01",
|
||||
"emojis": [],
|
||||
"fields": [],
|
||||
"enable_rss": true,
|
||||
"group": false
|
||||
},
|
||||
"media_attachments": [
|
||||
{
|
||||
"id": "01F8MH6NEM8D7527KZAECTCR76",
|
||||
"type": "image",
|
||||
"url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg",
|
||||
"text_url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg",
|
||||
"preview_url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.webp",
|
||||
"remote_url": null,
|
||||
"preview_remote_url": null,
|
||||
"meta": {
|
||||
"original": {
|
||||
"width": 1200,
|
||||
"height": 630,
|
||||
"size": "1200x630",
|
||||
"aspect": 1.9047619
|
||||
},
|
||||
"small": {
|
||||
"width": 512,
|
||||
"height": 268,
|
||||
"size": "512x268",
|
||||
"aspect": 1.9104477
|
||||
},
|
||||
"focus": {
|
||||
"x": -0.5,
|
||||
"y": 0.5
|
||||
}
|
||||
},
|
||||
"description": "Black and white image of some 50's style text saying: Welcome On Board",
|
||||
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj"
|
||||
}
|
||||
],
|
||||
"mentions": [],
|
||||
"tags": [
|
||||
{
|
||||
"name": "welcome",
|
||||
"url": "http://localhost:8080/tags/welcome"
|
||||
}
|
||||
],
|
||||
"emojis": [
|
||||
{
|
||||
"shortcode": "rainbow",
|
||||
"url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png",
|
||||
"static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/static/01F8MH9H8E4VG3KDYJR9EGPXCQ.png",
|
||||
"visible_in_picker": true,
|
||||
"category": "reactions"
|
||||
}
|
||||
],
|
||||
"card": null,
|
||||
"poll": null,
|
||||
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
||||
"content_type": "text/plain",
|
||||
"filtered": [
|
||||
{
|
||||
"filter": {
|
||||
"id": "01HN26VM6KZTW1ANNRVSBMA461",
|
||||
"title": "fnord",
|
||||
"context": [
|
||||
"home",
|
||||
"public"
|
||||
],
|
||||
"expires_at": null,
|
||||
"filter_action": "warn",
|
||||
"keywords": [
|
||||
{
|
||||
"id": "01HN272TAVWAXX72ZX4M8JZ0PS",
|
||||
"keyword": "fnord",
|
||||
"whole_word": true
|
||||
}
|
||||
],
|
||||
"statuses": []
|
||||
},
|
||||
"keyword_matches": [
|
||||
"fnord"
|
||||
],
|
||||
"status_matches": []
|
||||
}
|
||||
],
|
||||
"interaction_policy": {
|
||||
"can_favourite": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reply": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reblog": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"application": {
|
||||
"name": "superseriousbusiness",
|
||||
"website": "https://superserious.business"
|
||||
},
|
||||
"account": {
|
||||
"id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"username": "admin",
|
||||
"acct": "admin",
|
||||
"display_name": "",
|
||||
"locked": false,
|
||||
"discoverable": true,
|
||||
"bot": false,
|
||||
"created_at": "2022-05-17T13:10:59.000Z",
|
||||
"note": "",
|
||||
"url": "http://localhost:8080/@admin",
|
||||
"avatar": "",
|
||||
"avatar_static": "",
|
||||
"header": "http://localhost:8080/assets/default_header.webp",
|
||||
"header_static": "http://localhost:8080/assets/default_header.webp",
|
||||
"header_description": "Flat gray background (default header).",
|
||||
"followers_count": 1,
|
||||
"following_count": 1,
|
||||
"statuses_count": 4,
|
||||
"last_status_at": "2021-10-20",
|
||||
"emojis": [],
|
||||
"fields": [],
|
||||
"enable_rss": true,
|
||||
"roles": [
|
||||
{
|
||||
"id": "admin",
|
||||
"name": "admin",
|
||||
"color": ""
|
||||
}
|
||||
],
|
||||
"group": false
|
||||
},
|
||||
"media_attachments": [],
|
||||
"mentions": [],
|
||||
"tags": [],
|
||||
"emojis": [],
|
||||
"card": null,
|
||||
"poll": null,
|
||||
"filtered": [
|
||||
{
|
||||
"filter": {
|
||||
"id": "01HN26VM6KZTW1ANNRVSBMA461",
|
||||
"title": "fnord",
|
||||
"context": [
|
||||
"home",
|
||||
"public"
|
||||
],
|
||||
"expires_at": null,
|
||||
"filter_action": "warn",
|
||||
"keywords": [
|
||||
{
|
||||
"id": "01HN272TAVWAXX72ZX4M8JZ0PS",
|
||||
"keyword": "fnord",
|
||||
"whole_word": true
|
||||
}
|
||||
],
|
||||
"statuses": []
|
||||
},
|
||||
"keyword_matches": [
|
||||
"fnord"
|
||||
],
|
||||
"status_matches": []
|
||||
}
|
||||
],
|
||||
"interaction_policy": {
|
||||
"can_favourite": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reply": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
},
|
||||
"can_reblog": {
|
||||
"automatic_approval": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"manual_approval": [],
|
||||
"always": [
|
||||
"public",
|
||||
"me"
|
||||
],
|
||||
"with_approval": []
|
||||
}
|
||||
}
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
// Test that a status which is filtered with a hide filter by the requesting user results in the ErrHideStatus error.
|
||||
func (suite *InternalToFrontendTestSuite) TestHideFilteredStatusToFrontend() {
|
||||
_, err := suite.filteredStatusToFrontend(gtsmodel.FilterActionHide, false)
|
||||
suite.ErrorIs(err, typeutils.ErrHideStatus)
|
||||
}
|
||||
|
||||
// Test that a status which is filtered with a hide filter by the requesting user results in the ErrHideStatus error for a boost of that status.
|
||||
func (suite *InternalToFrontendTestSuite) TestHideFilteredBoostToFrontend() {
|
||||
_, err := suite.filteredStatusToFrontend(gtsmodel.FilterActionHide, true)
|
||||
suite.ErrorIs(err, typeutils.ErrHideStatus)
|
||||
}
|
||||
|
||||
// Test that a hashtag filter for a hashtag in Mastodon HTML content works the way most users would expect.
|
||||
func (suite *InternalToFrontendTestSuite) testHashtagFilteredStatusToFrontend(wholeWord bool, boost bool) {
|
||||
ctx := suite.T().Context()
|
||||
|
||||
testStatus := new(gtsmodel.Status)
|
||||
*testStatus = *suite.testStatuses["admin_account_status_1"]
|
||||
testStatus.Content = `<p>doggo doggin' it</p><p><a href="https://example.test/tags/dogsofmastodon" class="mention hashtag" rel="tag nofollow noreferrer noopener" target="_blank">#<span>dogsofmastodon</span></a></p>`
|
||||
|
||||
if boost {
|
||||
boost, err := suite.typeconverter.StatusToBoost(
|
||||
suite.T().Context(),
|
||||
testStatus,
|
||||
suite.testAccounts["admin_account"],
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
testStatus = boost
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
filter := >smodel.Filter{
|
||||
ID: id.NewULID(),
|
||||
Title: id.NewULID(),
|
||||
AccountID: requestingAccount.ID,
|
||||
Action: gtsmodel.FilterActionWarn,
|
||||
Contexts: gtsmodel.FilterContexts(gtsmodel.FilterContextHome),
|
||||
}
|
||||
|
||||
filterKeyword := >smodel.FilterKeyword{
|
||||
ID: id.NewULID(),
|
||||
FilterID: filter.ID,
|
||||
Keyword: "#dogsofmastodon",
|
||||
WholeWord: &wholeWord,
|
||||
}
|
||||
|
||||
filter.KeywordIDs = []string{filterKeyword.ID}
|
||||
|
||||
err = suite.state.DB.PutFilterKeyword(ctx, filterKeyword)
|
||||
suite.NoError(err)
|
||||
|
||||
err = suite.state.DB.PutFilter(ctx, filter)
|
||||
suite.NoError(err)
|
||||
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(
|
||||
suite.T().Context(),
|
||||
testStatus,
|
||||
requestingAccount,
|
||||
gtsmodel.FilterContextHome,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
suite.NotEmpty(apiStatus.Filtered)
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestHashtagWholeWordFilteredStatusToFrontend() {
|
||||
suite.testHashtagFilteredStatusToFrontend(true, false)
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestHashtagWholeWordFilteredBoostToFrontend() {
|
||||
suite.testHashtagFilteredStatusToFrontend(true, true)
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestHashtagAnywhereFilteredStatusToFrontend() {
|
||||
suite.testHashtagFilteredStatusToFrontend(false, false)
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestHashtagAnywhereFilteredBoostToFrontend() {
|
||||
suite.testHashtagFilteredStatusToFrontend(false, true)
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownAttachments() {
|
||||
testStatus := suite.testStatuses["remote_account_2_status_1"]
|
||||
requestingAccount := suite.testAccounts["admin_account"]
|
||||
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -1895,7 +1278,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownLanguage()
|
|||
*testStatus = *suite.testStatuses["admin_account_status_1"]
|
||||
testStatus.Language = ""
|
||||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -2056,7 +1439,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendPartialInteraction
|
|||
*testStatus = *suite.testStatuses["local_account_1_status_3"]
|
||||
testStatus.Language = ""
|
||||
requestingAccount := suite.testAccounts["admin_account"]
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount, gtsmodel.FilterContextNone)
|
||||
apiStatus, err := suite.typeconverter.StatusToAPIStatus(suite.T().Context(), testStatus, requestingAccount)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiStatus, "", " ")
|
||||
|
|
@ -2169,7 +1552,6 @@ func (suite *InternalToFrontendTestSuite) TestStatusToAPIStatusPendingApproval()
|
|||
suite.T().Context(),
|
||||
testStatus,
|
||||
requestingAccount,
|
||||
gtsmodel.FilterContextNone,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
|
|
@ -3933,10 +3315,9 @@ func (suite *InternalToFrontendTestSuite) TestIntReqToAPI() {
|
|||
|
||||
func (suite *InternalToFrontendTestSuite) TestConversationToAPISelfConvo() {
|
||||
var (
|
||||
ctx = suite.T().Context()
|
||||
requester = suite.testAccounts["local_account_1"]
|
||||
lastStatus = suite.testStatuses["local_account_1_status_1"]
|
||||
filters []*gtsmodel.Filter = nil
|
||||
ctx = suite.T().Context()
|
||||
requester = suite.testAccounts["local_account_1"]
|
||||
lastStatus = suite.testStatuses["local_account_1_status_1"]
|
||||
)
|
||||
|
||||
convo := >smodel.Conversation{
|
||||
|
|
@ -3954,7 +3335,6 @@ func (suite *InternalToFrontendTestSuite) TestConversationToAPISelfConvo() {
|
|||
ctx,
|
||||
convo,
|
||||
requester,
|
||||
filters,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
|
|
@ -4106,10 +3486,9 @@ func (suite *InternalToFrontendTestSuite) TestConversationToAPISelfConvo() {
|
|||
|
||||
func (suite *InternalToFrontendTestSuite) TestConversationToAPI() {
|
||||
var (
|
||||
ctx = suite.T().Context()
|
||||
requester = suite.testAccounts["local_account_1"]
|
||||
lastStatus = suite.testStatuses["local_account_1_status_1"]
|
||||
filters []*gtsmodel.Filter = nil
|
||||
ctx = suite.T().Context()
|
||||
requester = suite.testAccounts["local_account_1"]
|
||||
lastStatus = suite.testStatuses["local_account_1_status_1"]
|
||||
)
|
||||
|
||||
convo := >smodel.Conversation{
|
||||
|
|
@ -4129,7 +3508,6 @@ func (suite *InternalToFrontendTestSuite) TestConversationToAPI() {
|
|||
ctx,
|
||||
convo,
|
||||
requester,
|
||||
filters,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue