diff options
-rw-r--r-- | CHANGELOG.rst | 1 | ||||
-rw-r--r-- | TODO.md | 2 | ||||
-rw-r--r-- | mastodon/Mastodon.py | 106 | ||||
-rw-r--r-- | tests/cassettes/test_app_account_create_invalid.yaml | 245 | ||||
-rw-r--r-- | tests/cassettes/test_push_set.yaml | 122 | ||||
-rw-r--r-- | tests/test_create_app.py | 23 |
6 files changed, 331 insertions, 168 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fb69ded..7526873 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst | |||
@@ -8,6 +8,7 @@ v1.6.3 | |||
8 | * Add confirmation email resend API (`email_resend_confirmation`) | 8 | * Add confirmation email resend API (`email_resend_confirmation`) |
9 | * Add account lookup API (`account_lookup`) | 9 | * Add account lookup API (`account_lookup`) |
10 | * Add `policy` param to control notification sources for `push_subscription_set` | 10 | * Add `policy` param to control notification sources for `push_subscription_set` |
11 | * Add ability to get detailed signup error to `create_account` | ||
11 | 12 | ||
12 | v1.6.2 | 13 | v1.6.2 |
13 | ------ | 14 | ------ |
@@ -33,7 +33,7 @@ Refer to mastodon changelog and API docs for details when implementing, add or m | |||
33 | * [x] Add POST /api/v1/emails/confirmations to REST API | 33 | * [x] Add POST /api/v1/emails/confirmations to REST API |
34 | * [x] Add GET /api/v1/accounts/lookup to REST API | 34 | * [x] Add GET /api/v1/accounts/lookup to REST API |
35 | * [x] Add policy param to POST /api/v1/push/subscriptions in REST API | 35 | * [x] Add policy param to POST /api/v1/push/subscriptions in REST API |
36 | * [ ] Add details to error response for POST /api/v1/accounts in REST API | 36 | * [x] Add details to error response for POST /api/v1/accounts in REST API |
37 | 37 | ||
38 | 3.4.2 | 38 | 3.4.2 |
39 | ----- | 39 | ----- |
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 12a054a..ae560c5 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py | |||
@@ -605,8 +605,7 @@ class Mastodon: | |||
605 | params['scope'] = " ".join(scopes) | 605 | params['scope'] = " ".join(scopes) |
606 | 606 | ||
607 | try: | 607 | try: |
608 | response = self.__api_request( | 608 | response = self.__api_request('POST', '/oauth/token', params, do_ratelimiting=False) |
609 | 'POST', '/oauth/token', params, do_ratelimiting=False) | ||
610 | self.access_token = response['access_token'] | 609 | self.access_token = response['access_token'] |
611 | self.__set_refresh_token(response.get('refresh_token')) | 610 | self.__set_refresh_token(response.get('refresh_token')) |
612 | self.__set_token_expired(int(response.get('expires_in', 0))) | 611 | self.__set_token_expired(int(response.get('expires_in', 0))) |
@@ -658,8 +657,8 @@ class Mastodon: | |||
658 | self.access_token = None | 657 | self.access_token = None |
659 | self.__logged_in_id = None | 658 | self.__logged_in_id = None |
660 | 659 | ||
661 | @api_version("2.7.0", "2.7.0", "2.7.0") | 660 | @api_version("2.7.0", "2.7.0", "3.4.0") |
662 | def create_account(self, username, password, email, agreement=False, reason=None, locale="en", scopes=__DEFAULT_SCOPES, to_file=None): | 661 | def create_account(self, username, password, email, agreement=False, reason=None, locale="en", scopes=__DEFAULT_SCOPES, to_file=None, return_detailed_error=False): |
663 | """ | 662 | """ |
664 | Creates a new user account with the given username, password and email. "agreement" | 663 | Creates a new user account with the given username, password and email. "agreement" |
665 | must be set to true (after showing the user the instance's user agreement and having | 664 | must be set to true (after showing the user the instance's user agreement and having |
@@ -674,6 +673,26 @@ class Mastodon: | |||
674 | Returns an access token (just like log_in), which it can also persist to to_file, | 673 | Returns an access token (just like log_in), which it can also persist to to_file, |
675 | and sets it internally so that the user is now logged in. Note that this token | 674 | and sets it internally so that the user is now logged in. Note that this token |
676 | can only be used after the user has confirmed their email. | 675 | can only be used after the user has confirmed their email. |
676 | |||
677 | By default, the function will throw if the account could not be created. Alternately, | ||
678 | when `return_detailed_error` is passed, Mastodon.py will return the detailed error | ||
679 | response that the API provides (Starting from version 3.4.0 - not checked here) as an dict with | ||
680 | error details as the second return value and the token returned as `None` in case of error. | ||
681 | The dict will contain a text `error` values as well as a `details` value which is a dict with | ||
682 | one optional key for each potential field (`username`, `password`, `email` and `agreement`), | ||
683 | each if present containing a dict with an `error` category and free text `description`. | ||
684 | Valid error categories are: | ||
685 | |||
686 | * ERR_BLOCKED - When e-mail provider is not allowed | ||
687 | * ERR_UNREACHABLE - When e-mail address does not resolve to any IP via DNS (MX, A, AAAA) | ||
688 | * ERR_TAKEN - When username or e-mail are already taken | ||
689 | * ERR_RESERVED - When a username is reserved, e.g. "webmaster" or "admin" | ||
690 | * ERR_ACCEPTED - When agreement has not been accepted | ||
691 | * ERR_BLANK - When a required attribute is blank | ||
692 | * ERR_INVALID - When an attribute is malformed, e.g. wrong characters or invalid e-mail address | ||
693 | * ERR_TOO_LONG - When an attribute is over the character limit | ||
694 | * ERR_TOO_SHORT - When an attribute is under the character requirement | ||
695 | * ERR_INCLUSION - When an attribute is not one of the allowed values, e.g. unsupported locale | ||
677 | """ | 696 | """ |
678 | params = self.__generate_params(locals(), ['to_file', 'scopes']) | 697 | params = self.__generate_params(locals(), ['to_file', 'scopes']) |
679 | params['client_id'] = self.client_id | 698 | params['client_id'] = self.client_id |
@@ -690,8 +709,7 @@ class Mastodon: | |||
690 | oauth_params['client_secret'] = self.client_secret | 709 | oauth_params['client_secret'] = self.client_secret |
691 | oauth_params['grant_type'] = 'client_credentials' | 710 | oauth_params['grant_type'] = 'client_credentials' |
692 | 711 | ||
693 | response = self.__api_request( | 712 | response = self.__api_request('POST', '/oauth/token', oauth_params, do_ratelimiting=False) |
694 | 'POST', '/oauth/token', oauth_params, do_ratelimiting=False) | ||
695 | temp_access_token = response['access_token'] | 713 | temp_access_token = response['access_token'] |
696 | except Exception as e: | 714 | except Exception as e: |
697 | raise MastodonIllegalArgumentError( | 715 | raise MastodonIllegalArgumentError( |
@@ -699,13 +717,16 @@ class Mastodon: | |||
699 | 717 | ||
700 | # Step 2: Use that to create a user | 718 | # Step 2: Use that to create a user |
701 | try: | 719 | try: |
702 | response = self.__api_request('POST', '/api/v1/accounts', params, do_ratelimiting=False, | 720 | response = self.__api_request('POST', '/api/v1/accounts', params, do_ratelimiting=False, access_token_override=temp_access_token, skip_error_check=True) |
703 | access_token_override=temp_access_token) | 721 | if "error" in response: |
722 | if return_detailed_error: | ||
723 | return None, response | ||
724 | raise MastodonIllegalArgumentError('Invalid request: %s' % e) | ||
704 | self.access_token = response['access_token'] | 725 | self.access_token = response['access_token'] |
705 | self.__set_refresh_token(response.get('refresh_token')) | 726 | self.__set_refresh_token(response.get('refresh_token')) |
706 | self.__set_token_expired(int(response.get('expires_in', 0))) | 727 | self.__set_token_expired(int(response.get('expires_in', 0))) |
707 | except Exception as e: | 728 | except Exception as e: |
708 | raise MastodonIllegalArgumentError('Invalid request: %s' % e) | 729 | raise MastodonIllegalArgumentError('Invalid request') |
709 | 730 | ||
710 | # Step 3: Check scopes, persist, et cetera | 731 | # Step 3: Check scopes, persist, et cetera |
711 | received_scopes = response["scope"].split(" ") | 732 | received_scopes = response["scope"].split(" ") |
@@ -714,8 +735,7 @@ class Mastodon: | |||
714 | received_scopes += self.__SCOPE_SETS[scope_set] | 735 | received_scopes += self.__SCOPE_SETS[scope_set] |
715 | 736 | ||
716 | if not set(scopes) <= set(received_scopes): | 737 | if not set(scopes) <= set(received_scopes): |
717 | raise MastodonAPIError( | 738 | raise MastodonAPIError('Granted scopes "' + " ".join(received_scopes) + '" do not contain all of the requested scopes "' + " ".join(scopes) + '".') |
718 | 'Granted scopes "' + " ".join(received_scopes) + '" do not contain all of the requested scopes "' + " ".join(scopes) + '".') | ||
719 | 739 | ||
720 | if to_file is not None: | 740 | if to_file is not None: |
721 | with open(to_file, 'w') as token_file: | 741 | with open(to_file, 'w') as token_file: |
@@ -724,7 +744,10 @@ class Mastodon: | |||
724 | 744 | ||
725 | self.__logged_in_id = None | 745 | self.__logged_in_id = None |
726 | 746 | ||
727 | return response['access_token'] | 747 | if return_detailed_error: |
748 | return response['access_token'], {} | ||
749 | else: | ||
750 | return response['access_token'] | ||
728 | 751 | ||
729 | @api_version("3.4.0", "3.4.0", "3.4.0") | 752 | @api_version("3.4.0", "3.4.0", "3.4.0") |
730 | def email_resend_confirmation(self): | 753 | def email_resend_confirmation(self): |
@@ -2775,7 +2798,7 @@ class Mastodon: | |||
2775 | """ | 2798 | """ |
2776 | if not policy in ['all', 'none', 'follower', 'followed']: | 2799 | if not policy in ['all', 'none', 'follower', 'followed']: |
2777 | raise MastodonIllegalArgumentError("Valid values for policy are 'all', 'none', 'follower' or 'followed'.") | 2800 | raise MastodonIllegalArgumentError("Valid values for policy are 'all', 'none', 'follower' or 'followed'.") |
2778 | 2801 | ||
2779 | endpoint = Mastodon.__protocolize(endpoint) | 2802 | endpoint = Mastodon.__protocolize(endpoint) |
2780 | 2803 | ||
2781 | push_pubkey_b64 = base64.b64encode(encrypt_params['pubkey']) | 2804 | push_pubkey_b64 = base64.b64encode(encrypt_params['pubkey']) |
@@ -3506,7 +3529,7 @@ class Mastodon: | |||
3506 | isotime = isotime[:-2] + ":" + isotime[-2:] | 3529 | isotime = isotime[:-2] + ":" + isotime[-2:] |
3507 | return isotime | 3530 | return isotime |
3508 | 3531 | ||
3509 | def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, base_url_override=None, do_ratelimiting=True, use_json=False, parse=True, return_response_object=False): | 3532 | def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, base_url_override=None, do_ratelimiting=True, use_json=False, parse=True, return_response_object=False, skip_error_check=False): |
3510 | """ | 3533 | """ |
3511 | Internal API request helper. | 3534 | Internal API request helper. |
3512 | """ | 3535 | """ |
@@ -3657,35 +3680,32 @@ class Mastodon: | |||
3657 | time.sleep(to_next) | 3680 | time.sleep(to_next) |
3658 | request_complete = False | 3681 | request_complete = False |
3659 | continue | 3682 | continue |
3683 | |||
3684 | if skip_error_check == False: | ||
3685 | if response_object.status_code == 404: | ||
3686 | ex_type = MastodonNotFoundError | ||
3687 | if not error_msg: | ||
3688 | error_msg = 'Endpoint not found.' | ||
3689 | # this is for compatibility with older versions | ||
3690 | # which raised MastodonAPIError('Endpoint not found.') | ||
3691 | # on any 404 | ||
3692 | elif response_object.status_code == 401: | ||
3693 | ex_type = MastodonUnauthorizedError | ||
3694 | elif response_object.status_code == 500: | ||
3695 | ex_type = MastodonInternalServerError | ||
3696 | elif response_object.status_code == 502: | ||
3697 | ex_type = MastodonBadGatewayError | ||
3698 | elif response_object.status_code == 503: | ||
3699 | ex_type = MastodonServiceUnavailableError | ||
3700 | elif response_object.status_code == 504: | ||
3701 | ex_type = MastodonGatewayTimeoutError | ||
3702 | elif response_object.status_code >= 500 and \ | ||
3703 | response_object.status_code <= 511: | ||
3704 | ex_type = MastodonServerError | ||
3705 | else: | ||
3706 | ex_type = MastodonAPIError | ||
3660 | 3707 | ||
3661 | if response_object.status_code == 404: | 3708 | raise ex_type('Mastodon API returned error', response_object.status_code, response_object.reason, error_msg) |
3662 | ex_type = MastodonNotFoundError | ||
3663 | if not error_msg: | ||
3664 | error_msg = 'Endpoint not found.' | ||
3665 | # this is for compatibility with older versions | ||
3666 | # which raised MastodonAPIError('Endpoint not found.') | ||
3667 | # on any 404 | ||
3668 | elif response_object.status_code == 401: | ||
3669 | ex_type = MastodonUnauthorizedError | ||
3670 | elif response_object.status_code == 500: | ||
3671 | ex_type = MastodonInternalServerError | ||
3672 | elif response_object.status_code == 502: | ||
3673 | ex_type = MastodonBadGatewayError | ||
3674 | elif response_object.status_code == 503: | ||
3675 | ex_type = MastodonServiceUnavailableError | ||
3676 | elif response_object.status_code == 504: | ||
3677 | ex_type = MastodonGatewayTimeoutError | ||
3678 | elif response_object.status_code >= 500 and \ | ||
3679 | response_object.status_code <= 511: | ||
3680 | ex_type = MastodonServerError | ||
3681 | else: | ||
3682 | ex_type = MastodonAPIError | ||
3683 | |||
3684 | raise ex_type( | ||
3685 | 'Mastodon API returned error', | ||
3686 | response_object.status_code, | ||
3687 | response_object.reason, | ||
3688 | error_msg) | ||
3689 | 3709 | ||
3690 | if return_response_object: | 3710 | if return_response_object: |
3691 | return response_object | 3711 | return response_object |
diff --git a/tests/cassettes/test_app_account_create_invalid.yaml b/tests/cassettes/test_app_account_create_invalid.yaml new file mode 100644 index 0000000..bbf4843 --- /dev/null +++ b/tests/cassettes/test_app_account_create_invalid.yaml | |||
@@ -0,0 +1,245 @@ | |||
1 | interactions: | ||
2 | - request: | ||
3 | body: client_name=mastodon.py+generated+test+app&scopes=read+write+follow+push&redirect_uris=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob | ||
4 | headers: | ||
5 | Accept: | ||
6 | - '*/*' | ||
7 | Accept-Encoding: | ||
8 | - gzip, deflate | ||
9 | Connection: | ||
10 | - keep-alive | ||
11 | Content-Length: | ||
12 | - '122' | ||
13 | Content-Type: | ||
14 | - application/x-www-form-urlencoded | ||
15 | User-Agent: | ||
16 | - python-requests/2.28.1 | ||
17 | method: POST | ||
18 | uri: http://localhost:3000/api/v1/apps | ||
19 | response: | ||
20 | body: | ||
21 | string: '{"id":"4","name":"mastodon.py generated test app","website":null,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":"OueO_7-PGuI_wJwqr3gJCO3Mfp-CnB7ntLm2BDNgwoE","client_secret":"i74hCQQyscpk-AoXt8gL1lBYLlgsQw7r-vP_cuKWquA","vapid_key":"BFu6DBpfcm8_h8gm3rHUkfaOLg7azvYN_auFI4KcNuh5SLBVMhTkKKvUaLENtA_c6v5Hmrucvh0WwsN1o9NFQRU="}' | ||
22 | headers: | ||
23 | Cache-Control: | ||
24 | - no-store | ||
25 | Content-Security-Policy: | ||
26 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
27 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
28 | style-src ''self'' http://localhost:3000 ''nonce-eWGkzakxkoJqETI+J7AOfA==''; | ||
29 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
30 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
31 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
32 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
33 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
34 | worker-src ''self'' blob: http://localhost:3000' | ||
35 | Content-Type: | ||
36 | - application/json; charset=utf-8 | ||
37 | ETag: | ||
38 | - W/"e57c57d0ecbadb4122581beb6057bf7b" | ||
39 | Referrer-Policy: | ||
40 | - strict-origin-when-cross-origin | ||
41 | Transfer-Encoding: | ||
42 | - chunked | ||
43 | Vary: | ||
44 | - Accept, Origin | ||
45 | X-Content-Type-Options: | ||
46 | - nosniff | ||
47 | X-Download-Options: | ||
48 | - noopen | ||
49 | X-Frame-Options: | ||
50 | - SAMEORIGIN | ||
51 | X-Permitted-Cross-Domain-Policies: | ||
52 | - none | ||
53 | X-Request-Id: | ||
54 | - c5d33842-2156-4850-ac91-154f71f72d2e | ||
55 | X-Runtime: | ||
56 | - '0.017495' | ||
57 | X-XSS-Protection: | ||
58 | - 1; mode=block | ||
59 | status: | ||
60 | code: 200 | ||
61 | message: OK | ||
62 | - request: | ||
63 | body: null | ||
64 | headers: | ||
65 | Accept: | ||
66 | - '*/*' | ||
67 | Accept-Encoding: | ||
68 | - gzip, deflate | ||
69 | Connection: | ||
70 | - keep-alive | ||
71 | User-Agent: | ||
72 | - mastodonpy | ||
73 | method: GET | ||
74 | uri: http://localhost:3000/api/v1/instance/ | ||
75 | response: | ||
76 | body: | ||
77 | string: '{"uri":"localhost:3000","title":"Mastodon","short_description":"","description":"","email":"","version":"4.0.0rc2","urls":{"streaming_api":"ws://localhost:4000"},"stats":{"user_count":4,"status_count":5,"domain_count":0},"thumbnail":"http://localhost:3000/packs/media/images/preview-6399aebd96ccf025654e2977454f168f.png","languages":["en"],"registrations":true,"approval_required":false,"invites_enabled":true,"configuration":{"accounts":{"max_featured_tags":10},"statuses":{"max_characters":500,"max_media_attachments":4,"characters_reserved_per_url":23},"media_attachments":{"supported_mime_types":["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":2304000},"polls":{"max_options":4,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746}},"contact_account":null,"rules":[]}' | ||
78 | headers: | ||
79 | Cache-Control: | ||
80 | - max-age=180, public | ||
81 | Content-Security-Policy: | ||
82 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
83 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
84 | style-src ''self'' http://localhost:3000 ''nonce-pTVYgmBqNUhkaog/3BIWrg==''; | ||
85 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
86 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
87 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
88 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
89 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
90 | worker-src ''self'' blob: http://localhost:3000' | ||
91 | Content-Type: | ||
92 | - application/json; charset=utf-8 | ||
93 | Date: | ||
94 | - Sat, 19 Nov 2022 00:05:11 GMT | ||
95 | ETag: | ||
96 | - W/"58e74f5c697f043d86089eae87509b84" | ||
97 | Referrer-Policy: | ||
98 | - strict-origin-when-cross-origin | ||
99 | Transfer-Encoding: | ||
100 | - chunked | ||
101 | Vary: | ||
102 | - Accept, Origin | ||
103 | X-Content-Type-Options: | ||
104 | - nosniff | ||
105 | X-Download-Options: | ||
106 | - noopen | ||
107 | X-Frame-Options: | ||
108 | - SAMEORIGIN | ||
109 | X-Permitted-Cross-Domain-Policies: | ||
110 | - none | ||
111 | X-Request-Id: | ||
112 | - 3f3580e9-a01c-4cb3-a8ff-e933a5b0a878 | ||
113 | X-Runtime: | ||
114 | - '0.038202' | ||
115 | X-XSS-Protection: | ||
116 | - 1; mode=block | ||
117 | status: | ||
118 | code: 200 | ||
119 | message: OK | ||
120 | - request: | ||
121 | body: scope=read+write+follow+push&client_id=OueO_7-PGuI_wJwqr3gJCO3Mfp-CnB7ntLm2BDNgwoE&client_secret=i74hCQQyscpk-AoXt8gL1lBYLlgsQw7r-vP_cuKWquA&grant_type=client_credentials | ||
122 | headers: | ||
123 | Accept: | ||
124 | - '*/*' | ||
125 | Accept-Encoding: | ||
126 | - gzip, deflate | ||
127 | Connection: | ||
128 | - keep-alive | ||
129 | Content-Length: | ||
130 | - '170' | ||
131 | Content-Type: | ||
132 | - application/x-www-form-urlencoded | ||
133 | User-Agent: | ||
134 | - mastodonpy | ||
135 | method: POST | ||
136 | uri: http://localhost:3000/oauth/token | ||
137 | response: | ||
138 | body: | ||
139 | string: '{"access_token":"dDfMnIWbtYShiuOouwPaH1j8ZzGRdcg39Ns0nV5jXpo","token_type":"Bearer","scope":"read | ||
140 | write follow push","created_at":1668816311}' | ||
141 | headers: | ||
142 | Cache-Control: | ||
143 | - no-store | ||
144 | Content-Security-Policy: | ||
145 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
146 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
147 | style-src ''self'' http://localhost:3000 ''nonce-2oP2TTFLzbSaiZa7hgho0w==''; | ||
148 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
149 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
150 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
151 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
152 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
153 | worker-src ''self'' blob: http://localhost:3000' | ||
154 | Content-Type: | ||
155 | - application/json; charset=utf-8 | ||
156 | ETag: | ||
157 | - W/"d29d21f6cdf89562d22826c8794a7259" | ||
158 | Pragma: | ||
159 | - no-cache | ||
160 | Referrer-Policy: | ||
161 | - strict-origin-when-cross-origin | ||
162 | Transfer-Encoding: | ||
163 | - chunked | ||
164 | Vary: | ||
165 | - Accept, Origin | ||
166 | X-Content-Type-Options: | ||
167 | - nosniff | ||
168 | X-Download-Options: | ||
169 | - noopen | ||
170 | X-Frame-Options: | ||
171 | - SAMEORIGIN | ||
172 | X-Permitted-Cross-Domain-Policies: | ||
173 | - none | ||
174 | X-Request-Id: | ||
175 | - 10f9efb8-85eb-4b30-ad16-9656fe53fd44 | ||
176 | X-Runtime: | ||
177 | - '0.022478' | ||
178 | X-XSS-Protection: | ||
179 | - 1; mode=block | ||
180 | status: | ||
181 | code: 200 | ||
182 | message: OK | ||
183 | - request: | ||
184 | body: username=coolguy73878&password=&email=email%40localhost73878&locale=en&client_id=OueO_7-PGuI_wJwqr3gJCO3Mfp-CnB7ntLm2BDNgwoE&client_secret=i74hCQQyscpk-AoXt8gL1lBYLlgsQw7r-vP_cuKWquA | ||
185 | headers: | ||
186 | Accept: | ||
187 | - '*/*' | ||
188 | Accept-Encoding: | ||
189 | - gzip, deflate | ||
190 | Authorization: | ||
191 | - Bearer dDfMnIWbtYShiuOouwPaH1j8ZzGRdcg39Ns0nV5jXpo | ||
192 | Connection: | ||
193 | - keep-alive | ||
194 | Content-Length: | ||
195 | - '182' | ||
196 | Content-Type: | ||
197 | - application/x-www-form-urlencoded | ||
198 | User-Agent: | ||
199 | - mastodonpy | ||
200 | method: POST | ||
201 | uri: http://localhost:3000/api/v1/accounts | ||
202 | response: | ||
203 | body: | ||
204 | string: '{"error":"Validation failed: Password can''t be blank, Service agreement | ||
205 | must be accepted","details":{"password":[{"error":"ERR_BLANK","description":"can''t | ||
206 | be blank"}],"agreement":[{"error":"ERR_ACCEPTED","description":"must be accepted"}]}}' | ||
207 | headers: | ||
208 | Cache-Control: | ||
209 | - no-store | ||
210 | Content-Security-Policy: | ||
211 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
212 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
213 | style-src ''self'' http://localhost:3000 ''nonce-OtVYUk9hlx5v/TcbXUduUA==''; | ||
214 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
215 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
216 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
217 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
218 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
219 | worker-src ''self'' blob: http://localhost:3000' | ||
220 | Content-Type: | ||
221 | - application/json; charset=utf-8 | ||
222 | Referrer-Policy: | ||
223 | - strict-origin-when-cross-origin | ||
224 | Transfer-Encoding: | ||
225 | - chunked | ||
226 | Vary: | ||
227 | - Accept, Origin | ||
228 | X-Content-Type-Options: | ||
229 | - nosniff | ||
230 | X-Download-Options: | ||
231 | - noopen | ||
232 | X-Frame-Options: | ||
233 | - SAMEORIGIN | ||
234 | X-Permitted-Cross-Domain-Policies: | ||
235 | - none | ||
236 | X-Request-Id: | ||
237 | - 48f3d7ec-0b4f-48ce-bf0c-acbec7479c71 | ||
238 | X-Runtime: | ||
239 | - '0.561200' | ||
240 | X-XSS-Protection: | ||
241 | - 1; mode=block | ||
242 | status: | ||
243 | code: 422 | ||
244 | message: Unprocessable Entity | ||
245 | version: 1 | ||
diff --git a/tests/cassettes/test_push_set.yaml b/tests/cassettes/test_push_set.yaml deleted file mode 100644 index 4995277..0000000 --- a/tests/cassettes/test_push_set.yaml +++ /dev/null | |||
@@ -1,122 +0,0 @@ | |||
1 | interactions: | ||
2 | - request: | ||
3 | body: subscription%5Bendpoint%5D=https%3A%2F%2Fexample.com&subscription%5Bkeys%5D%5Bp256dh%5D=BKFO5w6Uf%2B%2F2wo89ovbphk5Zrb0mcAKjZrIyrX66f2IAijRtuXx4yK6J9hR%2FemKnF2DyQcyx7%2F4IGhKHBk0OTEk%3D&subscription%5Bkeys%5D%5Bauth%5D=6T8gVmd01DhQUbejRj%2Bxmg%3D%3D&policy=none&data%5Balerts%5D%5Bfollow%5D=1&data%5Balerts%5D%5Bfavourite%5D=1&data%5Balerts%5D%5Breblog%5D=1&data%5Balerts%5D%5Bmention%5D=1&data%5Balerts%5D%5Bpoll%5D=1&data%5Balerts%5D%5Bfollow_request%5D=1&data%5Balerts%5D%5Bstatus%5D=1 | ||
4 | headers: | ||
5 | Accept: | ||
6 | - '*/*' | ||
7 | Accept-Encoding: | ||
8 | - gzip, deflate | ||
9 | Authorization: | ||
10 | - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN | ||
11 | Connection: | ||
12 | - keep-alive | ||
13 | Content-Length: | ||
14 | - '489' | ||
15 | Content-Type: | ||
16 | - application/x-www-form-urlencoded | ||
17 | User-Agent: | ||
18 | - tests/v311 | ||
19 | method: POST | ||
20 | uri: http://localhost:3000/api/v1/push/subscription | ||
21 | response: | ||
22 | body: | ||
23 | string: '{"id":5,"endpoint":"https://example.com","alerts":{"mention":true,"status":true,"reblog":true,"follow":true,"follow_request":true,"favourite":true,"poll":true},"server_key":"BFu6DBpfcm8_h8gm3rHUkfaOLg7azvYN_auFI4KcNuh5SLBVMhTkKKvUaLENtA_c6v5Hmrucvh0WwsN1o9NFQRU="}' | ||
24 | headers: | ||
25 | Cache-Control: | ||
26 | - no-store | ||
27 | Content-Security-Policy: | ||
28 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
29 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
30 | style-src ''self'' http://localhost:3000 ''nonce-bz+9uelNbajqElylgkM2Gg==''; | ||
31 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
32 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
33 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
34 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
35 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
36 | worker-src ''self'' blob: http://localhost:3000' | ||
37 | Content-Type: | ||
38 | - application/json; charset=utf-8 | ||
39 | ETag: | ||
40 | - W/"083c3b807a4d145ec452ffd03c768d76" | ||
41 | Referrer-Policy: | ||
42 | - strict-origin-when-cross-origin | ||
43 | Transfer-Encoding: | ||
44 | - chunked | ||
45 | Vary: | ||
46 | - Accept, Origin | ||
47 | X-Content-Type-Options: | ||
48 | - nosniff | ||
49 | X-Download-Options: | ||
50 | - noopen | ||
51 | X-Frame-Options: | ||
52 | - SAMEORIGIN | ||
53 | X-Permitted-Cross-Domain-Policies: | ||
54 | - none | ||
55 | X-Request-Id: | ||
56 | - abb4aec7-a93a-4a99-ba4c-5a290bbbf782 | ||
57 | X-Runtime: | ||
58 | - '0.027761' | ||
59 | X-XSS-Protection: | ||
60 | - 1; mode=block | ||
61 | status: | ||
62 | code: 200 | ||
63 | message: OK | ||
64 | - request: | ||
65 | body: null | ||
66 | headers: | ||
67 | Accept: | ||
68 | - '*/*' | ||
69 | Accept-Encoding: | ||
70 | - gzip, deflate | ||
71 | Authorization: | ||
72 | - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN | ||
73 | Connection: | ||
74 | - keep-alive | ||
75 | User-Agent: | ||
76 | - tests/v311 | ||
77 | method: GET | ||
78 | uri: http://localhost:3000/api/v1/push/subscription | ||
79 | response: | ||
80 | body: | ||
81 | string: '{"id":5,"endpoint":"https://example.com","alerts":{"mention":true,"status":true,"reblog":true,"follow":true,"follow_request":true,"favourite":true,"poll":true},"server_key":"BFu6DBpfcm8_h8gm3rHUkfaOLg7azvYN_auFI4KcNuh5SLBVMhTkKKvUaLENtA_c6v5Hmrucvh0WwsN1o9NFQRU="}' | ||
82 | headers: | ||
83 | Cache-Control: | ||
84 | - no-store | ||
85 | Content-Security-Policy: | ||
86 | - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src | ||
87 | ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000; | ||
88 | style-src ''self'' http://localhost:3000 ''nonce-4kBQ0XWCASsxXi+/Z0W5jA==''; | ||
89 | media-src ''self'' https: data: http://localhost:3000; frame-src ''self'' | ||
90 | https:; manifest-src ''self'' http://localhost:3000; connect-src ''self'' | ||
91 | data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000 | ||
92 | ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline'' | ||
93 | ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000; | ||
94 | worker-src ''self'' blob: http://localhost:3000' | ||
95 | Content-Type: | ||
96 | - application/json; charset=utf-8 | ||
97 | ETag: | ||
98 | - W/"083c3b807a4d145ec452ffd03c768d76" | ||
99 | Referrer-Policy: | ||
100 | - strict-origin-when-cross-origin | ||
101 | Transfer-Encoding: | ||
102 | - chunked | ||
103 | Vary: | ||
104 | - Accept, Origin | ||
105 | X-Content-Type-Options: | ||
106 | - nosniff | ||
107 | X-Download-Options: | ||
108 | - noopen | ||
109 | X-Frame-Options: | ||
110 | - SAMEORIGIN | ||
111 | X-Permitted-Cross-Domain-Policies: | ||
112 | - none | ||
113 | X-Request-Id: | ||
114 | - eeb007da-a24b-4e31-b962-30ef0ba3bc16 | ||
115 | X-Runtime: | ||
116 | - '0.008203' | ||
117 | X-XSS-Protection: | ||
118 | - 1; mode=block | ||
119 | status: | ||
120 | code: 200 | ||
121 | message: OK | ||
122 | version: 1 | ||
diff --git a/tests/test_create_app.py b/tests/test_create_app.py index 8a7ea62..c7282df 100644 --- a/tests/test_create_app.py +++ b/tests/test_create_app.py | |||
@@ -70,5 +70,24 @@ def test_app_account_create(): | |||
70 | # We can also test resending (marginally) | 70 | # We can also test resending (marginally) |
71 | test_app_api.email_resend_confirmation() | 71 | test_app_api.email_resend_confirmation() |
72 | 72 | ||
73 | 73 | @pytest.mark.vcr(match_on=['path']) | |
74 | 74 | def test_app_account_create_invalid(): | |
75 | suffix = str(time.time()).replace(".", "")[-5:] | ||
76 | |||
77 | test_app = test_app = Mastodon.create_app( | ||
78 | "mastodon.py generated test app", | ||
79 | api_base_url="http://localhost:3000/" | ||
80 | ) | ||
81 | |||
82 | test_app_api = Mastodon( | ||
83 | test_app[0], | ||
84 | test_app[1], | ||
85 | api_base_url="http://localhost:3000/" | ||
86 | ) | ||
87 | test_token, error = test_app_api.create_account("coolguy" + suffix, "", "email@localhost" + suffix, agreement=False, return_detailed_error=True) | ||
88 | assert test_token is None | ||
89 | assert "details" in error | ||
90 | assert "password" in error.details | ||
91 | assert "password" in error.details | ||
92 | assert not "username" in error.details | ||
93 | \ No newline at end of file | ||