From 6e776cbe64b47e98fd97b008c46ae7ca3cbc613e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Fri, 6 Mar 2026 10:25:37 +0100 Subject: [PATCH] [IMP] partner_phone_country_validation: add tests --- .../tests/__init__.py | 4 + .../test_partner_phone_country_validation.py | 149 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 partner_phone_country_validation/tests/__init__.py create mode 100644 partner_phone_country_validation/tests/test_partner_phone_country_validation.py diff --git a/partner_phone_country_validation/tests/__init__.py b/partner_phone_country_validation/tests/__init__.py new file mode 100644 index 0000000..17299eb --- /dev/null +++ b/partner_phone_country_validation/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024 Elabore (https://elabore.coop) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_partner_phone_country_validation diff --git a/partner_phone_country_validation/tests/test_partner_phone_country_validation.py b/partner_phone_country_validation/tests/test_partner_phone_country_validation.py new file mode 100644 index 0000000..aa61ebe --- /dev/null +++ b/partner_phone_country_validation/tests/test_partner_phone_country_validation.py @@ -0,0 +1,149 @@ +# Copyright 2024 Elabore (https://elabore.coop) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.exceptions import ValidationError +from odoo.tests import Form, TransactionCase + + +class TestPartnerPhoneCountryValidation(TransactionCase): + """Tests for partner phone country validation module. + + Note on testing @api.onchange methods: + -------------------------------------- + In Odoo, @api.onchange methods are only triggered by the web UI, not by + direct field assignment in Python code. + + - `partner.phone = "xxx"` → triggers write() → validates constraints + BEFORE any onchange can run → fails if constraint not met + + - `Form(partner)` → simulates web UI behavior → triggers onchange methods + when field values change → onchange can set country BEFORE save + + We use Form() for tests that need onchange behavior (auto-detection of + country from phone prefix), and direct create/write for constraint tests. + """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.country_fr = cls.env.ref("base.fr") + cls.country_be = cls.env.ref("base.be") + # Set company country to France for default formatting + cls.env.company.country_id = cls.country_fr + + def test_normalize_00_to_plus(self): + """Test conversion of 00xx format to +xx format.""" + partner = self.env["res.partner"].create( + {"name": "Test Partner", "country_id": self.country_fr.id} + ) + # 00 → + conversion + self.assertEqual( + partner._normalize_phone_number("0033612345678"), "+33612345678" + ) + # + numbers unchanged + self.assertEqual( + partner._normalize_phone_number("+33612345678"), "+33612345678" + ) + # National numbers unchanged + self.assertEqual(partner._normalize_phone_number("0612345678"), "0612345678") + # Edge cases + self.assertIsNone(partner._normalize_phone_number(None)) + self.assertEqual(partner._normalize_phone_number(""), "") + + def test_auto_country_detection(self): + """Test automatic country detection from phone prefix.""" + partner = self.env["res.partner"].create({"name": "Test Partner"}) + self.assertFalse(partner.country_id) + + partner._set_country_from_phone_number("+33612345678") + self.assertEqual(partner.country_id, self.country_fr) + + def test_auto_country_detection_does_not_override(self): + """Test that auto-detection does not override existing country.""" + partner = self.env["res.partner"].create( + {"name": "Test Partner", "country_id": self.country_be.id} + ) + partner._set_country_from_phone_number("+33612345678") + # Country should remain Belgium + self.assertEqual(partner.country_id, self.country_be) + + def test_onchange_phone_full_flow(self): + """Test complete onchange flow: 00→+ normalization, country detection, formatting. + + Use Form to simulate UI behavior where onchange triggers before save. + """ + with Form(self.env["res.partner"]) as partner_form: + partner_form.name = "Test Partner" + partner_form.phone = "0033612345678" + # Onchange should have normalized and detected country + self.assertIn("+33", partner_form.phone) + self.assertEqual(partner_form.country_id, self.country_fr) + + def test_onchange_mobile_full_flow(self): + """Test complete onchange flow for mobile field.""" + with Form(self.env["res.partner"]) as partner_form: + partner_form.name = "Test Partner" + partner_form.mobile = "0032475123456" + # Onchange should have normalized and detected country + self.assertIn("+32", partner_form.mobile) + self.assertEqual(partner_form.country_id, self.country_be) + + def test_onchange_with_existing_country(self): + """Test formatting with existing country uses that country.""" + with Form(self.env["res.partner"]) as partner_form: + partner_form.name = "Test Partner" + partner_form.country_id = self.country_fr + partner_form.phone = "06 12 34 56 78" + # Should be formatted with French prefix + self.assertIn("+33", partner_form.phone) + + def test_constraint_country_required_with_phone(self): + """Test that country is required when phone/mobile is set.""" + with self.assertRaises(ValidationError): + self.env["res.partner"].create( + {"name": "Test Partner", "phone": "0612345678"} + ) + + with self.assertRaises(ValidationError): + self.env["res.partner"].create( + {"name": "Test Partner", "mobile": "0612345678"} + ) + + def test_constraint_passes_without_phone(self): + """Test that partner without phone doesn't require country.""" + partner = self.env["res.partner"].create({"name": "Test Partner"}) + self.assertTrue(partner.id) + self.assertFalse(partner.country_id) + + +class TestGetCountryFromPhoneNumber(TransactionCase): + """Test the utility function for country detection.""" + + def test_valid_international_number(self): + """Test detection from valid international number.""" + from odoo.addons.partner_phone_country_validation.tools.get_country_from_phone_number import ( + get_country_from_phone_number, + ) + + self.assertEqual(get_country_from_phone_number("+33612345678"), "FR") + + def test_invalid_number_returns_false(self): + """Test that invalid numbers return False.""" + from odoo.addons.partner_phone_country_validation.tools.get_country_from_phone_number import ( + get_country_from_phone_number, + ) + + self.assertFalse(get_country_from_phone_number("invalid")) + self.assertFalse(get_country_from_phone_number("0612345678")) + + def test_national_number_without_prefix(self): + """Test handling of national number without international prefix.""" + from odoo.addons.partner_phone_country_validation.tools.get_country_from_phone_number import ( + get_country_from_phone_number, + ) + + # National numbers without + cannot be reliably detected + result = get_country_from_phone_number("0612345678") + # phonenumbers cannot determine country without prefix + self.assertFalse(result)