From 5cea9dddcc51b729ba9e2fd2ca029419d58735fe Mon Sep 17 00:00:00 2001 From: "Antoine Vandevenne (anv)" Date: Wed, 17 Jan 2024 17:55:07 +0100 Subject: [PATCH] [IMP] payment: improve test coverage Some logic of the `payment.provider` and `provider.method` models was not properly covered by unit tests. closes odoo/odoo#149833 Related: odoo/enterprise#54930 Signed-off-by: Antoine Vandevenne (anv) --- addons/payment/tests/__init__.py | 1 + addons/payment/tests/common.py | 5 +- addons/payment/tests/test_payment_method.py | 144 ++++++++++++++++++ addons/payment/tests/test_payment_provider.py | 131 +++++++++++++++- 4 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 addons/payment/tests/test_payment_method.py diff --git a/addons/payment/tests/__init__.py b/addons/payment/tests/__init__.py index 04f8aa0a76c..0da30228f1d 100644 --- a/addons/payment/tests/__init__.py +++ b/addons/payment/tests/__init__.py @@ -4,6 +4,7 @@ from . import common from . import http_common from . import test_flows from . import test_multicompany_flows +from . import test_payment_method from . import test_payment_provider from . import test_payment_token from . import test_payment_transaction diff --git a/addons/payment/tests/common.py b/addons/payment/tests/common.py index 222c4c9cb43..9626aaef95b 100644 --- a/addons/payment/tests/common.py +++ b/addons/payment/tests/common.py @@ -94,8 +94,9 @@ class PaymentCommon(BaseCommon): cls.provider = cls.dummy_provider cls.payment_methods = cls.provider.payment_method_ids - cls.payment_method_id = cls.provider.payment_method_ids[:1].id - cls.payment_method_code = cls.provider.payment_method_ids[:1].code + cls.payment_method = cls.payment_methods[:1] + cls.payment_method_id = cls.payment_method.id + cls.payment_method_code = cls.payment_method.code cls.amount = 1111.11 cls.company = cls.env.company cls.company_id = cls.company.id diff --git a/addons/payment/tests/test_payment_method.py b/addons/payment/tests/test_payment_method.py new file mode 100644 index 00000000000..da25241eaf5 --- /dev/null +++ b/addons/payment/tests/test_payment_method.py @@ -0,0 +1,144 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.exceptions import UserError +from odoo.models import Command +from odoo.tests import tagged + +from odoo.addons.payment.tests.common import PaymentCommon + + +@tagged('-at_install', 'post_install') +class TestPaymentMethod(PaymentCommon): + + def test_unlinking_payment_method_from_provider_state_archives_tokens(self): + """ Test that the active tokens of a payment method created through a provider are archived + when the method is unlinked from the provider. """ + token = self._create_token() + self.payment_method.provider_ids = [Command.unlink(self.payment_method.provider_ids[:1].id)] + self.assertFalse(token.active) + + def test_payment_method_requires_provider_to_be_activated(self): + """ Test that activating a payment method that is not linked to an enabled provider is + forbidden. """ + self.provider.state = 'disabled' + with self.assertRaises(UserError): + self.payment_methods.active = True + + def test_payment_method_compatible_when_provider_is_enabled(self): + """ Test that a payment method is available when it is supported by an enabled provider. """ + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_not_compatible_when_provider_is_disabled(self): + """ Test that a payment method is not available when there is no enabled provider that + supports it. """ + self.provider.state = 'disabled' + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertNotIn(self.payment_method, compatible_payment_methods) + + def test_non_primary_payment_method_not_compatible(self): + """ Test that a "brand" (i.e., non-primary) payment method is never available. """ + brand_payment_method = self.payment_method.copy() + brand_payment_method.primary_payment_method_id = self.payment_method_id # Make it a brand. + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertNotIn(brand_payment_method, compatible_payment_methods) + + def test_payment_method_compatible_with_supported_countries(self): + """ Test that the payment method is compatible with its supported countries. """ + belgium = self.env.ref('base.be') + self.payment_method.supported_country_ids = [Command.set([belgium.id])] + self.partner.country_id = belgium + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_not_compatible_with_unsupported_countries(self): + """ Test that the payment method is not compatible with a country that is not supported. """ + belgium = self.env.ref('base.be') + self.payment_method.supported_country_ids = [Command.set([belgium.id])] + france = self.env.ref('base.fr') + self.partner.country_id = france + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertNotIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_compatible_when_no_supported_countries_set(self): + """ Test that the payment method is always compatible when no supported countries are + set. """ + self.payment_method.supported_country_ids = [Command.clear()] + belgium = self.env.ref('base.be') + self.partner.country_id = belgium + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_compatible_with_supported_currencies(self): + """ Test that the payment method is compatible with its supported currencies. """ + self.payment_method.supported_currency_ids = [Command.set([self.currency_euro.id])] + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, currency_id=self.currency_euro.id + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_not_compatible_with_unsupported_currencies(self): + """ Test that the payment method is not compatible with a currency that is not + supported. """ + self.payment_method.supported_currency_ids = [Command.set([self.currency_euro.id])] + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, currency_id=self.currency_usd.id + ) + self.assertNotIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_compatible_when_no_supported_currencies_set(self): + """ Test that the payment method is always compatible when no supported currencies are + set. """ + self.payment_method.supported_currency_ids = [Command.clear()] + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, currency_id=self.currency_euro.id + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_compatible_when_tokenization_forced(self): + """ Test that the payment method is compatible when it supports tokenization while it is + forced by the calling module. """ + self.payment_method.support_tokenization = True + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, force_tokenization=True + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_not_compatible_when_tokenization_forced(self): + """ Test that the payment method is not compatible when it does not support tokenization + while it is forced by the calling module. """ + self.payment_method.support_tokenization = False + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, force_tokenization=True + ) + self.assertNotIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_compatible_with_express_checkout(self): + """ Test that the payment method is compatible when it supports express checkout while it is + an express checkout flow. """ + self.payment_method.support_express_checkout = True + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, is_express_checkout=True + ) + self.assertIn(self.payment_method, compatible_payment_methods) + + def test_payment_method_not_compatible_with_express_checkout(self): + """ Test that the payment method is not compatible when it does not support express checkout + while it is an express checkout flow. """ + self.payment_method.support_express_checkout = False + compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods( + self.provider.ids, self.partner.id, is_express_checkout=True + ) + self.assertNotIn(self.payment_method, compatible_payment_methods) diff --git a/addons/payment/tests/test_payment_provider.py b/addons/payment/tests/test_payment_provider.py index 16f31fc38e2..7ab08417bf5 100644 --- a/addons/payment/tests/test_payment_provider.py +++ b/addons/payment/tests/test_payment_provider.py @@ -11,6 +11,42 @@ from odoo.addons.payment.tests.common import PaymentCommon @tagged('-at_install', 'post_install') class TestPaymentProvider(PaymentCommon): + def test_changing_provider_state_archives_tokens(self): + """ Test that all active tokens of a provider are archived when its state is changed. """ + for old_state in ('enabled', 'test'): # No need to check when the provided was disabled. + for new_state in ('enabled', 'test', 'disabled'): + if old_state != new_state: # No need to check when the state is unchanged. + self.provider.state = old_state + token = self._create_token() + self.provider.state = new_state + self.assertFalse(token.active) + + def test_enabling_provider_activates_default_payment_methods(self): + """ Test that the default payment methods of a provider are activated when it is + enabled. """ + self.payment_methods.active = False + for new_state in ('enabled', 'test'): + self.provider.state = 'disabled' + with patch( + 'odoo.addons.payment.models.payment_provider.PaymentProvider' + '._get_default_payment_method_codes', return_value=self.payment_method_code, + ): + self.provider.state = new_state + self.assertTrue(self.payment_methods.active) + + def test_disabling_provider_deactivates_default_payment_methods(self): + """ Test that the default payment methods of a provider are deactivated when it is + disabled. """ + self.payment_methods.active = True + for old_state in ('enabled', 'test'): + self.provider.state = old_state + with patch( + 'odoo.addons.payment.models.payment_provider.PaymentProvider' + '._get_default_payment_method_codes', return_value=self.payment_method_code, + ): + self.provider.state = 'disabled' + self.assertFalse(self.payment_methods.active) + def test_published_provider_compatible_with_all_users(self): """ Test that a published provider is always available to all users. """ for user in (self.public_user, self.portal_user): @@ -41,6 +77,37 @@ class TestPaymentProvider(PaymentCommon): ) self.assertNotIn(self.provider, compatible_providers) + def test_provider_compatible_with_available_countries(self): + """ Test that the provider is compatible with its available countries. """ + belgium = self.env.ref('base.be') + self.provider.available_country_ids = [Command.set([belgium.id])] + self.partner.country_id = belgium + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount + ) + self.assertIn(self.provider, compatible_providers) + + def test_provider_not_compatible_with_unavailable_countries(self): + """ Test that the provider is not compatible with a country that is not available. """ + belgium = self.env.ref('base.be') + self.provider.available_country_ids = [Command.set([belgium.id])] + france = self.env.ref('base.fr') + self.partner.country_id = france + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount + ) + self.assertNotIn(self.provider, compatible_providers) + + def test_provider_compatible_when_no_available_countries_set(self): + """ Test that the provider is always compatible when no available countries are set. """ + self.provider.available_country_ids = [Command.clear()] + belgium = self.env.ref('base.be') + self.partner.country_id = belgium + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount + ) + self.assertIn(self.provider, compatible_providers) + def test_provider_compatible_when_maximum_amount_is_zero(self): """ Test that the maximum amount has no effect on the provider's compatibility when it is set to 0. """ @@ -90,10 +157,72 @@ class TestPaymentProvider(PaymentCommon): ) self.assertNotIn(self.provider, compatible_providers) - def test_provider_compatible_when_no_available_currency_set(self): + def test_provider_compatible_when_no_available_currencies_set(self): """ Test that the provider is always compatible when no available currency is set. """ self.provider.available_currency_ids = [Command.clear()] compatible_providers = self.provider._get_compatible_providers( self.company.id, self.partner.id, self.amount, currency_id=self.currency_euro.id ) self.assertIn(self.provider, compatible_providers) + + def test_provider_compatible_when_tokenization_forced(self): + """ Test that the provider is compatible when it allows tokenization while it is forced by + the calling module. """ + self.provider.allow_tokenization = True + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, force_tokenization=True + ) + self.assertIn(self.provider, compatible_providers) + + def test_provider_not_compatible_when_tokenization_forced(self): + """ Test that the provider is not compatible when it does not allow tokenization while it + is forced by the calling module. """ + self.provider.allow_tokenization = False + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, force_tokenization=True + ) + self.assertNotIn(self.provider, compatible_providers) + + def test_provider_compatible_when_tokenization_required(self): + """ Test that the provider is compatible when it allows tokenization while it is required by + the payment context (e.g., when paying for a subscription). """ + self.provider.allow_tokenization = True + with patch( + 'odoo.addons.payment.models.payment_provider.PaymentProvider._is_tokenization_required', + return_value=True, + ): + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount + ) + self.assertIn(self.provider, compatible_providers) + + def test_provider_not_compatible_when_tokenization_required(self): + """ Test that the provider is not compatible when it does not allow tokenization while it + is required by the payment context (e.g., when paying for a subscription). """ + self.provider.allow_tokenization = False + with patch( + 'odoo.addons.payment.models.payment_provider.PaymentProvider._is_tokenization_required', + return_value=True, + ): + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount + ) + self.assertNotIn(self.provider, compatible_providers) + + def test_provider_compatible_with_express_checkout(self): + """ Test that the provider is compatible when it allows express checkout while it is an + express checkout flow. """ + self.provider.allow_express_checkout = True + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, is_express_checkout=True + ) + self.assertIn(self.provider, compatible_providers) + + def test_provider_not_compatible_with_express_checkout(self): + """ Test that the provider is not compatible when it does not allow express checkout while + it is an express checkout flow. """ + self.provider.allow_express_checkout = False + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, is_express_checkout=True + ) + self.assertNotIn(self.provider, compatible_providers)