# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 """Unit tests for the pure helpers in fusion_helpdesk.utils. These need no live central Odoo — they pin the identity keystone, the scoping security boundary, the public-message filter and the unread maths as plain data transformations. """ from odoo.tests import TransactionCase, tagged from odoo.addons.fusion_helpdesk.utils import ( build_ticket_vals, build_scope_domain, is_public_message, compute_unread_count, _norm_email, ) @tagged('post_install', '-at_install', 'fusion_helpdesk') class TestBuildTicketVals(TransactionCase): def test_identity_fields_present(self): vals = build_ticket_vals( kind='bug', subject='X', body_html='

b

', team_id=1, client_label='ENTECH', reporter_name='John Doe', reporter_email='john@entech.com', company_name='ENTECH Inc', ) self.assertEqual(vals['partner_email'], 'john@entech.com') self.assertEqual(vals['partner_name'], 'John Doe') self.assertEqual(vals['x_fc_client_label'], 'ENTECH') self.assertEqual(vals['partner_company_name'], 'ENTECH Inc') self.assertEqual(vals['team_id'], 1) self.assertIn('X', vals['name']) self.assertIn('[ENTECH]', vals['name']) def test_no_email_omits_partner_email(self): vals = build_ticket_vals( kind='feature', subject='Y', body_html='

b

', team_id=False, client_label='', reporter_name='Jane', reporter_email='', company_name='', ) self.assertNotIn('partner_email', vals) # never send an empty email self.assertNotIn('team_id', vals) # omit falsy team self.assertNotIn('x_fc_client_label', vals) # omit empty label self.assertEqual(vals['partner_name'], 'Jane') self.assertIn('Feature Request', vals['name']) @tagged('post_install', '-at_install', 'fusion_helpdesk') class TestScopeDomain(TransactionCase): def test_regular_scope_binds_email_and_label(self): dom = build_scope_domain(label='ENTECH', email='john@entech.com', is_admin=False) self.assertIn(('x_fc_client_label', '=', 'ENTECH'), dom) self.assertIn(('partner_email', '=ilike', 'john@entech.com'), dom) def test_admin_scope_binds_label_only(self): dom = build_scope_domain(label='ENTECH', email='a@entech.com', is_admin=True) self.assertIn(('x_fc_client_label', '=', 'ENTECH'), dom) self.assertFalse(any(t[0] == 'partner_email' for t in dom)) def test_empty_label_never_matches_everything(self): dom = build_scope_domain(label='', email='', is_admin=True) # label term must be present and must NOT be an empty string label_terms = [t for t in dom if t[0] == 'x_fc_client_label'] self.assertEqual(len(label_terms), 1) self.assertNotEqual(label_terms[0][2], '') def test_wildcard_email_cannot_widen_scope(self): # IDOR guard: a self-set email of '%' must NOT become a match-all # =ilike term — the wildcard has to be escaped to a literal. dom = build_scope_domain(label='ENTECH', email='%', is_admin=False) email_terms = [t for t in dom if t[0] == 'partner_email'] self.assertEqual(len(email_terms), 1) self.assertEqual(email_terms[0][2], '\\%', "'%' must be escaped so ILIKE matches it literally") def test_underscore_in_real_email_is_escaped_but_preserved(self): dom = build_scope_domain(label='ENTECH', email='john_doe@x.com', is_admin=False) email_terms = [t for t in dom if t[0] == 'partner_email'] self.assertEqual(email_terms[0][2], 'john\\_doe@x.com') @tagged('post_install', '-at_install', 'fusion_helpdesk') class TestMessageFilterAndUnread(TransactionCase): def test_internal_note_is_not_public(self): self.assertFalse(is_public_message({'subtype_is_internal': True})) self.assertTrue(is_public_message({'subtype_is_internal': False})) self.assertTrue(is_public_message({})) # default visible def test_unread_count(self): tickets = [ {'id': 1, 'last_support_msg_id': 10}, # seen 10 -> read {'id': 2, 'last_support_msg_id': 5}, # seen 3 -> unread {'id': 3, 'last_support_msg_id': 0}, # no support msg ] seen = {1: 10, 2: 3} self.assertEqual(compute_unread_count(tickets, seen), 1) def test_unread_count_unseen_ticket_counts(self): tickets = [{'id': 9, 'last_support_msg_id': 4}] self.assertEqual(compute_unread_count(tickets, {}), 1) @tagged('post_install', '-at_install', 'fusion_helpdesk') class TestNormEmail(TransactionCase): def test_valid_email_is_normalised_lowercase(self): self.assertEqual(_norm_email('John@Entech.COM'), 'john@entech.com') def test_first_valid_candidate_wins(self): # confirmed reply email empty -> fall back to the next valid one self.assertEqual(_norm_email('', 'not an email', 'jane@x.com'), 'jane@x.com') def test_wildcard_is_rejected(self): # IDOR guard: a self-set '%' must not survive as a scope key self.assertEqual(_norm_email('%'), '') def test_non_email_login_falls_through_to_empty(self): self.assertEqual(_norm_email('admin', 'also-not-email', ''), '') def test_controller_namespace_resolves_norm_email(self): # Regression: _norm_email was called in controllers/main.py # (submit + _identity) but never imported/defined -> NameError on # every inbox endpoint. Guard that the name is resolvable there. from odoo.addons.fusion_helpdesk.controllers import main self.assertTrue(hasattr(main, '_norm_email'))