|
| 1 | +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. |
| 2 | +"""Tests for relational fields (one2many, many2many) used as bare predicates. |
| 3 | +
|
| 4 | +When a user writes a CEL expression like `program_membership_ids` (without |
| 5 | +a comparison operator), the translator should treat it as a truthiness |
| 6 | +check: "has records" → True, "no records" → False. This matches Python |
| 7 | +and CEL semantics where non-empty collections are truthy. |
| 8 | +""" |
| 9 | + |
| 10 | +from odoo.tests import TransactionCase, tagged |
| 11 | + |
| 12 | + |
| 13 | +@tagged("post_install", "-at_install") |
| 14 | +class TestRelationalBarePredicates(TransactionCase): |
| 15 | + """Test that one2many and many2many fields work as bare predicates.""" |
| 16 | + |
| 17 | + def setUp(self): |
| 18 | + super().setUp() |
| 19 | + self.service = self.env["spp.cel.service"] |
| 20 | + |
| 21 | + def test_one2many_field_as_bare_predicate_compiles(self): |
| 22 | + """A one2many field used as a bare predicate should compile. |
| 23 | +
|
| 24 | + Expression like `program_membership_ids` should be treated as |
| 25 | + checking whether the field has records (not empty). |
| 26 | + """ |
| 27 | + # program_membership_ids is a One2many on res.partner |
| 28 | + # (from spp_programs module) |
| 29 | + if "program_membership_ids" not in self.env["res.partner"]._fields: |
| 30 | + self.skipTest("spp_programs not installed (no program_membership_ids field)") |
| 31 | + |
| 32 | + result = self.service.compile_expression( |
| 33 | + "program_membership_ids", |
| 34 | + "registry_groups", |
| 35 | + ) |
| 36 | + self.assertTrue(result["valid"], f"Error: {result.get('error')}") |
| 37 | + # Should produce a domain checking the field is not empty |
| 38 | + self.assertIsInstance(result["domain"], list) |
| 39 | + |
| 40 | + def test_many2many_field_as_bare_predicate_compiles(self): |
| 41 | + """A many2many field used as a bare predicate should compile.""" |
| 42 | + # Find any many2many field on res.partner for testing |
| 43 | + partner_fields = self.env["res.partner"]._fields |
| 44 | + m2m_field = None |
| 45 | + for fname, fobj in partner_fields.items(): |
| 46 | + if getattr(fobj, "type", None) == "many2many": |
| 47 | + m2m_field = fname |
| 48 | + break |
| 49 | + |
| 50 | + if not m2m_field: |
| 51 | + self.skipTest("No many2many field found on res.partner") |
| 52 | + |
| 53 | + result = self.service.compile_expression( |
| 54 | + m2m_field, |
| 55 | + "registry_groups", |
| 56 | + ) |
| 57 | + self.assertTrue(result["valid"], f"Error: {result.get('error')}") |
| 58 | + self.assertIsInstance(result["domain"], list) |
| 59 | + |
| 60 | + def test_one2many_predicate_produces_correct_domain(self): |
| 61 | + """Bare one2many predicate should produce a '!= False' domain. |
| 62 | +
|
| 63 | + This is the standard Odoo pattern for checking if a relational |
| 64 | + field has records. |
| 65 | + """ |
| 66 | + if "program_membership_ids" not in self.env["res.partner"]._fields: |
| 67 | + self.skipTest("spp_programs not installed (no program_membership_ids field)") |
| 68 | + |
| 69 | + result = self.service.compile_expression( |
| 70 | + "program_membership_ids", |
| 71 | + "registry_groups", |
| 72 | + ) |
| 73 | + self.assertTrue(result["valid"], f"Error: {result.get('error')}") |
| 74 | + # The domain should contain a check for "has records" |
| 75 | + domain = result["domain"] |
| 76 | + # Look for the field check in the domain |
| 77 | + found = False |
| 78 | + for leaf in domain: |
| 79 | + if isinstance(leaf, tuple) and leaf[0] == "program_membership_ids" and leaf[1] == "!=": |
| 80 | + found = True |
| 81 | + break |
| 82 | + self.assertTrue(found, f"Expected '!= False' domain for one2many, got: {domain}") |
0 commit comments