diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 754ef98..85a8a30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -41,6 +41,7 @@ v2.0.0 (IN PROGRESS) * Add trend admin functions (`admin_trending_tags`, `admin_trending_statuses`, `admin_trending_links`, `admin_approve_trending_link`, `admin_reject_trending_link`, `admin_approve_trending_status`, `admin_reject_trending_status`, `admin_approve_trending_tag`, `admin_reject_trending_tag`) * Add admin IP block functions (`admin_ip_blocks`, `admin_ip_block`, `admin_ip_block_create`, `admin_ip_block_delete`) * Add `instance_domain_blocks` +* Add notification policy and requests (`notifications_policy`, `update_notifications_policy`, `notifications_requests`, `notification_request`, `accept_notification_request`, `reject_notification_request`, `notifications_merged`, `accept_multiple_notification_requests`, `dismiss_multiple_notification_requests`) v1.8.1 ------ diff --git a/docs/09_notifications.rst b/docs/09_notifications.rst index 9a6eac2..9418f80 100644 --- a/docs/09_notifications.rst +++ b/docs/09_notifications.rst @@ -11,7 +11,7 @@ Reading ~~~~~~~ .. automethod:: Mastodon.notifications .. automethod:: Mastodon.notifications_unread_count - + Writing ~~~~~~~ .. automethod:: Mastodon.notifications_clear @@ -19,6 +19,22 @@ Writing .. automethod:: Mastodon.conversations_read +Source filtering for notifications +---------------------------------- +These functions allow you to get information about source filters as well as to create and update filters, and +to accept or reject notification requests for filtered notifications. + +.. authomedod:: Mastodon.notifications_policy +.. automethod:: Mastodon.update_notifications_policy +.. automethod:: Mastodon.notification_requests +.. automethod:: Mastodon.notification_request +.. automethod:: Mastodon.accept_notification_request +.. automethod:: Mastodon.dismiss_notification_request +.. automethod:: Mastodon.accept_multiple_notification_requests +.. automethod:: Mastodon.dismiss_multiple_notification_requests +.. automethod:: Mastodon.notifications_merged + + Keyword filters --------------- These functions allow you to get information about keyword filters as well as to create and update filters. @@ -26,6 +42,8 @@ These functions allow you to get information about keyword filters as well as to **Very Important Note: The filtering system was revised in 4.0.0. This means that these functions will now not work anymore if an instance is on Mastodon 4.0.0 or above. When updating Mastodon.py for 4.0.0, we'll make an effort to emulate old behaviour, but this will not always be possible. Consider these methods deprecated, for now.** +The filters are applied to everything - notifications, timeline, .... + Reading ~~~~~~~ .. automethod:: Mastodon.filters @@ -38,6 +56,7 @@ Writing .. automethod:: Mastodon.filter_update .. automethod:: Mastodon.filter_delete + Push notifications ------------------ Mastodon supports the delivery of notifications via webpush. diff --git a/docs/update_everything_page.sh b/docs/update_everything_page.sh index 89da4cc..6025846 100755 --- a/docs/update_everything_page.sh +++ b/docs/update_everything_page.sh @@ -6,3 +6,8 @@ echo ".. py:class: Mastodon" >> 15_everything.rst echo "" >> 15_everything.rst cat 01_general.rst 02_return_values.rst 03_errors.rst 04_auth.rst 05_statuses.rst 06_accounts.rst 07_timelines.rst 08_instances.rst 09_notifications.rst 10_streaming.rst 11_misc.rst 12_utilities.rst 13_admin.rst | grep -E "automethod|autoclass" >> 15_everything.rst echo "" >> 15_everything.rst + +# Now go through 15_everything.rst and add :no-index: to all the automethods and autoclasses +# This is because we don't want to index them, as they are already indexed in their own pages +sed -i 's/automethod::/automethod:: :no-index:/g' 15_everything.rst +sed -i 's/autoclass::/autoclass:: :no-index:/g' 15_everything.rst diff --git a/mastodon/internals.py b/mastodon/internals.py index 6b4bce3..f91530a 100644 --- a/mastodon/internals.py +++ b/mastodon/internals.py @@ -343,9 +343,10 @@ class Mastodon(): Internal streaming API helper. Returns the correct URL for the streaming API. - - Caches the URL. """ + if self.__streaming_base is not None: + return self.__streaming_base + # Try to support implementations that have no v1 endpoint (Sharkey does this) streaming_api_url = None try: diff --git a/mastodon/notifications.py b/mastodon/notifications.py index ee7b8d6..93c24f2 100644 --- a/mastodon/notifications.py +++ b/mastodon/notifications.py @@ -3,7 +3,7 @@ from mastodon.errors import MastodonIllegalArgumentError from mastodon.utility import api_version from mastodon.internals import Mastodon as Internals -from mastodon.return_types import Notification, IdType, PaginatableList, Account, UnreadNotificationsCount +from mastodon.return_types import Notification, IdType, PaginatableList, Account, UnreadNotificationsCount, NotificationPolicy, NotificationRequest from typing import Union, Optional, List class Mastodon(Internals): @@ -89,3 +89,97 @@ class Mastodon(Internals): else: params = self.__generate_params(locals()) self.__api_request('POST', '/api/v1/notifications/dismiss', params) + + + ## + # Notification policies + ## + + @api_version("4.3.0", "4.3.0") + def notifications_policy(self) -> NotificationPolicy: + """ + Fetch the user's notification filtering policy. Requires scope `read:notifications`. + """ + return self.__api_request('GET', '/api/v2/notifications/policy') + + @api_version("4.3.0", "4.3.0") + def update_notifications_policy(self, for_not_following: Optional[str] = None, for_not_followers: Optional[str] = None, + for_new_accounts: Optional[str] = None, for_private_mentions: Optional[str] = None, + for_limited_accounts: Optional[str] = None) -> NotificationPolicy: + """ + Update the user's notification filtering policy. Requires scope `write:notifications`. + + - `for_not_following`: "accept", "filter", or "drop" notifications from non-followed accounts. + - `for_not_followers`: "accept", "filter", or "drop" notifications from non-followers. + - `for_new_accounts`: "accept", "filter", or "drop" notifications from accounts created in the past 30 days. + - `for_private_mentions`: "accept", "filter", or "drop" notifications from private mentions. + - `for_limited_accounts`: "accept", "filter", or "drop" notifications from accounts limited by moderators. + """ + params = self.__generate_params(locals()) + return self.__api_request('PATCH', '/api/v2/notifications/policy', params) + + ## + # Notification requests + ## + @api_version("4.3.0", "4.3.0") + def notification_requests(self, max_id: Optional[IdType] = None, since_id: Optional[IdType] = None, + min_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[NotificationRequest]: + """ + Fetch notification requests filtered by the user's policy. Requires scope `read:notifications`. + + NB: Notification requests are what happens when the user has set their policy to filter notifications from some source. + """ + params = self.__generate_params(locals()) + return self.__api_request('GET', '/api/v1/notifications/requests', params) + + @api_version("4.3.0", "4.3.0") + def notification_request(self, id: Union[NotificationRequest, IdType]) -> NotificationRequest: + """ + Fetch a single notification request by ID. Requires scope `read:notifications`. + """ + id = self.__unpack_id(id) + return self.__api_request('GET', f'/api/v1/notifications/requests/{id}') + + @api_version("4.3.0", "4.3.0") + def accept_notification_request(self, id: Union[NotificationRequest, IdType]) -> None: + """ + Accept a notification request. This moves filtered notifications from a user back into the main notifications feed + and allows future notifications from them. Requires scope `write:notifications`. + """ + id = self.__unpack_id(id) + self.__api_request('POST', f'/api/v1/notifications/requests/{id}/accept') + + @api_version("4.3.0", "4.3.0") + def dismiss_notification_request(self, id: Union[NotificationRequest, IdType]) -> None: + """ + Dismiss a notification request, removing it from pending requests. Requires scope `write:notifications`. + """ + id = self.__unpack_id(id) + self.__api_request('POST', f'/api/v1/notifications/requests/{id}/dismiss') + + @api_version("4.3.0", "4.3.0") + def accept_multiple_notification_requests(self, ids: List[Union[NotificationRequest, IdType]]) -> None: + """ + Accept multiple notification requests at once. This moves filtered notifications from those users back into + the main notifications feed and allows future notifications from them. Requires scope `write:notifications`. + """ + params = self.__generate_params({"id[]": [self.__unpack_id(i) for i in ids]}) + self.__api_request('POST', '/api/v1/notifications/requests/accept', params) + + @api_version("4.3.0", "4.3.0") + def dismiss_multiple_notification_requests(self, ids: List[Union[NotificationRequest, IdType]]) -> None: + """ + Dismiss multiple notification requests, removing them from pending requests. Requires scope `write:notifications`. + """ + params = self.__generate_params({"id[]": [self.__unpack_id(i) for i in ids]}) + self.__api_request('POST', '/api/v1/notifications/requests/dismiss', params) + + @api_version("4.3.0", "4.3.0") + def notifications_merged(self) -> bool: + """ + Check whether accepted notification requests have been merged into the main notification feed. + Accepting a notification request schedules a background job that merges the filtered notifications. + Clients can poll this endpoint to check if the merge has completed. Requires scope `read:notifications`. + """ + result = self.__api_request('GET', '/api/v1/notifications/requests/merged', override_type = dict) + return result["merged"] diff --git a/mastodon/return_types.py b/mastodon/return_types.py index 9b3d242..d0095d2 100644 --- a/mastodon/return_types.py +++ b/mastodon/return_types.py @@ -1301,6 +1301,38 @@ class Tag(AttribAccessDict): * 4.0.0: added """ + id: "Optional[str]" + """ + The ID of the Tag in the database. Only present for data returned from admin endpoints. (optional) + + Version history: + * 3.5.0: added + """ + + trendable: "Optional[bool]" + """ + Whether the hashtag has been approved to trend. Only present for data returned from admin endpoints. (optional) + + Version history: + * 3.5.0: added + """ + + usable: "Optional[bool]" + """ + Whether the hashtag has not been disabled from auto-linking. Only present for data returned from admin endpoints. (optional) + + Version history: + * 3.5.0: added + """ + + requires_review: "Optional[bool]" + """ + Whether the hashtag has not been reviewed yet to approve or deny its trending. Only present for data returned from admin endpoints. (optional) + + Version history: + * 3.5.0: added + """ + _version = "4.0.0" class TagHistory(AttribAccessDict): @@ -1796,6 +1828,7 @@ class Notification(AttribAccessDict): * 3.3.0: added `status` * 3.5.0: added `update` and `admin.sign_up` * 4.0.0: added `admin.report` + * 4.3.0: added `severed_relationships` and `moderation_warning` """ created_at: "datetime" @@ -1831,6 +1864,30 @@ class Notification(AttribAccessDict): * 4.3.0: added """ + report: "Optional[Report]" + """ + Report that was the object of the notification. (optional) + + Version history: + * 4.0.0: added + """ + + event: "Optional[RelationshipSeveranceEvent]" + """ + Summary of the event that caused follow relationships to be severed. (optional) + + Version history: + * 4.3.0: added + """ + + moderation_warning: "Optional[AccountWarning]" + """ + Moderation warning that caused the notification. (optional) + + Version history: + * 4.3.0: added + """ + _version = "4.3.0" class Context(AttribAccessDict): @@ -5539,7 +5596,7 @@ class AdminIpBlock(AttribAccessDict): .. code-block:: python # Returns a AdminIpBlock object - TODO_TO_BE_IMPLEMENTED + mastodon.admin_ip_blocks()[0] See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/Admin_IpBlock """ @@ -5603,7 +5660,7 @@ class DomainBlock(AttribAccessDict): .. code-block:: python # Returns a DomainBlock object - TODO_TO_BE_IMPLEMENTED + mastodon.instance_domain_blocks()[0] See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/DomainBlock """ @@ -6589,6 +6646,70 @@ class Appeal(AttribAccessDict): _version = "4.3.0" +class NotificationRequest(AttribAccessDict): + """ + Represents a group of filtered notifications from a specific user. + + Example: + + .. code-block:: python + + # Returns a NotificationRequest object + mastodon.notification_requests()[0] + + See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/NotificationRequest + """ + + id: "str" + """ + The ID of the notification request in the database. + + Version history: + * 4.3.0: added + """ + + created_at: "datetime" + """ + When the first filtered notification from that user was created. + + Version history: + * 4.3.0: added + """ + + updated_at: "datetime" + """ + When the notification request was last updated. + + Version history: + * 4.3.0: added + """ + + account: "Account" + """ + The account that performed the action that generated the filtered notifications. + + Version history: + * 4.3.0: added + """ + + notifications_count: "str" + """ + How many of this account’s notifications were filtered. + + Version history: + * 4.3.0: added + """ + + last_status: "Optional[Status]" + """ + Most recent status associated with a filtered notification from that account. (optional) + + Version history: + * 4.3.0: added + """ + + _version = "4.3.0" + ENTITY_NAME_MAP = { "Account": Account, "AccountField": AccountField, @@ -6699,6 +6820,7 @@ ENTITY_NAME_MAP = { "AccountWarning": AccountWarning, "UnreadNotificationsCount": UnreadNotificationsCount, "Appeal": Appeal, + "NotificationRequest": NotificationRequest, } __all__ = [ "Account", @@ -6810,5 +6932,6 @@ __all__ = [ "AccountWarning", "UnreadNotificationsCount", "Appeal", + "NotificationRequest", ] diff --git a/mastodon/types_base.py b/mastodon/types_base.py index 014dd89..ee8425c 100644 --- a/mastodon/types_base.py +++ b/mastodon/types_base.py @@ -163,7 +163,7 @@ if sys.version_info < (3, 9): DomainBlock, ExtendedDescription, FilterKeyword, FilterStatus, IdentityProof, StatusSource, \ Suggestion, Translation, AccountCreationError, AccountCreationErrorDetails, AccountCreationErrorDetailsField, NotificationPolicy, \ NotificationPolicySummary, RelationshipSeveranceEvent, GroupedNotificationsResults, PartialAccountWithAvatar, NotificationGroup, AccountWarning, \ - UnreadNotificationsCount, Appeal, TrendingLinkHistory + UnreadNotificationsCount, Appeal, TrendingLinkHistory, NotificationRequest if isinstance(t, ForwardRef): try: t = t._evaluate(globals(), locals(), frozenset()) @@ -520,6 +520,10 @@ class AttribAccessDict(OrderedStrDict, Entity): While the subclasses implement specific fields with proper typing, parsing and documentation, they all inherit from this class, and parsing is extremely permissive to allow for forward and backward compatibility as well as compatibility with other implementations of the Mastodon API. + + This class can ALSO have pagination information attached, for paginating lists *inside* the object, + because that's what Mastodon 4.3.0 does for groupee notifications. This is special cased in the class + definition, though. """ def __init__(self, **kwargs): """ diff --git a/srcgen/return_types.json b/srcgen/return_types.json index 6c412ba..99c1c1f 100644 --- a/srcgen/return_types.json +++ b/srcgen/return_types.json @@ -2895,6 +2895,10 @@ [ "4.0.0", "added `admin.report`" + ], + [ + "4.3.0", + "added `severed_relationships` and `moderation_warning`" ] ], "enum": { @@ -2971,6 +2975,36 @@ ] ], "enum": null + }, + "report": { + "description": "Report that was the object of the notification.", + "enum": null, + "version_history": [["4.0.0", "added"]], + "field_type": "Report", + "field_subtype": null, + "field_structuretype": null, + "is_optional": true, + "is_nullable": false + }, + "event": { + "description": "Summary of the event that caused follow relationships to be severed.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "RelationshipSeveranceEvent", + "field_subtype": null, + "field_structuretype": null, + "is_optional": true, + "is_nullable": false + }, + "moderation_warning": { + "description": "Moderation warning that caused the notification.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "AccountWarning", + "field_subtype": null, + "field_structuretype": null, + "is_optional": true, + "is_nullable": false } } }, @@ -9179,7 +9213,7 @@ { "name": "Notification Policy", "python_name": "NotificationPolicy", - "func_call": "TODO_TO_BE_IMPLEMENTED", + "func_call": "mastodon.notification_policy()", "func_call_real": null, "func_call_additional": null, "func_alternate_acc": null, @@ -9271,7 +9305,7 @@ { "name": "Notification Policy Summary", "python_name": "NotificationPolicySummary", - "func_call": "TODO_TO_BE_IMPLEMENTED", + "func_call": "mastodon.notification_policy().summary", "func_call_real": null, "func_call_additional": null, "func_alternate_acc": null, @@ -9818,5 +9852,79 @@ "is_nullable": false } } + }, + { + "name": "Notification Request", + "python_name": "NotificationRequest", + "func_call": "mastodon.notification_requests()[0]", + "func_call_real": null, + "func_call_additional": null, + "func_alternate_acc": null, + "manual_update": true, + "masto_doc_link": "https://docs.joinmastodon.org/entities/NotificationRequest", + "description": "Represents a group of filtered notifications from a specific user.", + "fields": { + "id": { + "description": "The ID of the notification request in the database.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "str", + "field_subtype": null, + "field_structuretype": null, + "is_optional": false, + "is_nullable": false + }, + "created_at": { + "description": "When the first filtered notification from that user was created.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "datetime", + "field_subtype": null, + "field_structuretype": null, + "is_optional": false, + "is_nullable": false + }, + "updated_at": { + "description": "When the notification request was last updated.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "datetime", + "field_subtype": null, + "field_structuretype": null, + "is_optional": false, + "is_nullable": false + }, + "account": { + "description": "The account that performed the action that generated the filtered notifications.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "Account", + "field_subtype": null, + "field_structuretype": null, + "is_optional": false, + "is_nullable": false + }, + "notifications_count": { + "description": "How many of this account’s notifications were filtered.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "str", + "field_subtype": null, + "field_structuretype": null, + "is_optional": false, + "is_nullable": false + }, + "last_status": { + "description": "Most recent status associated with a filtered notification from that account.", + "enum": null, + "version_history": [["4.3.0", "added"]], + "field_type": "Status", + "field_subtype": null, + "field_structuretype": null, + "is_optional": true, + "is_nullable": false + } + } } + ] diff --git a/tests/cassettes/test_notification_requests_accept.yaml b/tests/cassettes/test_notification_requests_accept.yaml new file mode 100644 index 0000000..5155491 --- /dev/null +++ b/tests/cassettes/test_notification_requests_accept.yaml @@ -0,0 +1,1204 @@ +interactions: +- request: + body: for_not_following=filter&for_not_followers=filter&for_new_accounts=filter&for_private_mentions=filter&for_limited_accounts=filter + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + Content-Length: + - '129' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: PATCH + uri: http://localhost:3000/api/v2/notifications/policy + response: + body: + string: '{"for_not_following":"filter","for_not_followers":"filter","for_new_accounts":"filter","for_private_mentions":"filter","for_limited_accounts":"filter","summary":{"pending_requests_count":0,"pending_notifications_count":0}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '222' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"cfaa13a484fe5fa09cfbb33de92ae61c" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.03, sql.active_record;dur=1.29, cache_generate.active_support;dur=1.24, + cache_write.active_support;dur=0.09, instantiation.active_record;dur=0.50, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=0.16, + process_action.action_controller;dur=31.33 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.025920Z' + X-Request-Id: + - 2d67d993-cb71-4bc9-a73a-35c26764f719 + X-Runtime: + - '0.048006' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/accounts/verify_credentials + response: + body: + string: '{"id":"114009019326978043","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@mastodonpy_test","uri":"http://localhost:3000/users/mastodonpy_test","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":0,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"source":{"privacy":"private","sensitive":false,"language":null,"note":"","fields":[],"follow_requests_count":0,"hide_collections":null,"discoverable":null,"indexable":false},"emojis":[],"roles":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1010' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"3314b09acbd7153ce5f8baa05f93e7f2" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=1.56, cache_generate.active_support;dur=2.08, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.34, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=9.77, + process_action.action_controller;dur=28.19 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.072819Z' + X-Request-Id: + - 3de3da97-f58d-444b-a043-9738b27bda14 + X-Runtime: + - '0.043254' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/notifications/requests/merged + response: + body: + string: '{"merged":true}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '15' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"254a7899941cc42d5cfdb942104f871a" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.02, sql.active_record;dur=0.51, cache_generate.active_support;dur=1.04, + cache_write.active_support;dur=0.06, instantiation.active_record;dur=0.19, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=0.03, + process_action.action_controller;dur=17.48 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.140243Z' + X-Request-Id: + - 0287b152-6308-47c7-917d-680fd397fd91 + X-Runtime: + - '0.036540' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/accounts/verify_credentials + response: + body: + string: '{"id":"114009019326978043","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@mastodonpy_test","uri":"http://localhost:3000/users/mastodonpy_test","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":0,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"source":{"privacy":"private","sensitive":false,"language":null,"note":"","fields":[],"follow_requests_count":0,"hide_collections":null,"discoverable":null,"indexable":false},"emojis":[],"roles":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1010' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"3314b09acbd7153ce5f8baa05f93e7f2" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.04, sql.active_record;dur=1.16, cache_generate.active_support;dur=1.58, + cache_write.active_support;dur=0.11, instantiation.active_record;dur=0.37, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=8.45, + process_action.action_controller;dur=26.99 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.177566Z' + X-Request-Id: + - 997d639a-0f91-4b74-8689-690e9add99d2 + X-Runtime: + - '0.041395' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: status=%40mastodonpy_test+please+follow+me+-+200%21&visibility=public + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: + string: '{"id":"114009197854516155","created_at":"2025-02-15T17:58:21.249Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197854516155","url":"http://localhost:3000/@admin/114009197854516155","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 200!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":6,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1819' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"e3ba4da547435e510856b7cb81467176" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=11.84, cache_generate.active_support;dur=2.87, + cache_write.active_support;dur=0.15, instantiation.active_record;dur=0.69, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=10.97, + render.active_model_serializers;dur=11.88, process_action.action_controller;dur=56.58 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '248' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.278469Z' + X-Request-Id: + - 5c7b30cf-bc92-4c34-b37e-1abc729d77a8 + X-Runtime: + - '0.071488' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: status=%40mastodonpy_test+please+follow+me+-+201%21&visibility=public + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: + string: '{"id":"114009197861174487","created_at":"2025-02-15T17:58:21.350Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197861174487","url":"http://localhost:3000/@admin/114009197861174487","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 201!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":7,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1819' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"633f4b1af93f3dedff51210940c1252a" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=10.89, cache_generate.active_support;dur=4.41, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.67, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=9.89, + render.active_model_serializers;dur=12.58, process_action.action_controller;dur=52.55 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '247' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.376756Z' + X-Request-Id: + - 76e457cf-a2d3-411e-a485-b556e95965fd + X-Runtime: + - '0.069270' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: status=%40mastodonpy_test+please+follow+me+-+202%21&visibility=public + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: + string: '{"id":"114009197869446405","created_at":"2025-02-15T17:58:21.476Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197869446405","url":"http://localhost:3000/@admin/114009197869446405","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 202!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":8,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1819' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"bf5398974cb82b4de77077b8a072fb58" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=11.93, cache_generate.active_support;dur=3.00, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.65, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=16.93, + render.active_model_serializers;dur=10.90, process_action.action_controller;dur=58.47 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '246' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.509216Z' + X-Request-Id: + - 12a49307-8aff-4760-99a0-477cd94d7730 + X-Runtime: + - '0.072824' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: status=%40mastodonpy_test+please+follow+me+-+203%21&visibility=public + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: + string: '{"id":"114009197876239442","created_at":"2025-02-15T17:58:21.581Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197876239442","url":"http://localhost:3000/@admin/114009197876239442","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 203!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":9,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1819' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"88c1f9c95593c169c3643a9b3861da99" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=13.56, cache_generate.active_support;dur=2.71, + cache_write.active_support;dur=0.13, instantiation.active_record;dur=0.75, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=9.34, + render.active_model_serializers;dur=13.18, process_action.action_controller;dur=55.06 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '245' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.609062Z' + X-Request-Id: + - 488dd2d0-bcbd-4338-88df-16c3680699f4 + X-Runtime: + - '0.070023' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: status=%40mastodonpy_test+please+follow+me+-+204%21&visibility=public + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/statuses + response: + body: + string: '{"id":"114009197882813642","created_at":"2025-02-15T17:58:21.679Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197882813642","url":"http://localhost:3000/@admin/114009197882813642","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 204!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":10,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1820' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"ef5aff982aea05ed44d91bf58fcc47d9" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=15.01, cache_generate.active_support;dur=2.74, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.74, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=8.89, + render.active_model_serializers;dur=12.21, process_action.action_controller;dur=55.28 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '244' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.709085Z' + X-Request-Id: + - 34a129eb-fe24-4bc3-a21b-bf2ae808e1a4 + X-Runtime: + - '0.070620' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/notifications/requests + response: + body: + string: '[{"id":"114009197966428919","created_at":"2025-02-15T17:58:22.959Z","updated_at":"2025-02-15T17:58:23.344Z","notifications_count":"5","account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":10,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"last_status":{"id":"114009197882813642","created_at":"2025-02-15T17:58:21.679Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197882813642","url":"http://localhost:3000/@admin/114009197882813642","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 204!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":10,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}}]' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '2716' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"58f6878305c677d2ba6b378f596f67ff" + Link: + - ; + rel="prev" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.11, sql.active_record;dur=4.68, cache_generate.active_support;dur=3.30, + cache_write.active_support;dur=0.14, instantiation.active_record;dur=1.23, + start_processing.action_controller;dur=0.00, cache_fetch_hit.active_support;dur=0.00, + render.active_model_serializers;dur=5.31, process_action.action_controller;dur=49.35 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.772803Z' + X-Request-Id: + - 2d10aebf-83a6-4482-9260-b5fe3655ac46 + X-Runtime: + - '0.064098' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/notifications/requests/114009197966428919 + response: + body: + string: '{"id":"114009197966428919","created_at":"2025-02-15T17:58:22.959Z","updated_at":"2025-02-15T17:58:23.344Z","notifications_count":"5","account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":10,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"last_status":{"id":"114009197882813642","created_at":"2025-02-15T17:58:21.679Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197882813642","url":"http://localhost:3000/@admin/114009197882813642","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"content":"\u003cp\u003e\u003cspan + class=\"h-card\" translate=\"no\"\u003e\u003ca href=\"http://localhost:3000/@mastodonpy_test\" + class=\"u-url mention\"\u003e@\u003cspan\u003emastodonpy_test\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e + please follow me - 204!\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":10,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '2714' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"81f609371ec385311b233ae5a0becf0d" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.09, sql.active_record;dur=3.11, cache_generate.active_support;dur=2.87, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.83, + start_processing.action_controller;dur=0.00, cache_fetch_hit.active_support;dur=0.00, + render.active_model_serializers;dur=22.99, process_action.action_controller;dur=41.92 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.881083Z' + X-Request-Id: + - f77ff390-2039-420d-b333-bc916c95c92b + X-Runtime: + - '0.057385' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: POST + uri: http://localhost:3000/api/v1/notifications/requests/114009197966428919/accept + response: + body: + string: '{}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '2' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"44136fa355b3678a1146ad16f7e8649e" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.03, sql.active_record;dur=7.14, cache_generate.active_support;dur=1.04, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.57, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=8.72, + render.active_model_serializers;dur=0.03, process_action.action_controller;dur=33.68 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.989866Z' + X-Request-Id: + - 9ad1c92a-0dde-4ece-99c5-f2eb4d2bbdb4 + X-Runtime: + - '0.056990' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v1/notifications/requests/merged + response: + body: + string: '{"merged":true}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '15' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"254a7899941cc42d5cfdb942104f871a" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.02, sql.active_record;dur=0.68, cache_generate.active_support;dur=1.06, + cache_write.active_support;dur=0.08, instantiation.active_record;dur=0.21, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=0.03, + process_action.action_controller;dur=18.26 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.045914Z' + X-Request-Id: + - 4a0cd51e-652d-49e6-b951-3706b78b87a9 + X-Runtime: + - '0.032595' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/statuses/114009197854516155 + response: + body: + string: '{"id":"114009197854516155","created_at":"2025-02-15T17:58:21.249Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197854516155","url":"http://localhost:3000/@admin/114009197854516155","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"text":"@mastodonpy_test + please follow me - 200!","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":9,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1590' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"2f7f5fe1e55b55052de9ecf13bfe66a3" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=7.37, cache_generate.active_support;dur=2.98, + cache_write.active_support;dur=0.16, instantiation.active_record;dur=0.90, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=4.01, + render.active_model_serializers;dur=18.55, process_action.action_controller;dur=46.68 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '30' + X-RateLimit-Remaining: + - '29' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.082840Z' + X-Request-Id: + - b9745b84-0cd2-4d9b-9472-6571fa9d5eff + X-Runtime: + - '0.061322' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/statuses/114009197861174487 + response: + body: + string: '{"id":"114009197861174487","created_at":"2025-02-15T17:58:21.350Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197861174487","url":"http://localhost:3000/@admin/114009197861174487","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"text":"@mastodonpy_test + please follow me - 201!","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":8,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1590' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"c6128f606596affb32fd1034417d5ef2" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.06, sql.active_record;dur=13.76, cache_generate.active_support;dur=2.77, + cache_write.active_support;dur=0.20, instantiation.active_record;dur=0.84, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=2.98, + render.active_model_serializers;dur=21.94, process_action.action_controller;dur=51.28 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '30' + X-RateLimit-Remaining: + - '29' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.179964Z' + X-Request-Id: + - fdf59cc1-1c56-4852-aefc-65bc27c3f4ca + X-Runtime: + - '0.068017' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/statuses/114009197869446405 + response: + body: + string: '{"id":"114009197869446405","created_at":"2025-02-15T17:58:21.476Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197869446405","url":"http://localhost:3000/@admin/114009197869446405","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"text":"@mastodonpy_test + please follow me - 202!","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":7,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1590' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"f91aefd13d9d1b20edf061955dc97214" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.06, sql.active_record;dur=6.36, cache_generate.active_support;dur=7.37, + cache_write.active_support;dur=0.18, instantiation.active_record;dur=5.52, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=2.96, + render.active_model_serializers;dur=30.15, process_action.action_controller;dur=64.73 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '30' + X-RateLimit-Remaining: + - '29' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.280977Z' + X-Request-Id: + - 160f87be-a4b2-4ee0-acf9-45b3359525cc + X-Runtime: + - '0.084145' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/statuses/114009197876239442 + response: + body: + string: '{"id":"114009197876239442","created_at":"2025-02-15T17:58:21.581Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197876239442","url":"http://localhost:3000/@admin/114009197876239442","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"text":"@mastodonpy_test + please follow me - 203!","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":6,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1590' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"a92e76b29b2c165417290120fd92523e" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.05, sql.active_record;dur=5.14, cache_generate.active_support;dur=7.01, + cache_write.active_support;dur=0.15, instantiation.active_record;dur=0.89, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=2.72, + render.active_model_serializers;dur=16.42, process_action.action_controller;dur=42.53 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '30' + X-RateLimit-Remaining: + - '29' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.392582Z' + X-Request-Id: + - c4ba9c0c-4a7d-42af-b1d4-63d467e84d07 + X-Runtime: + - '0.059185' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - tests/v311 + method: DELETE + uri: http://localhost:3000/api/v1/statuses/114009197882813642 + response: + body: + string: '{"id":"114009197882813642","created_at":"2025-02-15T17:58:21.679Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:3000/users/admin/statuses/114009197882813642","url":"http://localhost:3000/@admin/114009197882813642","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"text":"@mastodonpy_test + please follow me - 204!","filtered":[],"reblog":null,"application":{"name":"Mastodon.py + test suite","website":null},"account":{"id":"114009019031846737","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"discoverable":null,"indexable":false,"group":false,"created_at":"2025-02-15T00:00:00.000Z","note":"","url":"http://localhost:3000/@admin","uri":"http://localhost:3000/users/admin","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":6,"last_status_at":"2025-02-15","hide_collections":null,"noindex":false,"emojis":[],"roles":[{"id":"3","name":"Owner","color":""}],"fields":[]},"media_attachments":[],"mentions":[{"id":"114009019326978043","username":"mastodonpy_test","url":"http://localhost:3000/@mastodonpy_test","acct":"mastodonpy_test"}],"tags":[],"emojis":[],"card":null,"poll":null}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '1590' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"45ee169beab9458bef8a61372b2fe3f3" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.06, sql.active_record;dur=6.35, cache_generate.active_support;dur=2.92, + cache_write.active_support;dur=0.16, instantiation.active_record;dur=0.86, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=3.60, + render.active_model_serializers;dur=15.61, process_action.action_controller;dur=42.99 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '30' + X-RateLimit-Remaining: + - '29' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.479212Z' + X-Request-Id: + - ff127d74-3057-4f3e-a97f-ae644c82c76b + X-Runtime: + - '0.057640' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: for_not_following=accept&for_not_followers=accept&for_new_accounts=accept&for_private_mentions=accept&for_limited_accounts=accept + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN + Connection: + - keep-alive + Content-Length: + - '129' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: PATCH + uri: http://localhost:3000/api/v2/notifications/policy + response: + body: + string: '{"for_not_following":"accept","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"accept","for_limited_accounts":"accept","summary":{"pending_requests_count":0,"pending_notifications_count":0}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '222' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"5b34317397a0dd74b93479cc531b50b9" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.02, sql.active_record;dur=10.04, cache_generate.active_support;dur=0.87, + cache_write.active_support;dur=0.07, instantiation.active_record;dur=0.30, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=3.44, + render.active_model_serializers;dur=0.17, process_action.action_controller;dur=30.85 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T18:00:00.566953Z' + X-Request-Id: + - 35829675-146f-4aca-9696-e1d81fd99811 + X-Runtime: + - '0.045254' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_notifications_policy.yaml b/tests/cassettes/test_notifications_policy.yaml new file mode 100644 index 0000000..b9ded96 --- /dev/null +++ b/tests/cassettes/test_notifications_policy.yaml @@ -0,0 +1,187 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + User-Agent: + - tests/v311 + method: GET + uri: http://localhost:3000/api/v2/notifications/policy + response: + body: + string: '{"for_not_following":"filter","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"filter","for_limited_accounts":"filter","summary":{"pending_requests_count":0,"pending_notifications_count":0}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '222' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"017d15acc00d01e7395f157390a5994c" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.03, sql.active_record;dur=15.61, cache_generate.active_support;dur=2.11, + cache_write.active_support;dur=0.21, instantiation.active_record;dur=23.59, + start_processing.action_controller;dur=0.01, render.active_model_serializers;dur=0.42, + process_action.action_controller;dur=70.04 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T17:15:00.275621Z' + X-Request-Id: + - ca548381-9b3f-4fe5-b62a-ff679ec7960a + X-Runtime: + - '0.128868' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: for_not_following=filter&for_not_followers=accept + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '49' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: PATCH + uri: http://localhost:3000/api/v2/notifications/policy + response: + body: + string: '{"for_not_following":"filter","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"filter","for_limited_accounts":"filter","summary":{"pending_requests_count":0,"pending_notifications_count":0}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '222' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"017d15acc00d01e7395f157390a5994c" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.02, sql.active_record;dur=1.37, cache_generate.active_support;dur=1.27, + cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.34, + start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=0.18, + process_action.action_controller;dur=27.26 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T17:15:00.373556Z' + X-Request-Id: + - f09de8b0-6938-48c6-b66d-d29eb0a777f5 + X-Runtime: + - '0.051088' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +- request: + body: for_not_following=accept&for_not_followers=accept&for_new_accounts=accept&for_private_mentions=accept&for_limited_accounts=accept + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2 + Connection: + - keep-alive + Content-Length: + - '129' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - tests/v311 + method: PATCH + uri: http://localhost:3000/api/v2/notifications/policy + response: + body: + string: '{"for_not_following":"accept","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"accept","for_limited_accounts":"accept","summary":{"pending_requests_count":0,"pending_notifications_count":0}}' + headers: + Cache-Control: + - private, no-store + Content-Length: + - '222' + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none'; form-action 'none' + Content-Type: + - application/json; charset=utf-8 + ETag: + - W/"5b34317397a0dd74b93479cc531b50b9" + Referrer-Policy: + - strict-origin-when-cross-origin + Server-Timing: + - cache_read.active_support;dur=0.02, sql.active_record;dur=5.47, cache_generate.active_support;dur=1.23, + cache_write.active_support;dur=0.13, instantiation.active_record;dur=0.52, + start_processing.action_controller;dur=0.00, transaction.active_record;dur=4.72, + render.active_model_serializers;dur=0.29, process_action.action_controller;dur=35.37 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-RateLimit-Limit: + - '300' + X-RateLimit-Remaining: + - '299' + X-RateLimit-Reset: + - '2025-02-15T17:15:00.497607Z' + X-Request-Id: + - a5ea3462-c2bc-481e-83eb-fd338c8638cf + X-Runtime: + - '0.124773' + X-XSS-Protection: + - '0' + vary: + - Authorization, Origin + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_notifications.py b/tests/test_notifications.py index 97afade..d72cd84 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -80,3 +80,66 @@ def test_notifications_dismiss_pre_2_9_2(api, api2): def test_notifications_clear(api): api.notifications_clear() + +@pytest.mark.vcr(match_on=['path']) +def test_notifications_policy(api2): + """Test fetching and updating the notifications policy.""" + # Fetch current policy + policy = api2.notifications_policy() + assert policy is not None + + # Update policy + updated_policy = api2.update_notifications_policy(for_not_following="filter", for_not_followers="accept") + assert updated_policy.for_not_following == "filter" + assert updated_policy.for_not_followers == "accept" + + # Finally, change it to everything being accepted + updated_policy = api2.update_notifications_policy(for_not_following="accept", for_not_followers="accept", for_new_accounts="accept", for_limited_accounts="accept", for_private_mentions="accept") + +@pytest.mark.vcr() +def test_notification_requests_accept(api, api2): + """Test fetching, accepting, and dismissing notification requests.""" + temp = api + api = api2 + api2 = temp + + # Generate some request + posted = [] + api2.update_notifications_policy(for_not_following="filter", for_not_followers="filter", for_new_accounts="filter", for_limited_accounts="filter", for_private_mentions="filter") + time.sleep(1) + + while not api2.notifications_merged(): + time.sleep(1) + print("Waiting for notifications to merge...") + time.sleep(1) + + try: + reply_name = api2.account_verify_credentials().username + for i in range(5): + posted.append(api.status_post(f"@{reply_name} please follow me - {i+200}!", visibility="public")) + + time.sleep(3) + + # Fetch notification requests + requests = api2.notification_requests() + assert requests is not None + assert len(requests) > 0 + + request_id = requests[0].id + + # Fetch a single request + single_request = api2.notification_request(request_id) + assert single_request.id == request_id + + # Accept the request + api2.accept_notification_request(request_id) + time.sleep(5) + + # Check if notifications have been merged + merged_status = api2.notifications_merged() + assert isinstance(merged_status, bool) + assert merged_status == True + finally: + for status in posted: + api.status_delete(status) + api2.update_notifications_policy(for_not_following="accept", for_not_followers="accept", for_new_accounts="accept", for_limited_accounts="accept", for_private_mentions="accept")