Changeset View
Changeset View
Standalone View
Standalone View
looper/tests/test_transactions.py
| from unittest import mock | from unittest import mock | ||||
| from django.contrib.admin.models import LogEntry | from django.contrib.admin.models import LogEntry | ||||
| import requests.exceptions | import requests.exceptions | ||||
| import looper.exceptions | |||||
| from . import AbstractLooperTestCase | from . import AbstractLooperTestCase | ||||
| from .. import admin_log | from .. import admin_log | ||||
| from looper.money import Money | |||||
| from looper.taxes import ProductType | |||||
| import looper.exceptions | |||||
| @mock.patch('looper.gateways.BraintreeGateway.refund', new=mock.Mock(return_value='mocked-refund-transaction-id')) | |||||
| @mock.patch('looper.gateways.BraintreeGateway.transact_sale', return_value='mocked-transaction-id') | @mock.patch('looper.gateways.BraintreeGateway.transact_sale', return_value='mocked-transaction-id') | ||||
| class TransactionsTestCase(AbstractLooperTestCase): | class TransactionsTestCase(AbstractLooperTestCase): | ||||
| def test_transaction_create_from_subscription(self, _) -> None: | def test_transaction_create_from_subscription(self, _) -> None: | ||||
| subscription = self.create_subscription() | subscription = self.create_subscription() | ||||
| # We used to generate the initial order automatically, but no longer. | # We used to generate the initial order automatically, but no longer. | ||||
| order = subscription.latest_order() | order = subscription.latest_order() | ||||
| self.assertIsNone(order) | self.assertIsNone(order) | ||||
| ▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | def test_charge_bad_status(self, mock_transact_sale) -> None: | ||||
| mock_transact_sale.assert_not_called() | mock_transact_sale.assert_not_called() | ||||
| # This is a programming error, so it shouldn't create a log event. | # This is a programming error, so it shouldn't create a log event. | ||||
| entries_q = admin_log.entries_for(trans) | entries_q = admin_log.entries_for(trans) | ||||
| self.assertEqual(0, len(entries_q)) | self.assertEqual(0, len(entries_q)) | ||||
| self.assertEqual('', trans.failure_message) | self.assertEqual('', trans.failure_message) | ||||
| self.assertFalse(trans.paid) | self.assertFalse(trans.paid) | ||||
| def test_refund_single(self, mock_transact_sale) -> None: | |||||
| subscription = self.create_subscription() | |||||
| order = subscription.generate_order() | |||||
| trans = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| mock_transact_sale.assert_not_called() | |||||
| self.assertTrue(trans.charge()) | |||||
| # No refund yet | |||||
| self.assertIsNone(trans.refunded_at) | |||||
| self.assertIsNone(order.refunded_at) | |||||
| self.assertEqual(trans.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans.order.refunded, Money('EUR', 0)) | |||||
| # Issue a refund | |||||
| trans.refund(Money('EUR', 100)) | |||||
| trans.refresh_from_db() | |||||
| trans.order.refresh_from_db() | |||||
| # Check that refund was successful and that it was recorded | |||||
| self.assertIsNotNone(trans.refunded_at) | |||||
| self.assertEqual(trans.refunded_at, trans.order.refunded_at) | |||||
| self.assertEqual(trans.amount_refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans.order.refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans.order.tax_refunded, Money('EUR', 0)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!1.00 was successful') | |||||
| entries_q = admin_log.entries_for(trans.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!1.00 (refunded tax EUR!0.00)' | |||||
| ) | |||||
| def test_refund_single_with_tax(self, mock_transact_sale) -> None: | |||||
| # Prepare a subscription with Dutch VAT: | |||||
| subscription = self.create_subscription_with_product_and_billing_details( | |||||
| product_type=ProductType.ELECTRONIC_SERVICE.value, | |||||
| country='NL', | |||||
| ) | |||||
| self.assertEqual(subscription.tax_country, 'NL') | |||||
| self.assertEqual(subscription.tax_rate, 21) | |||||
| self.assertEqual(subscription.tax_type, 'VATC') | |||||
| order = subscription.generate_order() | |||||
| trans = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| mock_transact_sale.assert_not_called() | |||||
| mock_transact_sale.return_value = 'mocked-transaction-id' | |||||
| self.assertTrue(trans.charge()) | |||||
| # No refund yet | |||||
| self.assertIsNone(trans.refunded_at) | |||||
| self.assertIsNone(order.refunded_at) | |||||
| self.assertEqual(trans.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans.order.refunded, Money('EUR', 0)) | |||||
| # Issue a refund | |||||
| trans.refund(Money('EUR', 100)) | |||||
| trans.refresh_from_db() | |||||
| trans.order.refresh_from_db() | |||||
| # Check that refund was successful and that it was recorded | |||||
| self.assertIsNotNone(trans.refunded_at) | |||||
| self.assertEqual(trans.amount_refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans.order.refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans.order.tax_refunded, Money('EUR', 21)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!1.00 was successful') | |||||
| entries_q = admin_log.entries_for(trans.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!1.00 (refunded tax EUR!0.21)' | |||||
| ) | |||||
| def test_refund_multiple(self, mock_transact_sale) -> None: | |||||
| subscription = self.create_subscription() | |||||
| order = subscription.generate_order() | |||||
| trans1 = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| trans2 = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| mock_transact_sale.assert_not_called() | |||||
| mock_transact_sale.return_value = 'mocked-transaction-id' | |||||
| self.assertTrue(trans1.charge()) | |||||
| self.assertTrue(trans2.charge()) | |||||
| # No refund yet | |||||
| self.assertIsNone(order.refunded_at) | |||||
| self.assertIsNone(trans1.refunded_at) | |||||
| self.assertIsNone(trans2.refunded_at) | |||||
| self.assertEqual(trans1.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans2.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans1.order.refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans2.order.refunded, Money('EUR', 0)) | |||||
| # Issue a refund | |||||
| trans1.refund(Money('EUR', 100)) | |||||
| trans1.refresh_from_db() | |||||
| trans1.order.refresh_from_db() | |||||
| # Check that refund was successful and that it was recorded | |||||
| self.assertIsNotNone(trans1.refunded_at) | |||||
| self.assertEqual(trans1.amount_refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans1.order.refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans1.order.tax_refunded, Money('EUR', 0)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans1) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!1.00 was successful') | |||||
| entries_q = admin_log.entries_for(trans1.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!1.00 (refunded tax EUR!0.00)' | |||||
| ) | |||||
| # Issue another refund | |||||
| trans2.refund(Money('EUR', 250)) | |||||
| trans2.refresh_from_db() | |||||
| trans2.order.refresh_from_db() | |||||
| self.assertIsNotNone(trans2.refunded_at) | |||||
| self.assertEqual(trans2.amount_refunded, Money('EUR', 250)) | |||||
| self.assertEqual(trans2.order.refunded, Money('EUR', 350)) | |||||
| self.assertEqual(trans2.order.tax_refunded, Money('EUR', 0)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans2) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!2.50 was successful') | |||||
| entries_q = admin_log.entries_for(trans2.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!3.50 (refunded tax EUR!0.00)' | |||||
| ) | |||||
| def test_refund_multiple_with_tax(self, mock_transact_sale) -> None: | |||||
| # Prepare a subscription with Dutch VAT: | |||||
| subscription = self.create_subscription_with_product_and_billing_details( | |||||
| product_type=ProductType.ELECTRONIC_SERVICE.value, | |||||
| country='NL', | |||||
| ) | |||||
| self.assertEqual(subscription.tax_country, 'NL') | |||||
| self.assertEqual(subscription.tax_rate, 21) | |||||
| self.assertEqual(subscription.tax_type, 'VATC') | |||||
| order = subscription.generate_order() | |||||
| trans1 = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| trans2 = order.generate_transaction(ip_address='fe80::5ad5:4eaf:feb0:4747') | |||||
| mock_transact_sale.assert_not_called() | |||||
| mock_transact_sale.return_value = 'mocked-transaction-id' | |||||
| self.assertTrue(trans1.charge()) | |||||
| self.assertTrue(trans2.charge()) | |||||
| # No refund yet | |||||
| self.assertIsNone(order.refunded_at) | |||||
| self.assertIsNone(trans1.refunded_at) | |||||
| self.assertIsNone(trans2.refunded_at) | |||||
| self.assertEqual(trans1.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans2.amount_refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans1.order.refunded, Money('EUR', 0)) | |||||
| self.assertEqual(trans2.order.refunded, Money('EUR', 0)) | |||||
| # Issue a refund | |||||
| trans1.refund(Money('EUR', 100)) | |||||
| trans1.refresh_from_db() | |||||
| trans1.order.refresh_from_db() | |||||
| # Check that refund was successful and that it was recorded | |||||
| self.assertIsNotNone(trans1.refunded_at) | |||||
| self.assertEqual(trans1.amount_refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans1.order.refunded, Money('EUR', 100)) | |||||
| self.assertEqual(trans1.order.tax_refunded, Money('EUR', 21)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans1) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!1.00 was successful') | |||||
| entries_q = admin_log.entries_for(trans1.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!1.00 (refunded tax EUR!0.21)' | |||||
| ) | |||||
| self.assertEqual(trans1.refunded_at, trans1.order.refunded_at) | |||||
| # Issue another refund | |||||
| trans2.refund(Money('EUR', 250)) | |||||
| trans2.refresh_from_db() | |||||
| trans2.order.refresh_from_db() | |||||
| self.assertIsNotNone(trans2.refunded_at) | |||||
| self.assertEqual(trans2.amount_refunded, Money('EUR', 250)) | |||||
| # Both refund total and tax refunded were summed and stored | |||||
| self.assertEqual(trans2.order.refunded, Money('EUR', 350)) | |||||
| self.assertEqual(trans2.order.tax_refunded, Money('EUR', 74)) | |||||
| # A LogEntry should have been created about the refund | |||||
| entries_q = admin_log.entries_for(trans2) | |||||
| self.assertEqual(entries_q[0].change_message, 'Refund of EUR!2.50 was successful') | |||||
| entries_q = admin_log.entries_for(trans2.order) | |||||
| self.assertEqual( | |||||
| entries_q[0].change_message, | |||||
| 'Updated refunded amount to EUR!3.50 (refunded tax EUR!0.74)' | |||||
| ) | |||||
| self.assertEqual(trans2.refunded_at, trans2.order.refunded_at) | |||||