Changeset View
Changeset View
Standalone View
Standalone View
looper/tests/test_customer.py
| from unittest.mock import patch | from unittest.mock import patch | ||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||
| from django.urls import reverse | from django.urls import reverse | ||||
| from django_countries.fields import Country | from django_countries.fields import Country | ||||
| from looper.gateways import PaymentMethodInfo | from looper.gateways import PaymentMethodInfo | ||||
| from looper.models import Customer, Address, PaymentMethod, Gateway, GatewayCustomerId | from looper.models import Customer, Address, PaymentMethod, Gateway, GatewayCustomerId | ||||
| from looper.exceptions import GatewayError | from looper.exceptions import GatewayError | ||||
| from .. import signals | from looper import signals | ||||
| from . import AbstractBaseTestCase | from .base import AbstractBaseTestCase | ||||
| class AbstractSingleUserTestCase(AbstractBaseTestCase): | class AbstractSingleUserTestCase(AbstractBaseTestCase): | ||||
| fixtures = ['testuser'] | fixtures = ['testuser'] | ||||
| def setUp(self): | def setUp(self): | ||||
| self.user: User = User.objects.get(email='harry@blender.org') | self.user: User = User.objects.get(email='harry@blender.org') | ||||
| self.customer: Customer = self.user.customer | self.customer: Customer = self.user.customer | ||||
| class BillingAddressAsTextTest(AbstractSingleUserTestCase): | class BillingAddressAsTextTest(AbstractSingleUserTestCase): | ||||
| def test_just_country(self): | def test_just_country(self): | ||||
| """Country is a special field, not just a CharField""" | """Country is a special field, not just a CharField""" | ||||
| a = Address( | a = Address(street_address='Buikslotermeerplein 161', country='NL', user=self.user) | ||||
| street_address='Buikslotermeerplein 161', | |||||
| country='NL', | |||||
| user=self.user) | |||||
| a.save() | a.save() | ||||
| self.assertIsInstance(a.country, Country) | self.assertIsInstance(a.country, Country) | ||||
| self.assertEqual('Buikslotermeerplein 161\nNetherlands', | self.assertEqual( | ||||
| self.customer.billing_address_as_text()) | 'Buikslotermeerplein 161\nNetherlands', self.customer.billing_address.as_text() | ||||
| ) | |||||
| class CustomerCreationTestCase(AbstractBaseTestCase): | class CustomerCreationTestCase(AbstractBaseTestCase): | ||||
| def setUp(self): | def setUp(self): | ||||
| self.user = User.objects.create_user('harry', 'harry@blender.org') | self.user = User.objects.create_user('harry', 'harry@blender.org') | ||||
| self.customer: Customer = self.user.customer | self.customer: Customer = self.user.customer | ||||
| def test_customer_creation(self): | def test_customer_creation(self): | ||||
| # The Customer should have been created via the post_save signal | # The Customer should have been created via the post_save signal | ||||
| self.assertIsNotNone(self.customer) | self.assertIsNotNone(self.customer) | ||||
| # The default Customer billing_email should match the User email | # The default Customer billing_email should match the User email | ||||
| self.assertEqual(self.user.email, self.customer.billing_email) | self.assertEqual(self.user.email, self.customer.billing_email) | ||||
| def test_double_customer_creation(self): | def test_double_customer_creation(self): | ||||
| signals.create_customer(User, self.user, created=True) | signals.create_customer(User, self.user, created=True) | ||||
| self.user.refresh_from_db() | self.user.refresh_from_db() | ||||
| self.assertEqual(self.customer.pk, self.user.customer.pk) | self.assertEqual(self.customer.pk, self.user.customer.pk) | ||||
| class CustomerModelTestCase(AbstractSingleUserTestCase): | class CustomerModelTestCase(AbstractSingleUserTestCase): | ||||
| def setUp(self): | def setUp(self): | ||||
| super().setUp() | super().setUp() | ||||
| # Add default Gateway | # Add default Gateway | ||||
| self.gateway = Gateway.objects.create(name='braintree', is_default=True) | self.gateway = Gateway.objects.create(name='braintree', is_default=True) | ||||
| # Isolate Braintree and | # Isolate Braintree and | ||||
| def test_billing_address(self): | def test_billing_address(self): | ||||
| # Ensure the customer does not have an Address associated | # Ensure the customer does not have an Address associated | ||||
| with self.assertRaises(Address.DoesNotExist): | with self.assertRaises(Address.DoesNotExist): | ||||
| Address.objects.get(user=self.user) | Address.objects.get(user=self.user) | ||||
| # Getting the customer address should always return an Address object | # Getting the customer address should always return an Address object | ||||
| self.assertIsInstance(self.customer.billing_address, Address) | self.assertIsInstance(self.customer.billing_address, Address) | ||||
| def test_payment_method_default_no_method(self): | def test_payment_method_default_no_method(self): | ||||
| # Ensure no payment method is associated to the Customer | # Ensure no payment method is associated to the Customer | ||||
| with self.assertRaises(PaymentMethod.DoesNotExist): | with self.assertRaises(PaymentMethod.DoesNotExist): | ||||
| PaymentMethod.objects.get(user=self.user) | PaymentMethod.objects.get(user=self.user) | ||||
| # A customer without payment method should return payment method None | # A customer without payment method should return payment method None | ||||
| self.assertIsNone(self.customer.payment_method_default) | self.assertIsNone(self.customer.payment_method_default) | ||||
| def test_payment_method_default_one_methods(self): | def test_payment_method_default_one_methods(self): | ||||
| # Add one default payment method | # Add one default payment method | ||||
| first_payment_method = PaymentMethod.objects.create(user=self.user, token='abc', | first_payment_method = PaymentMethod.objects.create( | ||||
| gateway=self.gateway) | user=self.user, token='abc', gateway=self.gateway | ||||
| ) | |||||
| # Check that customer.payment_method_default returns that payment method | # Check that customer.payment_method_default returns that payment method | ||||
| self.assertEqual(self.customer.payment_method_default.id, first_payment_method.id) | self.assertEqual(self.customer.payment_method_default.id, first_payment_method.id) | ||||
| def test_payment_method_default_two_methods(self): | def test_payment_method_default_two_methods(self): | ||||
| # Add one default payment method | # Add one default payment method | ||||
| first_payment_method = PaymentMethod.objects.create(user=self.user, token='abc', | first_payment_method = PaymentMethod.objects.create( | ||||
| gateway=self.gateway) | user=self.user, token='abc', gateway=self.gateway | ||||
| ) | |||||
| # Add another payment method | # Add another payment method | ||||
| PaymentMethod.objects.create(user=self.user, token='def', gateway=self.gateway) | PaymentMethod.objects.create(user=self.user, token='def', gateway=self.gateway) | ||||
| # Getting payment_method_default should still return the first payment method | # Getting payment_method_default should still return the first payment method | ||||
| self.assertEqual(self.customer.payment_method_default.id, first_payment_method.id) | self.assertEqual(self.customer.payment_method_default.id, first_payment_method.id) | ||||
| # Add another customer | # Add another customer | ||||
| other_user = User.objects.create_user('ron', 'ron@blender.org') | other_user = User.objects.create_user('ron', 'ron@blender.org') | ||||
| other_customer = other_user.customer | other_customer = other_user.customer | ||||
| # Add a non default payment method for this customer | # Add a non default payment method for this customer | ||||
| other_payment_method = PaymentMethod.objects.create(user=other_user, token='123', | other_payment_method = PaymentMethod.objects.create( | ||||
| gateway=self.gateway) | user=other_user, token='123', gateway=self.gateway | ||||
| ) | |||||
| # Check that we get the proper payment method | # Check that we get the proper payment method | ||||
| self.assertEqual(other_customer.payment_method_default.id, other_payment_method.id) | self.assertEqual(other_customer.payment_method_default.id, other_payment_method.id) | ||||
| def test_payment_method_default_deleted_method(self): | def test_payment_method_default_deleted_method(self): | ||||
| # Create a deleted payment method | # Create a deleted payment method | ||||
| PaymentMethod.objects.create(user=self.user, token='hgr', gateway=self.gateway, | PaymentMethod.objects.create( | ||||
| is_deleted=True) | user=self.user, token='hgr', gateway=self.gateway, is_deleted=True | ||||
| ) | |||||
| self.assertIsNone(self.customer.payment_method_default) | self.assertIsNone(self.customer.payment_method_default) | ||||
| @patch('looper.gateways.BraintreeGateway.payment_method_create') | @patch('looper.gateways.BraintreeGateway.payment_method_create') | ||||
| def test_double_payment_method(self, mock_payment_method_create): | def test_double_payment_method(self, mock_payment_method_create): | ||||
| # Braintree always returns the same payment method token when the | # Braintree always returns the same payment method token when the | ||||
| # user selected the same payment method, even when the nonces are | # user selected the same payment method, even when the nonces are | ||||
| # different, and even when the function called is actually | # different, and even when the function called is actually | ||||
| # `payment_method.create(...)`. | # `payment_method.create(...)`. | ||||
| Show All 15 Lines | def test_gateway_customer_id_get_or_create(self, mock_customer_create): | ||||
| GatewayCustomerId.objects.get(user=self.user) | GatewayCustomerId.objects.get(user=self.user) | ||||
| # Create a new gateway customer | # Create a new gateway customer | ||||
| gateway_customer_id = self.customer.gateway_customer_id_get_or_create(self.gateway) | gateway_customer_id = self.customer.gateway_customer_id_get_or_create(self.gateway) | ||||
| self.assertEqual(expected_customer_id, gateway_customer_id) | self.assertEqual(expected_customer_id, gateway_customer_id) | ||||
| mock_customer_create.assert_called_with() | mock_customer_create.assert_called_with() | ||||
| # Check if it exists in the database | # Check if it exists in the database | ||||
| self.assertEqual(expected_customer_id, | self.assertEqual( | ||||
| GatewayCustomerId.objects.get(user=self.user).gateway_customer_id) | expected_customer_id, GatewayCustomerId.objects.get(user=self.user).gateway_customer_id | ||||
| ) | |||||
| @patch('looper.gateways.BraintreeGateway.customer_create') | @patch('looper.gateways.BraintreeGateway.customer_create') | ||||
| def test_gateway_customer_id_get_or_create_error(self, mock_customer_create): | def test_gateway_customer_id_get_or_create_error(self, mock_customer_create): | ||||
| # Mock the response as a failure | # Mock the response as a failure | ||||
| mock_customer_create.side_effect = GatewayError(message='mock', errors=['je moeder']) | mock_customer_create.side_effect = GatewayError(message='mock', errors=['je moeder']) | ||||
| with self.assertRaises(GatewayError): | with self.assertRaises(GatewayError): | ||||
| self.customer.gateway_customer_id_get_or_create(self.gateway) | self.customer.gateway_customer_id_get_or_create(self.gateway) | ||||
| Show All 20 Lines | def test_payment_method_delete(self, mock_payment_method_delete): | ||||
| pm.refresh_from_db() | pm.refresh_from_db() | ||||
| self.assertTrue(pm.is_deleted, 'Payment method should be soft-deleted') | self.assertTrue(pm.is_deleted, 'Payment method should be soft-deleted') | ||||
| mock_payment_method_delete.assert_called_with('hgr') | mock_payment_method_delete.assert_called_with('hgr') | ||||
| @patch('looper.gateways.BraintreeGateway.payment_method_delete') | @patch('looper.gateways.BraintreeGateway.payment_method_delete') | ||||
| @patch('looper.gateways.BraintreeGateway.payment_method_create') | @patch('looper.gateways.BraintreeGateway.payment_method_create') | ||||
| @patch('looper.gateways.BraintreeGateway.customer_create') | @patch('looper.gateways.BraintreeGateway.customer_create') | ||||
| def test_payment_method_add_deleted(self, mock_customer_create, mock_payment_method_create, | def test_payment_method_add_deleted( | ||||
| mock_payment_method_delete): | self, mock_customer_create, mock_payment_method_create, mock_payment_method_delete | ||||
| ): | |||||
| expected_customer_id = '214745181' | expected_customer_id = '214745181' | ||||
| token = 'act' | token = 'act' | ||||
| mock_customer_create.return_value = expected_customer_id | mock_customer_create.return_value = expected_customer_id | ||||
| mock_payment_method_create.return_value = PaymentMethodInfo(token) | mock_payment_method_create.return_value = PaymentMethodInfo(token) | ||||
| mock_payment_method_delete.return_value = None | mock_payment_method_delete.return_value = None | ||||
| pm = self.customer.payment_method_add('fake-valid-nonce', self.gateway) | pm = self.customer.payment_method_add('fake-valid-nonce', self.gateway) | ||||
| original_pm_pk = pm.pk | original_pm_pk = pm.pk | ||||
| self.assertEqual(token, pm.token) | self.assertEqual(token, pm.token) | ||||
| pm.delete() | pm.delete() | ||||
| pm.refresh_from_db() | pm.refresh_from_db() | ||||
| self.assertTrue(pm.is_deleted, 'Payment method should be soft-deleted') | self.assertTrue(pm.is_deleted, 'Payment method should be soft-deleted') | ||||
| mock_payment_method_delete.assert_called_with('act') | mock_payment_method_delete.assert_called_with('act') | ||||
| new_pm = self.customer.payment_method_add('another-fake-valid-nonce', self.gateway) | new_pm = self.customer.payment_method_add('another-fake-valid-nonce', self.gateway) | ||||
| self.assertFalse(new_pm.is_deleted, 'Payment method should not be deleted') | self.assertFalse(new_pm.is_deleted, 'Payment method should not be deleted') | ||||
| self.assertEqual(original_pm_pk, new_pm.pk, 'Payment method should have been undeleted') | self.assertEqual(original_pm_pk, new_pm.pk, 'Payment method should have been undeleted') | ||||
| class CustomerEditTestCase(AbstractSingleUserTestCase): | class CustomerEditTestCase(AbstractSingleUserTestCase): | ||||
| def test_customer_form_missing_fields(self): | def test_customer_form_missing_fields(self): | ||||
| pre_changes: dict = self.customer.__dict__.copy() | pre_changes: dict = self.customer.__dict__.copy() | ||||
| self.client.force_login(self.user) | self.client.force_login(self.user) | ||||
| self.client.post(reverse('looper:settings_personal_info'), data={}) | self.client.post(reverse('looper:settings_personal_info'), data={}) | ||||
| # The customer shouldn't be updated; values are missing in the posted data. | # The customer shouldn't be updated; values are missing in the posted data. | ||||
| self.customer.refresh_from_db() | self.customer.refresh_from_db() | ||||
| Show All 19 Lines | def test_customer_form_success(self): | ||||
| self.assertEqual(payload['company'], self.customer.company) | self.assertEqual(payload['company'], self.customer.company) | ||||
| # These fields weren't part of the form, so should keep their initial value. | # These fields weren't part of the form, so should keep their initial value. | ||||
| self.assertEqual('NL11ABCDEFQQQQ', self.customer.vat_number) | self.assertEqual('NL11ABCDEFQQQQ', self.customer.vat_number) | ||||
| self.assertEqual(True, self.customer.tax_exempt) | self.assertEqual(True, self.customer.tax_exempt) | ||||
| class BillingAddressEditTestCase(AbstractSingleUserTestCase): | class BillingAddressEditTestCase(AbstractSingleUserTestCase): | ||||
| def setUp(self): | def setUp(self): | ||||
| super().setUp() | super().setUp() | ||||
| ba: Address = self.customer.billing_address | ba: Address = self.customer.billing_address | ||||
| ba.company = self.customer.company | ba.company = self.customer.company | ||||
| ba.country = 'NL' | ba.country = 'NL' | ||||
| ba.street_address = 'Beukslotermeerplein 161' | ba.street_address = 'Beukslotermeerplein 161' | ||||
| ba.save() | ba.save() | ||||
| def test_billing_address_form_missing_fields(self): | def test_billing_address_form_missing_fields(self): | ||||
| pre_changes: str = self.customer.billing_address_as_text() | pre_changes: str = self.customer.billing_address.as_text() | ||||
| self.client.force_login(self.user) | self.client.force_login(self.user) | ||||
| self.client.post(reverse('looper:settings_billing_info'), data={}) | self.client.post(reverse('looper:settings_billing_info'), data={}) | ||||
| self.customer.refresh_from_db() | self.customer.refresh_from_db() | ||||
| self.customer.billing_address.refresh_from_db() | self.customer.billing_address.refresh_from_db() | ||||
| self.assertEqual(pre_changes, self.customer.billing_address_as_text()) | self.assertEqual(pre_changes, self.customer.billing_address.as_text()) | ||||
| def test_billing_address_form_success(self): | def test_billing_address_form_success(self): | ||||
| payload = { | payload = { | ||||
| 'full_name': 'Гарри Поттер', | 'full_name': 'Гарри Поттер', | ||||
| 'company': 'Министерство Магии', | 'company': 'Министерство Магии', | ||||
| 'street_address': 'Scotland Pl', | 'street_address': 'Scotland Pl', | ||||
| 'locality': 'London', | 'locality': 'London', | ||||
| 'postal_code': 'SW1A 2BD', | 'postal_code': 'SW1A 2BD', | ||||
| Show All 18 Lines | |||||