def test_error_is_raised_on_incorrect_authentication_details(self):
        currencycloud.login_id = 'non-existent-login-id'
        currencycloud.api_key = 'efb5ae2af84978b7a37f18dd61c8bbe139b403009faea83484405a3dcb64c4d8'  # noqa

        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette(
                'error/is_raised_on_incorrect_authentication_details')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except AuthenticationError as e:
                error = e

            assert error.code == 'auth_failed'
            assert error.raw_response is not None
            assert error.status_code == 401
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'username'
            assert error_message.code == 'invalid_supplied_credentials'
            assert error_message.message == 'Authentication failed with the supplied credentials'  # noqa
            assert not error_message.params
    def test_authentication_can_be_closed(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('authentication/can_be_closed')

            assert currencycloud.session() is not None
            assert currencycloud.close_session() is True
    def test_error_is_raised_on_bad_request(self):
        currencycloud.login_id = 'non-existent-login-id'
        currencycloud.api_key = 'ef0fd50fca1fb14c1fab3a8436b9ecb57528f0'

        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('error/is_raised_on_bad_request')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except BadRequestError as e:
                error = e

            assert error.code == 'auth_invalid_user_login_details'
            assert error.raw_response is not None
            assert error.status_code == 400
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'api_key'
            assert error_message.code == 'api_key_length_is_invalid'
            assert error_message.message == 'api_key should be 64 character(s) long'  # noqa
            assert error_message.params["length"] == 64
    def test_authentication_happens_lazily(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('authentication/happens_lazily')

            TestAuthentication.session_token = currencycloud.session().token

            assert currencycloud.session() is not None
            assert currencycloud.session().token is not None
            assert currencycloud.session().token
    def test_rates_can_find(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('rates/can_find')

            rates = Rate.find(currency_pair="GBPUSD,EURGBP")

            assert rates is not None
            assert rates.currencies
            assert len(rates.unavailable) == 0

            currencies = rates.currencies

            assert len(currencies) == 2

            currency_pairs = []
            for currency in currencies:
                assert currency is not None
                assert isinstance(currency, Rate)

                currency_pairs.append(currency.currency_pair)

            assert 'EURGBP' in currency_pairs

            rate = currencies[0]

            assert rate.bid
            assert rate.offer
    def test_actions_can_create(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_create')

            beneficiary = Beneficiary.create(**TestActions.beneficiary_params)

            TestActions.beneficiary_id = beneficiary.id

            assert isinstance(beneficiary, Beneficiary)
            assert beneficiary.id
            assert beneficiary.created_at
            assert beneficiary.updated_at
            assert beneficiary.created_at == beneficiary.updated_at

            for k, v in TestActions.beneficiary_params.items():
                if k in ('routing_code_type_1', 'routing_code_value_1'):
                    # skip fields with wrong values that are cleaned up by the
                    # API
                    continue
                b = beneficiary[k]
                if is_string(v):
                    b = str(b)
                    v = str(v)
                assert b == v
    def test_reference_can_retrieve_beneficiary_required_details(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette(
                'reference/can_retrieve_beneficiary_required_details')

            details = Reference.beneficiary_required_details(
                currency='GBP',
                bank_account_country='GB',
                beneficiary_country='GB'
            )

            assert len(details) > 0

            details = details[0]

            assert isinstance(details, BeneficiaryRequiredDetails)
            assert details.beneficiary_entity_type == 'individual'
            assert details.payment_type == 'priority'
            assert details.beneficiary_address == "^.{1,255}"
            assert details.beneficiary_city == "^.{1,255}"
            assert details.beneficiary_country == "^[A-z]{2}$"
            assert details.beneficiary_first_name == "^.{1,255}"
            assert details.beneficiary_last_name == "^.{1,255}"
            assert details.acct_number == "^[0-9A-Z]{1,50}$"
            assert details.sort_code == "^\\d{6}$"
    def test_actions_can_retrieve(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_retrieve')

            beneficiary = Beneficiary.retrieve(TestActions.beneficiary_id)

            assert isinstance(beneficiary, Beneficiary)
            assert beneficiary.id == TestActions.beneficiary_id
    def test_settlement_can_release(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette("settlements/can_release")

            settlement = Settlement.retrieve(TestSettlements.settlement_id)
            released_settlement = settlement.release()

            assert released_settlement is settlement
            assert released_settlement.status == "released"
    def test_actions_can_current(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_current')

            account = Account.current()

            assert isinstance(account, Account)
            assert account.id == '8ec3a69b-02d1-4f09-9a6b-6bd54a61b3a8'
            assert account.created_at == '2015-04-24T15:57:55+00:00'
    def test_actions_can_use_currency_to_retrieve_balance(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette(
                'actions/can_use_currency_to_retrieve_balance')

            balance = Balance.currency_with_code('GBP')

            assert isinstance(balance, Balance)
            assert balance.id
    def test_actions_can_delete(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_delete')

            beneficiary = Beneficiary.delete_id(TestActions.beneficiary_id)

            assert isinstance(beneficiary, Beneficiary)
            assert beneficiary.id == TestActions.beneficiary_id
            assert beneficiary.bank_account_holder_name == "Test Name 2"
    def test_reference_can_retrieve_conversion_dates(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('reference/can_retrieve_conversion_dates')

            dates = Reference.conversion_dates(conversion_pair='GBPUSD')

            assert isinstance(dates, ConversionDates)
            assert dates.first_conversion_date
            assert dates.default_conversion_date
            assert 'No trading on Saturday' in dates.invalid_conversion_dates.values()  # noqa
    def test_settlement_can_remove_conversion(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette("settlements/can_remove_conversion")

            settlement = Settlement.retrieve(TestSettlements.settlement_id)
            deleted_settlement = settlement.remove_conversion(TestSettlements.conversion_id)

            assert deleted_settlement is not None
            assert deleted_settlement.type == "bulk"
            assert deleted_settlement.status == "open"
            assert TestSettlements.conversion_id not in deleted_settlement.conversion_ids  # noqa
    def test_actions_can_first(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_first')

            beneficiary = Beneficiary.first(
                bank_account_holder_name=TestActions.beneficiary_params['bank_account_holder_name'])  # noqa

            assert isinstance(beneficiary, Beneficiary)
            assert beneficiary.id == TestActions.beneficiary_first_id
            assert beneficiary.bank_account_holder_name == TestActions.beneficiary_params[  # noqa
                'bank_account_holder_name']
    def test_authentication_can_use_just_a_token(self):
        currencycloud.login_id = None
        currencycloud.api_key = None
        currencycloud.token = TestAuthentication.session_token

        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('authentication/can_use_just_a_token')

            response = Beneficiary.find()

            assert response is not None
    def test_error_contains_full_details_for_api_error(self):
        currencycloud.login_id = 'non-existent-login-id'
        currencycloud.api_key = 'ef0fd50fca1fb14c1fab3a8436b9ecb57528f0'

        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('error/contains_full_details_for_api_error')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except BadRequestError as e:
                error = e

        assert error is not None

        expected_error_fields = [
            "login_id: non-existent-login-id",
            "api_key: " + currencycloud.api_key,
            "verb: post",
            "url: https://devapi.thecurrencycloud.com/v2/authenticate/api",
            "status_code: 400",
            "date:",
            "request_id:",
            "field: api_key",
            "code: api_key_length_is_invalid",
            "message: api_key should be 64 character(s) long",
            "length: 64"
        ]

        error_str = str(error)
        missing = False
        for f in expected_error_fields:
            if f not in error_str:
                missing = True
                break

        assert missing is False
    def test_error_is_raised_on_forbidden_request(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('error/is_raised_on_forbidden_request')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except ForbiddenError as e:
                error = e

            assert error.code == 'auth_failed'
            assert error.raw_response is not None
            assert error.status_code == 403
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'username'
            assert error_message.code == 'invalid_supplied_credentials'
            assert error_message.message == 'Authentication failed with the supplied credentials'  # noqa
            assert not error_message.params
    def test_error_is_raised_on_unexpected_error(self):
        error = None

        session = currencycloud.session(authenticate=False)
        with requests_mock.Mocker() as mock:
            def request_tester(request, context):
                raise Exception('Unexpected Error')
            mock.post(re.compile('.*'), json=request_tester)

            with Betamax(session.requests_session) as betamax:
                betamax.use_cassette('error/is_raised_on_unexpected_error')
                try:
                    currencycloud.session().authenticate()

                    raise Exception("Should have failed")
                except UnexpectedError as e:
                    error = e

        assert error is not None

        expected_error_fields = [
            "login_id: [email protected]",
            "api_key: " + currencycloud.api_key,
            "verb: post",
            "url: https://devapi.thecurrencycloud.com/v2/authenticate/api",
            "inner_error: Exception('Unexpected Error'",
        ]

        error_str = str(error)
        missing = False
        for f in expected_error_fields:
            if f not in error_str:
                missing = True
                break

        assert missing is False
        assert error.inner_error is not None
        assert isinstance(error.inner_error, Exception)
    def test_error_is_raised_on_internal_server_error(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('error/is_raised_on_internal_server_error')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except InternalApplicationError as e:
                error = e

            assert error.code == 'internal_application_error'
            assert error.raw_response is not None
            assert error.status_code == 500
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'base'
            assert error_message.code == 'internal_application_error'
            assert error_message.message == 'A general application error occurred'  # noqa
            assert str(
                error_message.params['request_id']) == '2771875643610572878'
    def test_reference_can_retrieve_settlement_accounts(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('reference/can_retrieve_settlement_accounts')

            settlement_accounts = Reference.settlement_accounts(currency='GBP')

            assert len(settlement_accounts) > 0

            settlement_account = settlement_accounts[0]

            assert isinstance(settlement_account, SettlementAccount)
            assert settlement_account.bank_name
            assert 'The Currency Cloud GBP' in settlement_account.bank_account_holder_name  # noqa
    def test_error_is_raised_when_too_many_requests_have_been_issued(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette(
                'error/is_raised_when_too_many_requests_have_been_issued')
            error = None
            try:
                currencycloud.session().authenticate()

                raise Exception("Should have failed")
            except TooManyRequestsError as e:
                error = e

            assert error.code == 'too_many_requests'
            assert error.raw_response is not None
            assert error.status_code == 429
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'base'
            assert error_message.code == 'too_many_requests'
            assert error_message.message == 'Too many requests have been made to the api. Please refer to the Developer Center for more information'  # noqa
            assert not error_message.params
    def test_rates_can_provide_detailed_rate(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('rates/can_provide_detailed_rate')

            detailed_rate = Rate.detailed(
                buy_currency="GBP",
                sell_currency="USD",
                fixed_side='buy',
                amount=10000
            )

            assert isinstance(detailed_rate, Rate)
            assert isinstance(float(detailed_rate.client_sell_amount), float)
    def test_reference_can_retrieve_currencies(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('reference/can_retrieve_currencies')

            currencies = Reference.currencies()

            assert len(currencies) > 0

            currency = currencies[0]

            assert isinstance(currency, Currency)
            assert currency.code == 'AED'
            assert currency.name == 'United Arab Emirates Dirham'
            assert currency.decimal_places == 2
    def test_actions_can_validate_beneficiaries(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_validate_beneficiaries')

            params = {
                'bank_country': 'GB',
                'currency': 'GBP',
                'account_number': TestActions.beneficiary_params['account_number'],  # noqa
                'routing_code_type_1': TestActions.beneficiary_params['routing_code_type_2'],  # noqa
                'routing_code_value_1': TestActions.beneficiary_params['routing_code_value_2'],  # noqa
                'payment_types': ['regular']}

            beneficiary = Beneficiary.validate(**params)

            assert isinstance(beneficiary, Beneficiary)
            assert beneficiary.account_number == TestActions.beneficiary_params[  # noqa
                'account_number']
            assert 'regular' in beneficiary.payment_types
    def test_error_is_raised_when_a_resource_is_not_found(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette(
                'error/is_raised_when_a_resource_is_not_found')
            error = None
            try:
                Beneficiary.retrieve('081596c9-02de-483e-9f2a-4cf55dcdf98c')

                raise Exception("Should have failed")
            except NotFoundError as e:
                error = e

            assert error.code == 'beneficiary_not_found'
            assert error.raw_response is not None
            assert error.status_code == 404
            assert len(error.messages) == 1

            error_message = error.messages[0]
            assert error_message.field == 'id'
            assert error_message.code == 'beneficiary_not_found'
            assert error_message.message == 'Beneficiary was not found for this id'  # noqa
            assert not error_message.params
    def test_actions_can_find(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette('actions/can_find')

            beneficiaries = Beneficiary.find(
                bank_account_holder_name=TestActions.beneficiary_params['bank_account_holder_name'])  # noqa

            assert beneficiaries
            assert len(beneficiaries) >= 1

            TestActions.beneficiary_first_id = beneficiaries[0].id

            for beneficiary in beneficiaries:
                assert isinstance(beneficiary, Beneficiary)

            pagination = beneficiaries.pagination

            assert pagination.total_entries > 0
            assert pagination.current_page == 1
            assert pagination.per_page == 25
            assert pagination.previous_page == -1
            assert pagination.order == 'created_at'
            assert pagination.order_asc_desc == 'asc'
    def test_settlement_can_add_conversion(self):
        session = currencycloud.session(authenticate=False)
        with Betamax(session.requests_session) as betamax:
            betamax.use_cassette("settlements/can_add_conversion")

            conversion = Conversion.create(**self.params)
            settlement = Settlement.create()
            updated_settlement = settlement.add_conversion(conversion.id)

            assert settlement is updated_settlement
            assert conversion.id in settlement.conversion_ids
            assert len(settlement.entries) > 0

            gbp_currency = settlement.entries[0]
            assert "GBP" in gbp_currency
            assert gbp_currency["GBP"] == {"receive_amount": "1000.00", "send_amount": "0.00"}

            usd_currency = settlement.entries[1]
            assert "USD" in usd_currency
            assert usd_currency["USD"]["receive_amount"] == "0.00"
            assert usd_currency["USD"]["send_amount"]

            TestSettlements.settlement_id = settlement.id
            TestSettlements.conversion_id = conversion.id
    def __init__(self, session=None):
        if session is None:
            session = currencycloud.session()

        self.__session = session