From 77bb46ec1d6ba10990dc6c94c727d18db3b608a1 Mon Sep 17 00:00:00 2001 From: David Nowinsky Date: Fri, 2 Oct 2020 15:26:13 +0200 Subject: [PATCH] tests(oauth2): exclude local helpers from coverage --- .coveragerc | 3 +- oauth_connector_quickstart.py | 28 +++++++++++---- sonar-project.properties | 2 +- tests/google_sheets_2/test_google_sheets_2.py | 23 +++++++++++- .../oauth2_authorization_webserver.py | 26 ++++++++++++++ .../oauth2_connector/oauth2connector.py | 35 ++++++------------- 6 files changed, 82 insertions(+), 35 deletions(-) diff --git a/.coveragerc b/.coveragerc index cf88c9bb0..85247a59c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -13,4 +13,5 @@ exclude_lines = # Files to exclude from consideration omit = # Utilities for OAuth2 connectors testing - toucan_connectors/oauth2_connector/quickstart + oauth_connector_quickstart.py + toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py diff --git a/oauth_connector_quickstart.py b/oauth_connector_quickstart.py index 9a228df59..ae218a073 100644 --- a/oauth_connector_quickstart.py +++ b/oauth_connector_quickstart.py @@ -1,15 +1,29 @@ -from toucan_connectors.google_sheets_2.google_sheets_2_connector import GoogleSheets2Connector, GoogleSheets2DataSource -from toucan_connectors.oauth2_connector.oauth2_authorization_webserver import get_authorization_response -from toucan_connectors.oauth2_connector.oauth2connector import JsonFileSecretsKeeper - +from toucan_connectors.google_sheets_2.google_sheets_2_connector import ( + GoogleSheets2Connector, + GoogleSheets2DataSource, +) +from toucan_connectors.oauth2_connector.oauth2_authorization_webserver import ( + JsonFileSecretsKeeper, + get_authorization_response, +) CLIENT_ID = '' CLIENT_SECRET = '' REDIRECT_URI = 'http://localhost:34097/' -google_sheets_conn = GoogleSheets2Connector(name='test', auth_flow_id='test', client_id=CLIENT_ID, client_secret=CLIENT_SECRET, - redirect_uri=REDIRECT_URI, secrets_keeper=JsonFileSecretsKeeper(filename="secrets.json")) -sample_data_source_ss = GoogleSheets2DataSource(name='test', domain='test-connector', spreadsheet_id='1L5YraXEToFv7p0HMke7gXI4IhJotdT0q5bk_PInI1hA') +google_sheets_conn = GoogleSheets2Connector( + name='test', + auth_flow_id='test', + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + redirect_uri=REDIRECT_URI, + secrets_keeper=JsonFileSecretsKeeper(filename="secrets.json"), +) +sample_data_source_ss = GoogleSheets2DataSource( + name='test', + domain='test-connector', + spreadsheet_id='1L5YraXEToFv7p0HMke7gXI4IhJotdT0q5bk_PInI1hA', +) # authorization_response = get_authorization_response(google_sheets_conn.build_authorization_url(), 'localhost', 34097) # google_sheets_conn.retrieve_tokens(authorization_response) diff --git a/sonar-project.properties b/sonar-project.properties index 096040594..95c998ad6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -7,7 +7,7 @@ sonar.projectVersion=0.41.4 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=./toucan_connectors -sonar.coverage.exclusions=./toucan_connectors/install_scripts/**/*,./toucan_connectors/oauth2_connector/quickstart/**/* +sonar.coverage.exclusions=./toucan_connectors/install_scripts,./toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py sonar.test.inclusions=./tests # Encoding of the source code. Default is default system encoding diff --git a/tests/google_sheets_2/test_google_sheets_2.py b/tests/google_sheets_2/test_google_sheets_2.py index ab83b4666..90128a7a4 100644 --- a/tests/google_sheets_2/test_google_sheets_2.py +++ b/tests/google_sheets_2/test_google_sheets_2.py @@ -9,6 +9,7 @@ GoogleSheets2DataSource, NoCredentialsError, ) +from toucan_connectors.oauth2_connector.oauth2connector import OAuth2Connector import_path = 'toucan_connectors.google_sheets_2.google_sheets_2_connector' @@ -191,13 +192,21 @@ def mock_api_responses(uri: str): assert df.columns.tolist() == ['country', 'city'] -def test_get_status_no_secrets(mocker, con): +def test_get_status_no_secrets(con, remove_secrets): """ It should fail if no secrets are provided """ assert con.get_status().status is False +def test_get_status_secrets_error(mocker, con): + """ + It should fail if secrets can't be retrieved + """ + mocker.patch(f'{import_path}.OAuth2Connector.get_access_token', side_effect=Exception) + assert con.get_status().status is False + + def test_get_status_success(mocker, con): """ It should fail if no secrets are provided. @@ -230,3 +239,15 @@ def test_get_decimal_separator(mocker, con, ds): mocker.patch.object(GoogleSheets2Connector, '_run_fetch', return_value=fake_results) df = con.get_df(ds) assert df.to_dict() == {'Number': {1: 1.3, 2: 1.2}} + + +def test_delegate_oauth2_methods(mocker, con): + """ + It should proxy OAuth2Connectors methods + """ + mock_oauth2_connector = mocker.Mock(spec=OAuth2Connector) + con.__dict__['_oauth2_connector'] = mock_oauth2_connector + con.build_authorization_url() + mock_oauth2_connector.build_authorization_url.assert_called() + con.retrieve_tokens('toto') + mock_oauth2_connector.retrieve_tokens.assert_called_with('toto') diff --git a/toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py b/toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py index b53d54a6d..ef5c589a6 100644 --- a/toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py +++ b/toucan_connectors/oauth2_connector/oauth2_authorization_webserver.py @@ -1,6 +1,12 @@ +""" +This provide a helper to test OAuth2 connectors locally +""" +import json import webbrowser import wsgiref.simple_server import wsgiref.util +from sys import path +from typing import Any class _RedirectWSGIApp(object): @@ -44,3 +50,23 @@ def get_authorization_response(authorization_url, host, port): local_server = wsgiref.simple_server.make_server(host, port, app) local_server.handle_request() return app.last_request_uri + + +class JsonFileSecretsKeeper: + def __init__(self, filename: str): + self.filename = filename + + def load_file(self) -> dict: + if not path.exists(self.filename): + return {} + with open(self.filename, 'r') as f: + return json.load(f) + + def save(self, key: str, value): + values = self.load_file() + values[key] = value + with open(self.filename, 'w') as f: + json.dump(values, f) + + def load(self, key: str) -> Any: + return self.load_file()[key] diff --git a/toucan_connectors/oauth2_connector/oauth2connector.py b/toucan_connectors/oauth2_connector/oauth2connector.py index b133b11fa..0460f5f4e 100644 --- a/toucan_connectors/oauth2_connector/oauth2connector.py +++ b/toucan_connectors/oauth2_connector/oauth2connector.py @@ -1,5 +1,4 @@ -import json -from os import path +from abc import ABC, abstractmethod from time import time from typing import Any from urllib import parse as url_parse @@ -7,32 +6,18 @@ from authlib.integrations.requests_client import OAuth2Session -class SecretsKeeper: +class SecretsKeeper(ABC): + @abstractmethod def save(self, key: str, value): - pass - - def load(self, key: str) -> Any: - pass - - -class JsonFileSecretsKeeper: - def __init__(self, filename: str): - self.filename = filename - - def load_file(self) -> dict: - if not path.exists(self.filename): - return {} - with open(self.filename, 'r') as f: - return json.load(f) - - def save(self, key: str, value): - values = self.load_file() - values[key] = value - with open(self.filename, 'w') as f: - json.dump(values, f) + """ + Save secrets in a secrets repository + """ + @abstractmethod def load(self, key: str) -> Any: - return self.load_file()[key] + """ + Load secrets from the secrets repository + """ class OAuth2Connector: