Changeset View
Changeset View
Standalone View
Standalone View
looper/tests/test_payment_methods.py
| import typing | import typing | ||||
| from unittest import mock | from unittest import mock | ||||
| from django.urls import reverse | from django.urls import reverse | ||||
| from .base import AbstractLooperTestCase | |||||
| from looper import forms, gateways | |||||
| from looper.middleware import PREFERRED_CURRENCY_SESSION_KEY | |||||
| from looper.views.settings import PaymentMethodReplaceView | from looper.views.settings import PaymentMethodReplaceView | ||||
| from ..middleware import PREFERRED_CURRENCY_SESSION_KEY | |||||
| from .. import forms, gateways | |||||
| from . import AbstractLooperTestCase | |||||
| address_data = { | address_data = { | ||||
| 'full_name': 'Erik von Namenstein', | 'full_name': 'Erik von Namenstein', | ||||
| 'company': 'Nöming Thingéys', | 'company': 'Nöming Thingéys', | ||||
| 'street_address': 'Scotland Pl', | 'street_address': 'Scotland Pl', | ||||
| 'locality': 'Amsterdongel', | 'locality': 'Amsterdongel', | ||||
| 'postal_code': '1025 ET', | 'postal_code': '1025 ET', | ||||
| 'region': 'Worbelmoster', | 'region': 'Worbelmoster', | ||||
| 'country': 'NL', | 'country': 'NL', | ||||
| } | } | ||||
| class PaymentMethodChangeTest(AbstractLooperTestCase): | class PaymentMethodChangeTest(AbstractLooperTestCase): | ||||
| fixtures = AbstractLooperTestCase.fixtures + ['mock-gateway'] | fixtures = AbstractLooperTestCase.fixtures + ['mock-gateway'] | ||||
| def setUp(self): | def setUp(self): | ||||
| super().setUp() | super().setUp() | ||||
| self.client.force_login(self.user) | self.client.force_login(self.user) | ||||
| def _happy_change( | def _happy_change( | ||||
| self, subs, | self, | ||||
| subs, | |||||
| to_gateway: typing.Type[gateways.AbstractPaymentGateway] = gateways.MockableGateway, | to_gateway: typing.Type[gateways.AbstractPaymentGateway] = gateways.MockableGateway, | ||||
| expect_new_payment_method=True, | expect_new_payment_method=True, | ||||
| ): | ): | ||||
| orig_pm = subs.payment_method | orig_pm = subs.payment_method | ||||
| self.assertIsNotNone(orig_pm) | self.assertIsNotNone(orig_pm) | ||||
| paymeth_count_before = self.user.paymentmethod_set.count() | paymeth_count_before = self.user.paymentmethod_set.count() | ||||
| url = reverse('looper:payment_method_change', kwargs={'subscription_id': subs.id}) | url = reverse('looper:payment_method_change', kwargs={'subscription_id': subs.id}) | ||||
| with mock.patch('looper.gateways.MockableGateway.generate_client_token') as mock_gct: | with mock.patch('looper.gateways.MockableGateway.generate_client_token') as mock_gct: | ||||
| mock_gct.return_value = 'mock-client-auth-token' | mock_gct.return_value = 'mock-client-auth-token' | ||||
| resp = self.client.get(url) | resp = self.client.get(url) | ||||
| self.assertEqual(200, resp.status_code, resp.content.decode()) | self.assertEqual(200, resp.status_code, resp.content.decode()) | ||||
| form = forms.ChangePaymentMethodForm({ | form = forms.ChangePaymentMethodForm( | ||||
| { | |||||
| **address_data, | **address_data, | ||||
| 'payment_method_nonce': 'new-pm-nonce', | 'payment_method_nonce': 'new-pm-nonce', | ||||
| 'gateway': to_gateway.gateway_name, | 'gateway': to_gateway.gateway_name, | ||||
| 'next_url_after_done': '/je-moeder', | 'next_url_after_done': '/je-moeder', | ||||
| }) | } | ||||
| ) | |||||
| form.full_clean() | form.full_clean() | ||||
| gw_name = f'{to_gateway.__module__}.{to_gateway.__name__}' | gw_name = f'{to_gateway.__module__}.{to_gateway.__name__}' | ||||
| with mock.patch(f'{gw_name}.customer_create') as mock_cc, \ | with mock.patch(f'{gw_name}.customer_create') as mock_cc, mock.patch( | ||||
| mock.patch(f'{gw_name}.payment_method_create') as mock_pmc: | f'{gw_name}.payment_method_create' | ||||
| ) as mock_pmc: | |||||
| mock_cc.return_value = 'mock-customer-id' | mock_cc.return_value = 'mock-customer-id' | ||||
| mock_paymeth = mock.Mock(spec=gateways.PaymentMethodInfo) | mock_paymeth = mock.Mock(spec=gateways.PaymentMethodInfo) | ||||
| mock_paymeth.token = 'new-mock-payment-token' | mock_paymeth.token = 'new-mock-payment-token' | ||||
| mock_paymeth.recognisable_name.return_value = 'new-mock-recognisable-name' | mock_paymeth.recognisable_name.return_value = 'new-mock-recognisable-name' | ||||
| mock_paymeth.type_for_database.return_value = 'pa' | mock_paymeth.type_for_database.return_value = 'pa' | ||||
| mock_pmc.return_value = mock_paymeth | mock_pmc.return_value = mock_paymeth | ||||
| resp = self.client.post(url, data=form.cleaned_data) | resp = self.client.post(url, data=form.cleaned_data) | ||||
| self.assertEqual(302, resp.status_code, resp.content.decode()) | self.assertEqual(302, resp.status_code, resp.content.decode()) | ||||
| self.assertEqual('/je-moeder', resp['Location']) | self.assertEqual('/je-moeder', resp['Location']) | ||||
| # A new payment method could have been added. | # A new payment method could have been added. | ||||
| self.assertEqual(paymeth_count_before + expect_new_payment_method, | self.assertEqual( | ||||
| self.user.paymentmethod_set.count()) | paymeth_count_before + expect_new_payment_method, self.user.paymentmethod_set.count() | ||||
| ) | |||||
| new_pm = self.user.paymentmethod_set.exclude(pk=orig_pm.pk).first() | new_pm = self.user.paymentmethod_set.exclude(pk=orig_pm.pk).first() | ||||
| return new_pm, orig_pm | return new_pm, orig_pm | ||||
| def test_happy_change_subs_order_paid(self): | def test_happy_change_subs_order_paid(self): | ||||
| subs = self.create_active_subscription() | subs = self.create_active_subscription() | ||||
| order = subs.latest_order() | order = subs.latest_order() | ||||
| self.assertEqual('paid', order.status) | self.assertEqual('paid', order.status) | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | class PaymentMethodChangeTest(AbstractLooperTestCase): | ||||
| def test_mock_to_bank_to_bank(self): | def test_mock_to_bank_to_bank(self): | ||||
| subs = self.create_on_hold_subscription() | subs = self.create_on_hold_subscription() | ||||
| order = subs.latest_order() | order = subs.latest_order() | ||||
| self.assertEqual('created', order.status) | self.assertEqual('created', order.status) | ||||
| new_pm1, orig_pm1 = self._happy_change(subs, gateways.BankGateway) | new_pm1, orig_pm1 = self._happy_change(subs, gateways.BankGateway) | ||||
| self._happy_change(subs, gateways.MockableGateway) | self._happy_change(subs, gateways.MockableGateway) | ||||
| new_pm2, orig_pm2 = self._happy_change(subs, gateways.BankGateway, | new_pm2, orig_pm2 = self._happy_change( | ||||
| expect_new_payment_method=False) | subs, gateways.BankGateway, expect_new_payment_method=False | ||||
| ) | |||||
| self.assertEqual(new_pm1, new_pm2, | |||||
| 'Switching from bank to bank should keep the payment method') | self.assertEqual( | ||||
| new_pm1, new_pm2, 'Switching from bank to bank should keep the payment method' | |||||
| ) | |||||
| def test_different_currency(self): | def test_different_currency(self): | ||||
| self.client.get('/') # Get any URL to start a session. | self.client.get('/') # Get any URL to start a session. | ||||
| self.assertEqual('USD', self.client.session[PREFERRED_CURRENCY_SESSION_KEY]) | self.assertEqual('USD', self.client.session[PREFERRED_CURRENCY_SESSION_KEY]) | ||||
| subs = self.create_active_subscription() | subs = self.create_active_subscription() | ||||
| self.assertEqual('EUR', subs.currency) | self.assertEqual('EUR', subs.currency) | ||||
| url = reverse('looper:payment_method_change', kwargs={'subscription_id': subs.id}) | url = reverse('looper:payment_method_change', kwargs={'subscription_id': subs.id}) | ||||
| passed_currency = '' | passed_currency = '' | ||||
| def generate_client_token(self, for_currency: str, | def generate_client_token( | ||||
| gateway_customer_id: typing.Optional[str] = None) -> str: | self, for_currency: str, gateway_customer_id: typing.Optional[str] = None | ||||
| ) -> str: | |||||
| nonlocal passed_currency | nonlocal passed_currency | ||||
| passed_currency = for_currency | passed_currency = for_currency | ||||
| return 'mock-client-auth-token' | return 'mock-client-auth-token' | ||||
| with mock.patch('looper.gateways.MockableGateway.generate_client_token', | with mock.patch( | ||||
| new=generate_client_token) as mock_gct: | 'looper.gateways.MockableGateway.generate_client_token', new=generate_client_token | ||||
| ): | |||||
| resp = self.client.get(url) | resp = self.client.get(url) | ||||
| self.assertEqual(200, resp.status_code, resp.content.decode()) | self.assertEqual(200, resp.status_code, resp.content.decode()) | ||||
| self.assertEqual('EUR', passed_currency, 'Expected the subscription currency to be used') | self.assertEqual('EUR', passed_currency, 'Expected the subscription currency to be used') | ||||
| class PaymentMethodReplaceTest(AbstractLooperTestCase): | class PaymentMethodReplaceTest(AbstractLooperTestCase): | ||||
| fixtures = AbstractLooperTestCase.fixtures + ['mock-gateway'] | fixtures = AbstractLooperTestCase.fixtures + ['mock-gateway'] | ||||
| Show All 25 Lines | def test_happy_change_subs_and_order(self): | ||||
| # The session should have one preferred currency, whereas the payment method | # The session should have one preferred currency, whereas the payment method | ||||
| # was used to pay with a different currency. | # was used to pay with a different currency. | ||||
| self.client.get('/') # Get any URL to start a session. | self.client.get('/') # Get any URL to start a session. | ||||
| self.assertEqual('USD', self.client.session[PREFERRED_CURRENCY_SESSION_KEY]) | self.assertEqual('USD', self.client.session[PREFERRED_CURRENCY_SESSION_KEY]) | ||||
| self.assertEqual('EUR', subs_active.currency) | self.assertEqual('EUR', subs_active.currency) | ||||
| passed_currency = '' | passed_currency = '' | ||||
| def generate_client_token(self, for_currency: str, | def generate_client_token( | ||||
| gateway_customer_id: typing.Optional[str] = None) -> str: | self, for_currency: str, gateway_customer_id: typing.Optional[str] = None | ||||
| ) -> str: | |||||
| nonlocal passed_currency | nonlocal passed_currency | ||||
| passed_currency = for_currency | passed_currency = for_currency | ||||
| return 'mock-client-auth-token' | return 'mock-client-auth-token' | ||||
| with mock.patch('looper.gateways.MockableGateway.generate_client_token', | with mock.patch( | ||||
| new=generate_client_token) as mock_gct: | 'looper.gateways.MockableGateway.generate_client_token', new=generate_client_token | ||||
| ): | |||||
| resp = self.client.get(url) | resp = self.client.get(url) | ||||
| self.assertEqual(200, resp.status_code, resp.content.decode()) | self.assertEqual(200, resp.status_code, resp.content.decode()) | ||||
| self.assertEqual('EUR', passed_currency, 'Expected the subscription currency to be used') | self.assertEqual('EUR', passed_currency, 'Expected the subscription currency to be used') | ||||
| form = PaymentMethodReplaceView.form_class({ | form = PaymentMethodReplaceView.form_class( | ||||
| 'payment_method_nonce': 'new-pm-nonce', | {'payment_method_nonce': 'new-pm-nonce', 'gateway': 'mock'} | ||||
| 'gateway': 'mock', | ) | ||||
| }) | |||||
| form.full_clean() | form.full_clean() | ||||
| with mock.patch('looper.gateways.MockableGateway.customer_create') as mock_cc, \ | with mock.patch('looper.gateways.MockableGateway.customer_create') as mock_cc, mock.patch( | ||||
| mock.patch('looper.gateways.MockableGateway.payment_method_create') as mock_pmc: | 'looper.gateways.MockableGateway.payment_method_create' | ||||
| ) as mock_pmc: | |||||
| mock_cc.return_value = 'mock-customer-id' | mock_cc.return_value = 'mock-customer-id' | ||||
| mock_paymeth = mock.Mock(spec=gateways.PaymentMethodInfo) | mock_paymeth = mock.Mock(spec=gateways.PaymentMethodInfo) | ||||
| mock_paymeth.token = 'new-mock-payment-token' | mock_paymeth.token = 'new-mock-payment-token' | ||||
| mock_paymeth.recognisable_name.return_value = 'new-mock-recognisable-name' | mock_paymeth.recognisable_name.return_value = 'new-mock-recognisable-name' | ||||
| mock_paymeth.type_for_database.return_value = 'pa' | mock_paymeth.type_for_database.return_value = 'pa' | ||||
| mock_pmc.return_value = mock_paymeth | mock_pmc.return_value = mock_paymeth | ||||
| Show All 9 Lines | def test_happy_change_subs_and_order(self): | ||||
| subs_active.refresh_from_db() | subs_active.refresh_from_db() | ||||
| subs_on_hold.refresh_from_db() | subs_on_hold.refresh_from_db() | ||||
| subs_cancelled.refresh_from_db() | subs_cancelled.refresh_from_db() | ||||
| order_subs_on_hold.refresh_from_db() | order_subs_on_hold.refresh_from_db() | ||||
| order_subs_active.refresh_from_db() | order_subs_active.refresh_from_db() | ||||
| order_subs_cancelled.refresh_from_db() | order_subs_cancelled.refresh_from_db() | ||||
| # It should be used for the still-payable stuff, but not for already-paid/cancelled stuff. | # It should be used for the still-payable stuff, but not for already-paid/cancelled stuff. | ||||
| self.assertEqual(new_pm.pk, subs_active.payment_method_id, | self.assertEqual( | ||||
| f'Expected subs of status {subs_active.status!r} to change PM') | new_pm.pk, | ||||
| self.assertEqual(new_pm.pk, subs_on_hold.payment_method_id, | subs_active.payment_method_id, | ||||
| f'Expected subs of status {subs_on_hold.status!r} to change PM') | f'Expected subs of status {subs_active.status!r} to change PM', | ||||
| self.assertEqual(new_pm.pk, order_subs_on_hold.payment_method_id, | ) | ||||
| f'Expected order of status {order_subs_on_hold.status!r} to change PM') | self.assertEqual( | ||||
| new_pm.pk, | |||||
| subs_on_hold.payment_method_id, | |||||
| f'Expected subs of status {subs_on_hold.status!r} to change PM', | |||||
| ) | |||||
| self.assertEqual( | |||||
| new_pm.pk, | |||||
| order_subs_on_hold.payment_method_id, | |||||
| f'Expected order of status {order_subs_on_hold.status!r} to change PM', | |||||
| ) | |||||
| self.assertEqual(orig_pm.pk, subs_cancelled.payment_method_id) | self.assertEqual(orig_pm.pk, subs_cancelled.payment_method_id) | ||||
| self.assertEqual(orig_pm.pk, order_subs_active.payment_method_id) | self.assertEqual(orig_pm.pk, order_subs_active.payment_method_id) | ||||
| self.assertEqual(orig_pm.pk, order_subs_cancelled.payment_method_id) | self.assertEqual(orig_pm.pk, order_subs_cancelled.payment_method_id) | ||||