def setUp(self): super().setUp() self.admin = User.objects.create_superuser('admin', '*****@*****.**', 'password') self.request_factory = RequestFactory() self.account = get_account(account_name='My Account') self.account.save() self.imported_payment = get_payment( identifier='My Payment 1', account=self.account, state=PaymentState.READY_TO_PROCESS, variable_symbol='VAR1', transaction_date=date(2019, 1, 1), ) self.imported_payment.save() self.processed_payment = get_payment( identifier='My Payment 2', account=self.account, state=PaymentState.PROCESSED, variable_symbol='VAR2', processor='dummy', processing_error=PaymentProcessingError.DUPLICITY, ) self.processed_payment.save() self.invoice = get_invoice(number='INV111222') self.invoice.save() self.invoice.payments.add(self.processed_payment) self.payment_client = get_client(handle='HANDLE', payment=self.processed_payment) self.payment_client.save()
def test_payments_from_to(self): """Test from/to parameters.""" payment = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy') payment.save() payment2 = get_payment(identifier='PAYMENT_2', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy', create_time=datetime.date(2010, 1, 1)) payment2.save() payment3 = get_payment(identifier='PAYMENT_3', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy') payment3.save() # auto_add fields must be changed after first save: payment.create_time = datetime.date(2000, 1, 1) payment.save() payment2.create_time = datetime.date(2010, 1, 1) payment2.save() payment3.create_time = datetime.date(2020, 1, 1) payment3.save() call_command('get_card_payments_states', '--from', '2009-01-01 00:00', '--to', '2017-01-02 00:00') self.assertQuerysetEqual(BankPayment.objects.values_list('identifier', 'state').order_by('identifier'), [('PAYMENT_1', PaymentState.INITIALIZED.value), ('PAYMENT_2', PaymentState.READY_TO_PROCESS.value), ('PAYMENT_3', PaymentState.INITIALIZED.value)], transform=tuple)
def parse(self, bank_statement) -> List[BankPayment]: account = BankAccount.objects.get(account_number='123456/7890') return [ get_payment(identifier='PAYMENT_1', account=account, variable_symbol='1234'), get_payment(identifier='PAYMENT_2', account=account, amount=Money('370.00', 'CZK')), ]
def test_card_payments_defferred(self): get_payment(identifier='PAYMENT_2', account=self.account, state=PaymentState.READY_TO_PROCESS, payment_type=PaymentType.CARD_PAYMENT, counter_account_number='', processor='dummy_error', transaction_date=date(2018, 5, 10)).save() get_payment(identifier='PAYMENT_3', account=self.account, state=PaymentState.READY_TO_PROCESS, payment_type=PaymentType.CARD_PAYMENT, counter_account_number='', processor='dummy', transaction_date=date(2018, 5, 11)).save() with override_settings(PAIN_PROCESS_PAYMENTS_LOCK_FILE=os.path.join( self.tempdir.path, 'test.lock')): call_command('process_payments') self.assertQuerysetEqual( BankPayment.objects.values_list('identifier', 'state', 'processor'), [('PAYMENT_1', PaymentState.PROCESSED, 'dummy'), ('PAYMENT_2', PaymentState.DEFERRED, 'dummy_error'), ('PAYMENT_3', PaymentState.PROCESSED, 'dummy')], transform=tuple, ordered=False) self.assertEqual(BankPayment.objects.first().objective, 'True objective') self.log_handler.check( ('django_pain.management.commands.process_payments', 'INFO', 'Command process_payments started.'), ('django_pain.management.commands.process_payments', 'INFO', 'Lock acquired.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing 3 unprocessed payments.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing card payments.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing card payments with processor dummy.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing card payments with processor dummy_error.'), ('django_pain.management.commands.process_payments', 'INFO', 'Saving payment %s as DEFERRED with error PaymentProcessingError.DUPLICITY.' % BankPayment.objects.get(identifier='PAYMENT_2').uuid), ('django_pain.management.commands.process_payments', 'INFO', 'Processing payments with processor dummy.'), ('django_pain.management.commands.process_payments', 'INFO', 'Marking 0 unprocessed payments as DEFERRED.'), ('django_pain.management.commands.process_payments', 'INFO', 'Command process_payments finished.'), )
def test_retrieve_exists(self): account = get_account(account_number='123456', currency='CZK') account.save() payment = get_payment(identifier='1', account=account, counter_account_number='', payment_type=PaymentType.CARD_PAYMENT, state=PaymentState.INITIALIZED, card_handler='csob') payment.save() result_mock = Mock() result_mock.payload = { 'paymentStatus': CSOB.PAYMENT_STATUS_CANCELLED, 'resultCode': CSOB.RETURN_CODE_OK } card_payment_hadler = get_card_payment_handler_instance( payment.card_handler) with patch.object(card_payment_hadler, '_client') as gateway_client_mock: gateway_client_mock.payment_status.return_value = result_mock response = self.client.get('/api/private/bankpayment/{}/'.format( payment.uuid)) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['state'], ExternalPaymentState.CANCELED)
def test_get_state(self): account = get_account(account_number='123456', currency='CZK') payment = get_payment(identifier='1', account=account, counter_account_name='Account one', state=PaymentState.PROCESSED) serializer = BankPaymentSerializer() self.assertEqual(serializer.get_state(payment), ExternalPaymentState.PAID)
def setUp(self): super().setUp() self.account = get_account() self.account.save() self.payment = get_payment(account=self.account, state=PaymentState.READY_TO_PROCESS) self.payment.save()
def test_credit_card_summary_payment(self): payment = get_payment( counter_account_number='None/None', constant_symbol='1176', ) with self.assertRaises(ValidationError): payment = skip_credit_card_transaction_summary(payment)
def setUp(self): self.account = BankAccount(account_number='123456/7890', currency='CZK') self.account.save() self.payment = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.IMPORTED) self.payment.save()
def parse(self, bank_statement) -> List[BankPayment]: account = BankAccount.objects.get(account_number='123456/7890') return [ get_payment(identifier='PAYMENT_3', account=account, counter_account_number='None/None', constant_symbol='1176'), ]
def test_format_payment(self): """Test format_payment.""" payment = get_payment(identifier='ID', create_time=datetime(2018, 1, 1, 12, 0, 0), amount=Money('42.00', 'CZK'), description='Memo...', counter_account_name='Acc') self.assertRegex( format_payment(payment), r'^ID\s+2018-01-01T12:00:00\s+amount:\s+42.00 Kč\s+account_memo: Memo...\s+account_name: Acc$' )
def test_processing_does_not_overwrite_locked_rows(self): get_payment(identifier='PAYMENT_1', transaction_date=datetime.date(2018, 5, 2), account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy').save() get_payment(identifier='PAYMENT_2', transaction_date=datetime.date(2018, 4, 1), account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy').save() processing_finished = threading.Event() query_finished = threading.Event() def target_processing(): query_finished.wait() try: call_command('get_card_payments_states') except Exception as e: # pragma: no cover self.errors.put(e) raise e finally: processing_finished.set() close_old_connections() def target_query(): try: with transaction.atomic(): BankPayment.objects.select_for_update().filter(identifier='PAYMENT_1').get() query_finished.set() processing_finished.wait() except Exception as e: # pragma: no cover self.errors.put(e) raise e finally: query_finished.set() close_old_connections() threads = [threading.Thread(target=target_processing), threading.Thread(target=target_query)] for t in threads: t.start() for t in threads: t.join() self.assertTrue(self.errors.empty()) self.assertQuerysetEqual(BankPayment.objects.values_list('identifier', 'state').order_by('identifier'), [('PAYMENT_1', PaymentState.INITIALIZED.value), ('PAYMENT_2', PaymentState.READY_TO_PROCESS.value)], transform=tuple)
def test_normal_run(self): payment = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy') payment.save() payment2 = get_payment(identifier='PAYMENT_2', account=self.account, state=PaymentState.READY_TO_PROCESS, card_handler='dummy') payment2.save() call_command('get_card_payments_states') self.log_handler.check( ('django_pain.management.commands.get_card_payments_states', 'INFO', 'Command get_card_payments_states started.'), ('django_pain.management.commands.get_card_payments_states', 'INFO', 'Getting state of 1 payment(s).'), ) self.assertQuerysetEqual(BankPayment.objects.values_list('identifier', 'state').order_by('identifier'), [('PAYMENT_1', PaymentState.READY_TO_PROCESS.value), ('PAYMENT_2', PaymentState.READY_TO_PROCESS.value)], transform=tuple)
def test_not_empty(self): account = BankAccount(account_number='123456789/0123', currency='CZK') account.save() payment1 = get_payment(identifier='PAYMENT_1', account=account) payment1.save() payment2 = get_payment(identifier='PAYMENT_2', account=account) payment2.save() result = self.client.get(reverse('payments-list')) self.assertQuerysetEqual(result.context['payments'].values_list( 'identifier', 'account', 'counter_account_number', 'transaction_date'), [ (payment1.identifier, payment1.account.pk, payment1.counter_account_number, payment1.transaction_date), (payment2.identifier, payment2.account.pk, payment2.counter_account_number, payment2.transaction_date), ], transform=tuple, ordered=False)
def test_format_payment_with_processing_error(self): """Test format_payment with processing_error.""" payment = get_payment(identifier='ID', create_time=datetime(2018, 1, 1, 12, 0, 0), amount=Money('42.00', 'CZK'), description='Memo...', counter_account_name='Acc', processing_error=PaymentProcessingError.DUPLICITY) self.assertRegex( format_payment(payment), r'^ID\s+2018-01-01T12:00:00\s+amount:\s+42.00 Kč\s+' r'account_memo: Memo...\s+account_name: Acc\s+processing_error: Duplicate payment$' )
def setUp(self): self.tempdir = TempDirectory() self.account_in = BankAccount(account_number='123456/7890', currency='CZK') self.account_ex = BankAccount(account_number='987654/3210', currency='CZK') self.account_in.save() self.account_ex.save() get_payment(identifier='PAYMENT_1', account=self.account_in, state=PaymentState.READY_TO_PROCESS).save() get_payment(identifier='PAYMENT_2', account=self.account_ex, state=PaymentState.READY_TO_PROCESS).save() self.log_handler = LogCapture( 'django_pain.management.commands.process_payments', propagate=False) # Exception in a threads does not fail the test - wee need to collect it somemehow self.errors = Queue() # type: Queue
def setUp(self): super().setUp() self.tempdir = TempDirectory() self.account = BankAccount(account_number='123456/7890', currency='CZK') self.account.save() payment = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.READY_TO_PROCESS) payment.save() self.log_handler = LogCapture( 'django_pain.management.commands.process_payments', propagate=False)
def test_error(self): payment = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy_exc') payment.save() call_command('get_card_payments_states') self.log_handler.check( ('django_pain.management.commands.get_card_payments_states', 'INFO', 'Command get_card_payments_states started.'), ('django_pain.management.commands.get_card_payments_states', 'INFO', 'Getting state of 1 payment(s).'), ('django_pain.management.commands.get_card_payments_states', 'ERROR', 'Error while updating state of payment identifier=PAYMENT_1') )
def setUp(self): self.request_factory = RequestFactory() self.admin = User.objects.create_superuser('admin', '*****@*****.**', 'password') account = BankAccount(account_number='123456/7890', currency='CZK') account.save() get_payment(identifier='PAYMENT_1', account=account, state=PaymentState.INITIALIZED).save() get_payment(identifier='PAYMENT_2', account=account, state=PaymentState.READY_TO_PROCESS).save() get_payment(identifier='PAYMENT_3', account=account, state=PaymentState.DEFERRED).save() get_payment(identifier='PAYMENT_4', account=account, state=PaymentState.PROCESSED).save() get_payment(identifier='PAYMENT_5', account=account, state=PaymentState.CANCELED).save()
def test_payment_admin_locking(self): account = get_account(account_name='My Account') account.save() instance = get_payment( identifier='My Payment 1', account=account, state=PaymentState.READY_TO_PROCESS, variable_symbol='VAR1', transaction_date=date(2019, 1, 1), ) viewname = 'admin:django_pain_bankpayment_changelist' find_in_response = 'VAR1' model_class = BankPayment self._test_admin_locking(instance, viewname, find_in_response, model_class)
def test_inclusion_in_payment_processing(self): """Test including accounts from payment processing""" account2 = BankAccount(account_number='987654/3210', currency='CZK') account2.save() get_payment(identifier='PAYMENT_2', account=self.account, state=PaymentState.READY_TO_PROCESS).save() get_payment(identifier='PAYMENT_3', account=account2, state=PaymentState.READY_TO_PROCESS).save() with override_settings(PAIN_PROCESS_PAYMENTS_LOCK_FILE=os.path.join( self.tempdir.path, 'test.lock')): out = StringIO() err = StringIO() call_command('process_payments', '--include-accounts', '123456/7890', stdout=out, stderr=err) self.assertEqual(out.getvalue(), '') self.assertEqual(err.getvalue(), '') self.log_handler.check( ('django_pain.management.commands.process_payments', 'INFO', 'Command process_payments started.'), ('django_pain.management.commands.process_payments', 'INFO', 'Lock acquired.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing 2 unprocessed payments.'), ('django_pain.management.commands.process_payments', 'INFO', 'Processing payments with processor dummy.'), ('django_pain.management.commands.process_payments', 'INFO', 'Marking 0 unprocessed payments as DEFERRED.'), ('django_pain.management.commands.process_payments', 'INFO', 'Command process_payments finished.'), )
def test_update_payment_state_connection_error(self): account = get_account(account_number='123456', currency='CZK') account.save() payment = get_payment(identifier='1', account=account, counter_account_number='', payment_type=PaymentType.CARD_PAYMENT, state=PaymentState.INITIALIZED, card_handler='csob') payment.save() handler = CSOBCardPaymentHandler('csob') with patch.object(handler, '_client') as gateway_client_mock: gateway_client_mock.payment_status.side_effect = requests.ConnectionError( ) self.assertRaises(PaymentHandlerConnectionError, handler.update_payments_state, payment)
def test_update_payment_state_not_ok(self): account = get_account(account_number='123456', currency='CZK') account.save() payment = get_payment(identifier='1', account=account, counter_account_number='', payment_type=PaymentType.CARD_PAYMENT, state=PaymentState.INITIALIZED, card_handler='csob') payment.save() result_mock = Mock() result_mock.payload = {'resultCode': CSOB.RETURN_CODE_MERCHANT_BLOCKED} handler = CSOBCardPaymentHandler('csob') with patch.object(handler, '_client') as gateway_client_mock: gateway_client_mock.payment_status.return_value = result_mock self.assertRaisesRegex(PaymentHandlerError, 'payment_status resultCode != OK', handler.update_payments_state, payment) self.assertEqual(payment.state, PaymentState.INITIALIZED)
def test_retrieve_gateway_connection_error(self): account = get_account(account_number='123456', currency='CZK') account.save() payment = get_payment(identifier='1', account=account, counter_account_number='', payment_type=PaymentType.CARD_PAYMENT, state=PaymentState.READY_TO_PROCESS, card_handler='csob') payment.save() card_payment_hadler = get_card_payment_handler_instance( payment.card_handler) with patch.object(card_payment_hadler, '_client') as gateway_client_mock: gateway_client_mock.payment_status.side_effect = PaymentHandlerConnectionError( ) response = self.client.get('/api/private/bankpayment/{}/'.format( payment.uuid)) self.assertEqual(response.status_code, 503)
def test_update_payment_state_no_update_not_initialized(self): account = get_account(account_number='123456', currency='CZK') account.save() payment = get_payment(identifier='1', account=account, counter_account_number='', payment_type=PaymentType.CARD_PAYMENT, state=PaymentState.PROCESSED, card_handler='csob') payment.save() result_mock = Mock() result_mock.payload = { 'paymentStatus': CSOB.PAYMENT_STATUS_CANCELLED, 'resultCode': CSOB.RETURN_CODE_OK } handler = CSOBCardPaymentHandler('csob') with patch.object(handler, '_client') as gateway_client_mock: gateway_client_mock.payment_status.return_value = result_mock handler.update_payments_state(payment) self.assertEqual(payment.state, PaymentState.PROCESSED)
def setUp(self): super().setUp() self.payment = get_payment()
def setUpTestData(cls): account1 = get_account(account_number='123456', currency='CZK') account1.save() account2 = get_account(account_number='654321', currency='CZK') account2.save() get_payment(identifier='1', account=account1, counter_account_name='Account one', state=PaymentState.READY_TO_PROCESS).save() get_payment(identifier='2', account=account2, counter_account_name='Account two', state=PaymentState.READY_TO_PROCESS).save() get_payment(identifier='3', account=account1, counter_account_name='Account three', state=PaymentState.PROCESSED).save() get_payment(identifier='4', account=account2, counter_account_name='Account four', description='I am your father!', state=PaymentState.PROCESSED).save() get_payment(identifier='5', account=account1, counter_account_name='Account five', state=PaymentState.DEFERRED).save() get_payment(identifier='6', account=account1, counter_account_name='Account six', description='May the force be with you', state=PaymentState.DEFERRED).save() get_payment(identifier='7', account=account2, counter_account_name='Account seven', state=PaymentState.DEFERRED).save()
def test_processed_rows_not_overwritten(self): processing_started = threading.Event() query_finished = threading.Event() p1 = get_payment(identifier='PAYMENT_1', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy') p2 = get_payment(identifier='PAYMENT_2', account=self.account, state=PaymentState.INITIALIZED, card_handler='dummy') p1.save() p2.save() p1.create_time = datetime.datetime(2018, 5, 2) p2.create_time = datetime.datetime(2018, 4, 1) p1.save() p2.save() def mock_update_state(payment): processing_started.set() query_finished.wait() payment.state = PaymentState.READY_TO_PROCESS payment.save() def target_processing(): try: # cache may prevent mocking get_card_payment_handler_instance.cache_clear() get_card_payment_handler_class.cache_clear() with patch('django_pain.tests.utils.DummyCardPaymentHandler') as MockClass: instance = MockClass.return_value instance.update_payments_state = mock_update_state call_command('get_card_payments_states', '--from', datetime.datetime(2018, 5, 1)) except Exception as e: # pragma: no cover self.errors.put(e) raise e finally: processing_started.set() close_old_connections() # mock might be cached get_card_payment_handler_instance.cache_clear() get_card_payment_handler_class.cache_clear() def target_query(): processing_started.wait() try: with transaction.atomic(): payments = BankPayment.objects.select_for_update(skip_locked=True).all() for p in payments: p.state = PaymentState.PROCESSED p.save() except Exception as e: # pragma: no cover self.errors.put(e) raise e finally: query_finished.set() close_old_connections() threads = [threading.Thread(target=target_processing), threading.Thread(target=target_query)] for t in threads: t.start() for t in threads: t.join() self.assertTrue(self.errors.empty()) self.assertQuerysetEqual(BankPayment.objects.values_list('identifier', 'state').order_by('identifier'), [('PAYMENT_1', PaymentState.READY_TO_PROCESS.value), ('PAYMENT_2', PaymentState.PROCESSED.value)], transform=tuple)
def test_normal_payment(self): payment = get_payment() payment = skip_credit_card_transaction_summary(payment) self.assertEqual(payment.state, PaymentState.READY_TO_PROCESS)
def setUp(self): self.processor = IgnorePaymentProcessor() self.payment = get_payment()