From 6d094b5ed9bf3d432bdd57f7b01242306ed2b0be Mon Sep 17 00:00:00 2001 From: YariKartoshe4ka <49284924+YariKartoshe4ka@users.noreply.github.com> Date: Sun, 5 Jun 2022 18:52:05 +0300 Subject: [PATCH] Upgrading tests (#126) * Small tests refactoring, cover vk.exceptions * Tox fix * Empty ACCESS_TOKEN not a token * Cover vk.utils * Refactoring and covering vk.session.InteractiveMixin * Remove mocking requests objects and old vk.API (UserAPI) test --- .github/workflows/check.yml | 4 +- src/vk/exceptions.py | 34 ++++----- src/vk/session.py | 134 +++++++++++++++++++----------------- src/vk/utils.py | 30 +------- tests/conftest.py | 85 +++-------------------- tests/test_api.py | 27 +------- tests/test_exceptions.py | 35 ++++++++++ tests/test_user_api.py | 68 +++++------------- tests/test_utils.py | 20 ++++-- tox.ini | 2 + 10 files changed, 169 insertions(+), 270 deletions(-) create mode 100644 tests/test_exceptions.py diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c892dde..316eb3a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -5,7 +5,7 @@ on: push: schedule: - - cron: "0 12 * * *" + - cron: "0 12 */2 * *" jobs: test: @@ -38,6 +38,8 @@ jobs: run: tox -vv --notest - name: Run test suite run: tox --skip-pkg-install + env: + VK_ACCESS_TOKEN: ${{ secrets.VK_ACCESS_TOKEN }} - name: Upload coverage uses: codecov/codecov-action@v2 diff --git a/src/vk/exceptions.py b/src/vk/exceptions.py index c5763dc..1d36f69 100644 --- a/src/vk/exceptions.py +++ b/src/vk/exceptions.py @@ -1,10 +1,14 @@ +from enum import IntEnum -# API Error Codes -AUTHORIZATION_FAILED = 5 # Invalid access token -PERMISSION_IS_DENIED = 7 -CAPTCHA_IS_NEEDED = 14 -ACCESS_DENIED = 15 # No access to call this method -INVALID_USER_ID = 113 # User deactivated + +class ErrorCodes(IntEnum): + """VK API error codes""" + + AUTHORIZATION_FAILED = 5 # Invalid access token + PERMISSION_IS_DENIED = 7 + CAPTCHA_NEEDED = 14 + ACCESS_DENIED = 15 # No access to call this method + INVALID_USER_ID = 113 # User deactivated class VkException(Exception): @@ -18,9 +22,6 @@ class VkAuthError(VkException): class VkAPIError(VkException): __slots__ = ['error', 'code', 'message', 'request_params', 'redirect_uri'] - CAPTCHA_NEEDED = 14 - ACCESS_DENIED = 15 - def __init__(self, error_data): super(VkAPIError, self).__init__() self.error_data = error_data @@ -31,15 +32,16 @@ def __init__(self, error_data): @staticmethod def get_pretty_request_params(error_data): - request_params = error_data.get('request_params', ()) - request_params = {param['key']: param['value'] for param in request_params} - return request_params + return { + param['key']: param['value'] + for param in error_data.get('request_params', ()) + } def is_access_token_incorrect(self): - return self.code == self.ACCESS_DENIED and 'access_token' in self.message + return self.code in (ErrorCodes.AUTHORIZATION_FAILED, ErrorCodes.ACCESS_DENIED) def is_captcha_needed(self): - return self.code == self.CAPTCHA_NEEDED + return self.code == ErrorCodes.CAPTCHA_NEEDED @property def captcha_sid(self): @@ -50,7 +52,7 @@ def captcha_img(self): return self.error_data.get('captcha_img') def __str__(self): - error_message = '{self.code}. {self.message}. request_params = {self.request_params}'.format(self=self) + error_message = f'{self.code}. {self.message}. request_params = {self.request_params}' if self.redirect_uri: - error_message += ',\nredirect_uri = "{self.redirect_uri}"'.format(self=self) + error_message += f',\nredirect_uri = "{self.redirect_uri}"' return error_message diff --git a/src/vk/session.py b/src/vk/session.py index a8e2071..b4d971c 100644 --- a/src/vk/session.py +++ b/src/vk/session.py @@ -1,12 +1,14 @@ +import getpass import logging -import re import urllib +from json import loads +from re import findall import requests from .api import APINamespace from .exceptions import VkAPIError, VkAuthError -from .utils import json_iter_parse, stringify +from .utils import stringify logger = logging.getLogger('vk') @@ -47,27 +49,22 @@ def send(self, request): # todo Replace with something less exceptional response.raise_for_status() - # TODO: there are may be 2 dicts in one JSON - # for example: "{'error': ...}{'response': ...}" - for response_or_error in json_iter_parse(response.text): - request.response = response_or_error + response_or_error = loads(response.text) + request.response = response_or_error - if 'response' in response_or_error: - # todo Can we have error and response simultaneously - # for error in errors: - # logger.warning(str(error)) - return response_or_error['response'] + if 'response' in response_or_error: + # todo Can we have error and response simultaneously + # for error in errors: + # logger.warning(str(error)) + return response_or_error['response'] - elif 'error' in response_or_error: - api_error = VkAPIError(request.response['error']) - request.api_error = api_error - return self.handle_api_error(request) + elif 'error' in response_or_error: + api_error = VkAPIError(request.response['error']) + request.api_error = api_error + return self.handle_api_error(request) - def prepare_request(self, request): - request.method_params.setdefault('access_token', self.access_token) - - def get_access_token(self): - raise NotImplementedError + def prepare_request(self, request): # noqa: U100 + pass def handle_api_error(self, request): logger.error('Handle API error: %s', request.api_error) @@ -77,31 +74,15 @@ def handle_api_error(self, request): return api_error_handler(request) - def on_api_error_14(self, request): - """ - 14. Captcha needed - """ - request.method_params['captcha_key'] = self.get_captcha_key(request) - request.method_params['captcha_sid'] = request.api_error.captcha_sid - - return self.send(request) - - def on_api_error_15(self, request): - """ - 15. Access denied - - due to scope - """ - logger.error('Authorization failed. Access token will be dropped') - - del request.method_params['access_token'] - self.access_token = self.get_access_token() - - return self.send(request) - def on_api_error(self, request): - logger.error('API error: %s', request.api_error) raise request.api_error + +class API(APIBase): + def __init__(self, access_token, **kwargs): + super().__init__(**kwargs) + self.access_token = access_token + def get_captcha_key(self, request): """ Default behavior on CAPTCHA is to raise exception @@ -110,18 +91,24 @@ def get_captcha_key(self, request): # request.api_error.captcha_img raise request.api_error + def on_api_error_14(self, request): + """ + 14. Captcha needed + """ + request.method_params['captcha_key'] = self.get_captcha_key(request) + request.method_params['captcha_sid'] = request.api_error.captcha_sid -class API(APIBase): - def __init__(self, access_token, **kwargs): - super().__init__(**kwargs) - self.access_token = access_token + return self.send(request) + + def prepare_request(self, request): + request.method_params.setdefault('access_token', self.access_token) class UserAPI(APIBase): LOGIN_URL = 'https://m.vk.com' AUTHORIZE_URL = 'https://oauth.vk.com/authorize' - def __init__(self, user_login='', user_password='', app_id=None, scope='offline', **kwargs): + def __init__(self, user_login=None, user_password=None, app_id=None, scope='offline', **kwargs): super().__init__(**kwargs) self.user_login = user_login @@ -133,7 +120,7 @@ def __init__(self, user_login='', user_password='', app_id=None, scope='offline' @staticmethod def get_form_action(response): - form_action = re.findall(r'