diff --git a/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/conftest.py b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/conftest.py new file mode 100644 index 000000000000..cbdbcd13702b --- /dev/null +++ b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/conftest.py @@ -0,0 +1,102 @@ +# +# Copyright (c) 2022 Airbyte, Inc., all rights reserved. +# + +import json +import pathlib + +import pytest +from source_paypal_transaction.source import PayPalOauth2Authenticator, SourcePaypalTransaction + + +@pytest.fixture() +def api_endpoint(): + return "https://api-m.paypal.com" + + +@pytest.fixture() +def sandbox_api_endpoint(): + return "https://api-m.sandbox.paypal.com" + + +@pytest.fixture(autouse=True) +def time_sleep_mock(mocker): + time_mock = mocker.patch("time.sleep", lambda x: None) + yield time_mock + + +@pytest.fixture(autouse=True) +def transactions(request): + file = pathlib.Path(request.node.fspath.strpath) + transaction = file.with_name("transaction.json") + with transaction.open() as fp: + return json.load(fp) + + +@pytest.fixture() +def prod_config(): + """ + Credentials for oauth2.0 authorization + """ + return { + "client_id": "some_client_id", + "secret": "some_secret", + "start_date": "2021-07-01T00:00:00+00:00", + "end_date": "2021-07-10T00:00:00+00:00", + "is_sandbox": False + } + + +@pytest.fixture() +def sandbox_config(): + """ + Credentials for oauth2.0 authorization + """ + return { + "client_id": "some_client_id", + "secret": "some_secret", + "start_date": "2021-07-01T00:00:00+00:00", + "end_date": "2021-07-10T00:00:00+00:00", + "is_sandbox": True + } + + +@pytest.fixture() +def new_prod_config(): + """ + Credentials for oauth2.0 authorization + """ + return { + "credentials": { + "auth_type": "oauth2.0", + "client_id": "some_client_id", + "client_secret": "some_client_secret", + "refresh_token": "some_refresh_token" + }, + "start_date": "2021-07-01T00:00:00+00:00", + "end_date": "2021-07-10T00:00:00+00:00", + "is_sandbox": False + } + + +@pytest.fixture() +def error_while_refreshing_access_token(): + """ + Error raised when using incorrect access token + """ + return "Error while refreshing access token: 'access_token'" + + +@pytest.fixture() +def authenticator_instance(prod_config): + return PayPalOauth2Authenticator(prod_config) + + +@pytest.fixture() +def new_format_authenticator_instance(new_prod_config): + return PayPalOauth2Authenticator(new_prod_config) + + +@pytest.fixture() +def source_instance(): + return SourcePaypalTransaction() diff --git a/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/test_source.py b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/test_source.py new file mode 100644 index 000000000000..6d84ed2c59e2 --- /dev/null +++ b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/test_source.py @@ -0,0 +1,55 @@ +# +# Copyright (c) 2022 Airbyte, Inc., all rights reserved. +# + +from unittest.mock import MagicMock + +from source_paypal_transaction.source import PayPalOauth2Authenticator, get_endpoint + + +class TestAuthentication: + + def test_init_token_authentication_init(self, authenticator_instance): + assert isinstance(authenticator_instance, PayPalOauth2Authenticator) + + def test_get_refresh_request_body(self, authenticator_instance): + expected_body = {"grant_type": "client_credentials"} + assert authenticator_instance.get_refresh_request_body() == expected_body + + def test_oauth2_refresh_token_ok(self, requests_mock, authenticator_instance, api_endpoint): + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={"access_token": "test_access_token", "expires_in": 12345}) + result = authenticator_instance.refresh_access_token() + assert result == ("test_access_token", 12345) + + def test_oauth2_refresh_token_failed(self, requests_mock, authenticator_instance, api_endpoint, error_while_refreshing_access_token): + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={}) + try: + authenticator_instance.refresh_access_token() + except Exception as e: + assert e.args[0] == error_while_refreshing_access_token + + def test_new_oauth2_refresh_token_ok(self, requests_mock, new_format_authenticator_instance, api_endpoint): + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={"access_token": "test_access_token", "expires_in": 12345}) + result = new_format_authenticator_instance.refresh_access_token() + assert result == ("test_access_token", 12345) + + def test_streams_count(self, prod_config, source_instance): + assert len(source_instance.streams(prod_config)) == 2 + + def test_check_connection_ok(self, requests_mock, prod_config, api_endpoint, transactions, source_instance): + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={"access_token": "test_access_token", "expires_in": 12345}) + url = f'{api_endpoint}/v1/reporting/transactions' + '?start_date=2021-07-01T00%3A00%3A00%2B00%3A00&end_date=2021-07-02T00%3A00%3A00%2B00%3A00&fields=all&page_size=500&page=1' + requests_mock.get(url, json=transactions) + assert source_instance.check_connection(logger=MagicMock(), config=prod_config) == (True, None) + + def test_check_connection_error(self, requests_mock, prod_config, api_endpoint, source_instance): + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={"access_token": "test_access_token", "expires_in": 12345}) + url = f'{api_endpoint}/v1/reporting/transactions' + '?start_date=2021-07-01T00%3A00%3A00%2B00%3A00&end_date=2021-07-02T00%3A00%3A00%2B00%3A00&fields=all&page_size=500&page=1' + requests_mock.get(url, status_code=400, json={}) + assert not source_instance.check_connection(logger=MagicMock(), config=prod_config)[0] + + def test_get_prod_endpoint(self, prod_config, api_endpoint): + assert get_endpoint(prod_config["is_sandbox"]) == api_endpoint + + def test_get_sandbox_endpoint(self, sandbox_config, sandbox_api_endpoint): + assert get_endpoint(sandbox_config["is_sandbox"]) == sandbox_api_endpoint diff --git a/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/unit_test.py b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/unit_test.py index 9c0f127991de..d6d7bc268335 100644 --- a/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/unit_test.py +++ b/airbyte-integrations/connectors/source-paypal-transaction/unit_tests/unit_test.py @@ -2,28 +2,31 @@ # Copyright (c) 2022 Airbyte, Inc., all rights reserved. # -import json -import pathlib from datetime import datetime, timedelta +from airbyte_cdk.models import SyncMode from airbyte_cdk.sources.streams.http.auth import NoAuth from dateutil.parser import isoparse -from pytest import fixture, raises +from pytest import raises from source_paypal_transaction.source import Balances, PaypalTransactionStream, Transactions -@fixture(autouse=True) -def time_sleep_mock(mocker): - time_mock = mocker.patch("time.sleep", lambda x: None) - yield time_mock +def test_minimum_allowed_start_date(): + start_date = now() - timedelta(days=10 * 365) + stream = Transactions(authenticator=NoAuth(), start_date=start_date) + assert stream.start_date != start_date -@fixture(autouse=True) -def transactions(request): - file = pathlib.Path(request.node.fspath.strpath) - transaction = file.with_name("transaction.json") - with transaction.open() as fp: - return json.load(fp) +def test_transactions_transform_function(): + start_date = now() - timedelta(days=10 * 365) + stream = Transactions(authenticator=NoAuth(), start_date=start_date) + transformer = stream.transformer + input_data = {"transaction_amount": "123.45", "transaction_id": "111", "transaction_status": "done"} + schema = stream.get_json_schema() + schema['properties'] = {"transaction_amount": {"type": "number"}, "transaction_id": {"type": "integer"}, "transaction_status": {"type": "string"}} + transformer.transform(input_data, schema) + expected_data = {"transaction_amount": 123.45, "transaction_id": 111, "transaction_status": "done"} + assert input_data == expected_data def test_get_field(): @@ -319,3 +322,27 @@ def test_unnest_field(): PaypalTransactionStream.unnest_field(record, Transactions.nested_object, Transactions.cursor_field) # check the cursor now on the root level assert Transactions.cursor_field in record.keys() + + +def test_get_last_refreshed_datetime(requests_mock, prod_config, api_endpoint): + stream = Balances(authenticator=NoAuth(), **prod_config) + requests_mock.post(f"{api_endpoint}/v1/oauth2/token", json={"access_token": "test_access_token", "expires_in": 12345}) + url = f'{api_endpoint}/v1/reporting/balances' + '?as_of_time=2021-07-01T00%3A00%3A00%2B00%3A00' + requests_mock.get(url, json={}) + assert not stream.get_last_refreshed_datetime(SyncMode.full_refresh) + + +def test_get_updated_state(transactions): + start_date = "2021-06-01T10:00:00+00:00" + stream = Transactions( + authenticator=NoAuth(), + start_date=isoparse(start_date), + end_date=isoparse("2021-06-04T12:00:00+00:00"), + ) + state = stream.get_updated_state(current_stream_state={}, latest_record={}) + assert state == {"date": start_date} + + record = transactions[stream.data_field][0][stream.nested_object] + expected_state = {"date": now().isoformat()} + state = stream.get_updated_state(current_stream_state=expected_state, latest_record=record) + assert state == expected_state