From 0cda9d172711caabc44738bf0fb4cdd793c904ec Mon Sep 17 00:00:00 2001 From: "Ronald Portier (Therp BV)" Date: Wed, 14 Sep 2022 16:21:37 +0200 Subject: [PATCH] [IMP] *_online_ponto: test ponto_interface and buffer purge. --- .../online_bank_statement_provider_ponto.py | 5 +- .../tests/__init__.py | 1 + ...t_account_statement_import_online_ponto.py | 169 ++++++++++-------- .../tests/test_ponto_interface.py | 124 +++++++++++++ 4 files changed, 223 insertions(+), 76 deletions(-) create mode 100644 account_bank_statement_import_online_ponto/tests/test_ponto_interface.py diff --git a/account_bank_statement_import_online_ponto/models/online_bank_statement_provider_ponto.py b/account_bank_statement_import_online_ponto/models/online_bank_statement_provider_ponto.py index 1741336..355de12 100644 --- a/account_bank_statement_import_online_ponto/models/online_bank_statement_provider_ponto.py +++ b/account_bank_statement_import_online_ponto/models/online_bank_statement_provider_ponto.py @@ -63,11 +63,10 @@ class OnlineBankStatementProviderPonto(models.Model): access_data, latest_identifier ) - self.ponto_last_identifier = latest_identifier def _obtain_statement_data(self, date_since, date_until): self.ensure_one() - if self.service != "ponto": + if self.service != "ponto": # pragma: no cover return super()._obtain_statement_data( date_since, date_until, @@ -159,6 +158,8 @@ class OnlineBankStatementProviderPonto(models.Model): ("active", "=", True), ]) for provider in providers: + if provider.service != "ponto": + continue if not provider.ponto_buffer_retain_days: continue cutoff_date = today - relativedelta(days=provider.ponto_buffer_retain_days) diff --git a/account_bank_statement_import_online_ponto/tests/__init__.py b/account_bank_statement_import_online_ponto/tests/__init__.py index d1f2895..7f08307 100644 --- a/account_bank_statement_import_online_ponto/tests/__init__.py +++ b/account_bank_statement_import_online_ponto/tests/__init__.py @@ -1,3 +1,4 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import test_ponto_interface from . import test_account_statement_import_online_ponto diff --git a/account_bank_statement_import_online_ponto/tests/test_account_statement_import_online_ponto.py b/account_bank_statement_import_online_ponto/tests/test_account_statement_import_online_ponto.py index 3cebf21..d27893c 100644 --- a/account_bank_statement_import_online_ponto/tests/test_account_statement_import_online_ponto.py +++ b/account_bank_statement_import_online_ponto/tests/test_account_statement_import_online_ponto.py @@ -15,6 +15,83 @@ _interface_class = ( + ".PontoInterface" ) +THREE_TRANSACTIONS = [ + { + "type": "transaction", + "relationships": { + "account": { + "links": {"related": "https://api.myponto.com/accounts/"}, + "data": { + "type": "account", + "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", + }, + } + }, + "id": "701ab965-21c4-46ca-b157-306c0646e0e2", + "attributes": { + "valueDate": "2019-11-18T00:00:00.000Z", + "remittanceInformationType": "unstructured", + "remittanceInformation": "Minima vitae totam!", + "executionDate": "2019-11-20T00:00:00.000Z", + "description": "Wire transfer", + "currency": "EUR", + "counterpartReference": "BE26089479973169", + "counterpartName": "Osinski Group", + "amount": 6.08, + }, + }, + { + "type": "transaction", + "relationships": { + "account": { + "links": {"related": "https://api.myponto.com/accounts/"}, + "data": { + "type": "account", + "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", + }, + } + }, + "id": "9ac50483-16dc-4a82-aa60-df56077405cd", + "attributes": { + "valueDate": "2019-11-04T00:00:00.000Z", + "remittanceInformationType": "unstructured", + "remittanceInformation": "Quia voluptatem blanditiis.", + "executionDate": "2019-11-06T00:00:00.000Z", + "description": "Wire transfer", + "currency": "EUR", + "counterpartReference": "BE97201830401438", + "counterpartName": "Stokes-Miller", + "amount": 5.48, + }, + }, + { + "type": "transaction", + "relationships": { + "account": { + "links": {"related": "https://api.myponto.com/accounts/"}, + "data": { + "type": "account", + "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", + }, + } + }, + "id": "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff", + "attributes": { + "valueDate": "2019-11-04T00:00:00.000Z", + "remittanceInformationType": "unstructured", + "remittanceInformation": "Laboriosam repelo?", + "executionDate": "2019-11-04T00:00:00.000Z", + "description": "Wire transfer", + "currency": "EUR", + "counterpartReference": "BE10325927501996", + "counterpartName": "Strosin-Veum", + "amount": 5.83, + }, + }, +] + +EMPTY_TRANSACTIONS = [] + class TestBankAccountStatementImportOnlinePonto(common.TransactionCase): post_install = True @@ -80,80 +157,7 @@ class TestBankAccountStatementImportOnlinePonto(common.TransactionCase): # return list of transactions on first call, empty list on second call. self.mock_get_transactions = lambda: mock.patch( _interface_class + "._get_transactions", - side_effect=[[ - { - "type": "transaction", - "relationships": { - "account": { - "links": {"related": "https://api.myponto.com/accounts/"}, - "data": { - "type": "account", - "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", - }, - } - }, - "id": "701ab965-21c4-46ca-b157-306c0646e0e2", - "attributes": { - "valueDate": "2019-11-18T00:00:00.000Z", - "remittanceInformationType": "unstructured", - "remittanceInformation": "Minima vitae totam!", - "executionDate": "2019-11-20T00:00:00.000Z", - "description": "Wire transfer", - "currency": "EUR", - "counterpartReference": "BE26089479973169", - "counterpartName": "Osinski Group", - "amount": 6.08, - }, - }, - { - "type": "transaction", - "relationships": { - "account": { - "links": {"related": "https://api.myponto.com/accounts/"}, - "data": { - "type": "account", - "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", - }, - } - }, - "id": "9ac50483-16dc-4a82-aa60-df56077405cd", - "attributes": { - "valueDate": "2019-11-04T00:00:00.000Z", - "remittanceInformationType": "unstructured", - "remittanceInformation": "Quia voluptatem blanditiis.", - "executionDate": "2019-11-06T00:00:00.000Z", - "description": "Wire transfer", - "currency": "EUR", - "counterpartReference": "BE97201830401438", - "counterpartName": "Stokes-Miller", - "amount": 5.48, - }, - }, - { - "type": "transaction", - "relationships": { - "account": { - "links": {"related": "https://api.myponto.com/accounts/"}, - "data": { - "type": "account", - "id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75", - }, - } - }, - "id": "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff", - "attributes": { - "valueDate": "2019-11-04T00:00:00.000Z", - "remittanceInformationType": "unstructured", - "remittanceInformation": "Laboriosam repelo?", - "executionDate": "2019-11-04T00:00:00.000Z", - "description": "Wire transfer", - "currency": "EUR", - "counterpartReference": "BE10325927501996", - "counterpartName": "Strosin-Veum", - "amount": 5.83, - }, - }, - ], [], ] + side_effect=[THREE_TRANSACTIONS, EMPTY_TRANSACTIONS, ] ) def test_balance_start(self): @@ -222,3 +226,20 @@ class TestBankAccountStatementImportOnlinePonto(common.TransactionCase): self.assertEqual(statement.balance_end, 17.39) # Ponto does not give balance info in transactions. # self.assertEqual(statement.balance_end_real, 17.39) + + def test_ponto_buffer_purge(self): + """Create some old buffer records and test purging them.""" + self.provider.write( + { + "ponto_buffer_retain_days": 31, + "active": True, + } + ) + buffer_model = self.env["ponto.buffer"] + buffer_model.sudo()._store_transactions(self.provider, THREE_TRANSACTIONS) + buffers = buffer_model.search([("provider_id", "=", self.provider.id)]) + # As all transactions have a different date, they will be in separate buffers. + self.assertEqual(len(buffers), 3) + self.provider._ponto_buffer_purge() + buffers = buffer_model.search([("provider_id", "=", self.provider.id)]) + self.assertFalse(bool(buffers)) diff --git a/account_bank_statement_import_online_ponto/tests/test_ponto_interface.py b/account_bank_statement_import_online_ponto/tests/test_ponto_interface.py new file mode 100644 index 0000000..17bcf66 --- /dev/null +++ b/account_bank_statement_import_online_ponto/tests/test_ponto_interface.py @@ -0,0 +1,124 @@ +# Copyright 2022 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from dateutil.relativedelta import relativedelta +import json + +from unittest.mock import MagicMock, patch + +from odoo import fields +from odoo.tests import common + +from .test_account_statement_import_online_ponto import THREE_TRANSACTIONS + + +class TestPontoInterface(common.TransactionCase): + post_install = True + + @patch("requests.post") + def test_login(self, requests_post): + """Check Ponto API login.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = json.dumps( + { + "access_token": "live_the_token", + "expires_in": 1799, + "scope": "ai", + "token_type": "bearer", + } + ) + requests_post.return_value = mock_response + interface_model = self.env["ponto.interface"] + access_data = interface_model._login("uncle_john", "secret") + self.assertEqual(access_data["access_token"], "live_the_token") + self.assertIn("token_expiration", access_data) + + @patch("requests.get") + def test_set_access_account(self, requests_get): + """Test getting account data for Ponto access.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = json.dumps( + { + "data": [ + { + "id": "wrong_id", + "attributes": { + "reference": "NL66ABNA123456789", + }, + }, + { + "id": "2ad3df83-be01-47cf-a6be-cf0de5cb4c99", + "attributes": { + "reference": "NL66RABO123456789", + }, + }, + ], + } + ) + requests_get.return_value = mock_response + # Start of actual test. + access_data = self._get_access_dict(include_account=False) + interface_model = self.env["ponto.interface"] + interface_model._set_access_account(access_data, "NL66RABO123456789") + self.assertIn("ponto_account", access_data) + self.assertEqual( + access_data["ponto_account"], + "2ad3df83-be01-47cf-a6be-cf0de5cb4c99" + ) + + @patch("requests.post") + def test_ponto_synchronisation(self, requests_post): + """Test requesting Ponto synchronization.""" + mock_response = MagicMock() + mock_response.status_code = 400 + mock_response.text = json.dumps( + { + "errors": [ + { + "code": "accountRecentlySynchronized", + "detail": + "This type of synchronization was already created recently" + " for the account. Try again later or on the Dashboard.", + "meta": {} + } + ] + } + ) + requests_post.return_value = mock_response + # Start of actual test (succeeds if no Exceptions occur). + access_data = self._get_access_dict() + interface_model = self.env["ponto.interface"] + interface_model._ponto_synchronisation(access_data) + + @patch("requests.get") + def test_get_transactions(self, requests_get): + """Test getting transactions from Ponto.""" + mock_response = MagicMock() + mock_response.status_code = 200 + # Key "data" will contain a list of transactions. + mock_response.text = json.dumps({"data": THREE_TRANSACTIONS}) + requests_get.return_value = mock_response + # Start of actual test. + access_data = self._get_access_dict() + interface_model = self.env["ponto.interface"] + transactions = interface_model._get_transactions(access_data, False) + self.assertEqual(len(transactions), 3) + self.assertEqual(transactions[2]["id"], "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff") + self.assertEqual( + transactions[2]["attributes"]["counterpartReference"], + "BE10325927501996" + ) + + def _get_access_dict(self, include_account=True): + """Get access dict that caches login/account information.""" + token_expiration = fields.Datetime.now() + relativedelta(seconds=1800) + access_data = { + "username": "uncle_john", + "password": "secret", + "access_token": "live_the_token", + "token_expiration": token_expiration, + } + if include_account: + access_data["ponto_account"] = "2ad3df83-be01-47cf-a6be-cf0de5cb4c99" + return access_data