From aeeb10ad37bc6de0383a94728ca39834d41771a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 10 Oct 2022 10:02:18 +0000 Subject: [PATCH 1/4] [IMP] test_mail_full: improve test models to ease future tests In order to better reflect real models, let us have phone and mobile fields being computed either by hand, either based on customer on 'mail.test.sms.bl' test model. Done in its own commit to ease discovering of side effects in tests, which does not seem to be the case. Task-3012789 X-original-commit: 71e2b8713bc83ed999ab2e2839edc56804a0fe54 Part-of: odoo/odoo#104111 --- .../models/test_mail_sms_models.py | 16 +++++-- addons/test_mail_sms/tests/__init__.py | 1 + .../tests/test_mail_thread_phone.py | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 addons/test_mail_sms/tests/test_mail_thread_phone.py diff --git a/addons/test_mail_sms/models/test_mail_sms_models.py b/addons/test_mail_sms/models/test_mail_sms_models.py index bde42242c80..37ff433b5a7 100644 --- a/addons/test_mail_sms/models/test_mail_sms_models.py +++ b/addons/test_mail_sms/models/test_mail_sms_models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +from odoo import api, fields, models class MailTestSMS(models.Model): @@ -39,10 +39,20 @@ class MailTestSMSBL(models.Model): name = fields.Char() subject = fields.Char() email_from = fields.Char() - phone_nbr = fields.Char() - mobile_nbr = fields.Char() + phone_nbr = fields.Char(compute='_compute_phone_nbr', readonly=False, store=True) + mobile_nbr = fields.Char(compute='_compute_mobile_nbr', readonly=False, store=True) customer_id = fields.Many2one('res.partner', 'Customer') + @api.depends('customer_id') + def _compute_mobile_nbr(self): + for phone_record in self.filtered(lambda rec: not rec.mobile_nbr and rec.customer_id): + phone_record.mobile_nbr = phone_record.customer_id.mobile + + @api.depends('customer_id') + def _compute_phone_nbr(self): + for phone_record in self.filtered(lambda rec: not rec.phone_nbr and rec.customer_id): + phone_record.phone_nbr = phone_record.customer_id.phone + def _sms_get_partner_fields(self): return ['customer_id'] diff --git a/addons/test_mail_sms/tests/__init__.py b/addons/test_mail_sms/tests/__init__.py index 6aeea5b790d..a7a67dc3ef4 100644 --- a/addons/test_mail_sms/tests/__init__.py +++ b/addons/test_mail_sms/tests/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import test_mail_thread_phone from . import test_phone_blacklist from . import test_sms_composer from . import test_sms_management diff --git a/addons/test_mail_sms/tests/test_mail_thread_phone.py b/addons/test_mail_sms/tests/test_mail_thread_phone.py new file mode 100644 index 00000000000..cc8b5aeae1b --- /dev/null +++ b/addons/test_mail_sms/tests/test_mail_thread_phone.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_sms.tests.common import TestSMSCommon, TestSMSRecipients +from odoo.tests import tagged, users + + +@tagged('mail_thread') +class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients): + """ Test mail.thread.phone mixin, its tools and API """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.test_phone_records, cls.test_phone_partners = cls._create_records_for_batch( + 'mail.test.sms.bl', + 5, + ) + cls.test_phone_records += cls.env['mail.test.sms.bl'].create([ + { + 'phone_nbr': '+32475110505', + 'mobile_nbr': '+32475000505', + }, { + 'phone_nbr': '0032475110606', + 'mobile_nbr': '0032475000606', + }, { + 'phone_nbr': '0032475110707', + 'mobile_nbr': False, + }, { + 'phone_nbr': False, + 'mobile_nbr': False, + }, + ]) + + def test_initial_data(self): + """ Test initial data for this class, allowing to be sure of I/O of tests. """ + self.assertEqual( + self.test_phone_records.mapped('mobile_nbr'), + ['0475000000', '0475000101', '0475000202', '0475000303', '0475000404', + '+32475000505', '0032475000606', + False, False, + ] + ) + self.assertEqual( + self.test_phone_records.mapped('phone_nbr'), + [False] * 5 + ['+32475110505', '0032475110606', '0032475110707', False] + ) From 47f08bf9d36b868f24cef32ee3962da8f5580e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 10 Oct 2022 12:14:17 +0000 Subject: [PATCH 2/4] [FIX] phone_validation: correctly use phone fields when searching on phone/mobile Currently the search methods defined on ``phone_mobile_search`` field forces the usage of ``phone`` and ``mobile`` fields on model. This currently works on models using by default this search field (lead and partner). However this breaks on any model not having those two fields and inheriting from the 'mail.thread.phone' mixin. In this commit we use the result of ``_phone_get_number_fields`` to know which fields to use in the SQL query. Tests are added to ensure it works as intended. Task-3012789 X-original-commit: 6b235868da6fed1b2a0eaa7a25dc2202c6fefaa0 Part-of: odoo/odoo#104111 --- .../i18n/phone_validation.pot | 6 +++ .../models/mail_thread_phone.py | 48 ++++++++++--------- .../tests/test_mail_thread_phone.py | 46 ++++++++++++++++++ 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/addons/phone_validation/i18n/phone_validation.pot b/addons/phone_validation/i18n/phone_validation.pot index 4dd9f47f91b..0c2fc3ce383 100644 --- a/addons/phone_validation/i18n/phone_validation.pot +++ b/addons/phone_validation/i18n/phone_validation.pot @@ -248,6 +248,12 @@ msgstr "" msgid "Messages" msgstr "" +#. module: phone_validation +#: code:addons/phone_validation/models/mail_thread_phone.py:0 +#, python-format +msgid "Missing definition of phone fields." +msgstr "" + #. module: phone_validation #: model:ir.model.constraint,message:phone_validation.constraint_phone_blacklist_unique_number msgid "Number already exists" diff --git a/addons/phone_validation/models/mail_thread_phone.py b/addons/phone_validation/models/mail_thread_phone.py index 3cb3364f857..229c743d9a4 100644 --- a/addons/phone_validation/models/mail_thread_phone.py +++ b/addons/phone_validation/models/mail_thread_phone.py @@ -6,6 +6,7 @@ import re from odoo import api, fields, models, _ from odoo.addons.phone_validation.tools import phone_validation from odoo.exceptions import AccessError, UserError +from odoo.osv import expression class PhoneMixin(models.AbstractModel): @@ -47,6 +48,13 @@ class PhoneMixin(models.AbstractModel): phone_mobile_search = fields.Char("Phone/Mobile", store=False, search='_search_phone_mobile_search') def _search_phone_mobile_search(self, operator, value): + phone_fields = [ + fname for fname in self._phone_get_number_fields() + if fname in self._fields and self._fields[fname].store + ] + if not phone_fields: + raise UserError(_('Missing definition of phone fields.')) + value = value.strip() if len(value) < 3: raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.')) @@ -55,39 +63,35 @@ class PhoneMixin(models.AbstractModel): if value.startswith('+') or value.startswith('00'): # searching on +32485112233 should also finds 0032485112233 (and vice versa) # we therefore remove it from input value and search for both of them in db + where_str = ' OR '.join( + f"""model.{phone_field} IS NOT NULL AND ( + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s OR + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s + )""" + for phone_field in phone_fields + ) query = f""" SELECT model.id FROM {self._table} model - WHERE - model.phone IS NOT NULL AND ( - REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s OR - REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s - ) OR - model.mobile IS NOT NULL AND ( - REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s OR - REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s - ); + WHERE {where_str}; """ term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:]) + '%' - self._cr.execute(query, ( - pattern, '00' + term, - pattern, '+' + term, - pattern, '00' + term, - pattern, '+' + term - )) + self._cr.execute( + query, + (pattern, '00' + term, pattern, '+' + term) * len(phone_fields) + ) else: + where_str = ' OR '.join( + f"REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s" + for phone_field in phone_fields + ) query = f""" SELECT model.id FROM {self._table} model - WHERE - REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s OR - REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s; + WHERE {where_str}; """ term = '%' + re.sub(pattern, '', value) + '%' - self._cr.execute(query, ( - pattern, term, - pattern, term - )) + self._cr.execute(query, (pattern, term) * len(phone_fields)) res = self._cr.fetchall() if not res: return [(0, '=', 1)] diff --git a/addons/test_mail_sms/tests/test_mail_thread_phone.py b/addons/test_mail_sms/tests/test_mail_thread_phone.py index cc8b5aeae1b..e26bef4aee2 100644 --- a/addons/test_mail_sms/tests/test_mail_thread_phone.py +++ b/addons/test_mail_sms/tests/test_mail_thread_phone.py @@ -45,3 +45,49 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients): self.test_phone_records.mapped('phone_nbr'), [False] * 5 + ['+32475110505', '0032475110606', '0032475110707', False] ) + + @users('employee') + def test_search_phone_mobile_search_equal(self): + """ Test searching by phone/mobile with direct search """ + test_phone_records = self.test_phone_records.with_env(self.env) + + # test "=" search + # Currently not suported, returning results for "ilike" + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475')]) + self.assertEqual(results, test_phone_records[:5]) + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475000000')]) + self.assertEqual(results, test_phone_records[0]) + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0032475110606')]) + self.assertEqual(results, test_phone_records[6]) + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '+32475110606')]) + self.assertEqual(results, test_phone_records[6]) + + @users('employee') + def test_search_phone_mobile_search_ilike(self): + """ Test searching by phone/mobile on various ilike combinations """ + test_phone_records = self.test_phone_records.with_env(self.env) + + # test ilike search + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '0475')]) + self.assertEqual(results, test_phone_records[:5]) + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '101')]) + self.assertEqual(results, test_phone_records[1]) + + # test search using +32/0032 + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '+32475')]) + self.assertEqual(results, test_phone_records[5:8], + 'Search on phone_mobile_search: +32/0032 likeliness') + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '0032475')]) + self.assertEqual(results, test_phone_records[5:8], + 'Search on phone_mobile_search: +32/0032 likeliness') + + # test inverse ilike search + # Currently not supported, returning results for "ilike" + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '0475')]) + # self.assertEqual(results, test_phone_records - test_phone_records[:5], + self.assertEqual(results, test_phone_records[:5], + 'Seach on phone_mobile_search: not ilike not supported') + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '101')]) + # self.assertEqual(results, test_phone_records - test_phone_records[1], + self.assertEqual(results, test_phone_records[1], + 'Seach on phone_mobile_search: not ilike not supported') From 897eedf22e7499be43c7014b05ca30ce22ab50cb Mon Sep 17 00:00:00 2001 From: "Abdelouahab (abla)" Date: Wed, 21 Sep 2022 13:37:25 +0000 Subject: [PATCH 3/4] [FIX] phone_validation : filter by phone/mobile is set To reproduce ============ Go to Contacts and try to filter by phone/mobile is/not set. A traceback is raised. Problem ======= The search method responsible for this field didn't take into account Value to filter by to be a boolean. Solution ======== Correctly support this case in search method. Add unit test. Task-3012789 opw-2980542 X-original-commit: 62b7da527c6ddb4654fbccf5c6d8c94158915ebc Part-of: odoo/odoo#104111 --- .../models/mail_thread_phone.py | 10 +++++++- .../tests/test_mail_thread_phone.py | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/addons/phone_validation/models/mail_thread_phone.py b/addons/phone_validation/models/mail_thread_phone.py index 229c743d9a4..931dee5f565 100644 --- a/addons/phone_validation/models/mail_thread_phone.py +++ b/addons/phone_validation/models/mail_thread_phone.py @@ -48,6 +48,7 @@ class PhoneMixin(models.AbstractModel): phone_mobile_search = fields.Char("Phone/Mobile", store=False, search='_search_phone_mobile_search') def _search_phone_mobile_search(self, operator, value): + value = value.strip() if isinstance(value, str) else value phone_fields = [ fname for fname in self._phone_get_number_fields() if fname in self._fields and self._fields[fname].store @@ -55,7 +56,14 @@ class PhoneMixin(models.AbstractModel): if not phone_fields: raise UserError(_('Missing definition of phone fields.')) - value = value.strip() + # search if phone/mobile is set or not + if (value is True or not value) and operator in ('=', '!='): + if value: + # inverse the operator + operator = '=' if operator == '!=' else '!=' + op = expression.AND if operator == '=' else expression.OR + return op([[(phone_field, operator, False)] for phone_field in phone_fields]) + if len(value) < 3: raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.')) diff --git a/addons/test_mail_sms/tests/test_mail_thread_phone.py b/addons/test_mail_sms/tests/test_mail_thread_phone.py index e26bef4aee2..cd5350a45de 100644 --- a/addons/test_mail_sms/tests/test_mail_thread_phone.py +++ b/addons/test_mail_sms/tests/test_mail_thread_phone.py @@ -46,6 +46,29 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients): [False] * 5 + ['+32475110505', '0032475110606', '0032475110707', False] ) + @users('employee') + def test_search_phone_mobile_search_boolean(self): + test_phone_records = self.test_phone_records.with_env(self.env) + + # test Falsy -> is set / is not set + for test_values in [False, '', ' ']: + # test is not set -> both fields should be not set + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', test_values)]) + self.assertEqual(results, test_phone_records[-1], + 'Search on phone_mobile_search: = False: record with two void values') + # test is set -> at least one field should be set + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '!=', test_values)]) + self.assertEqual(results, test_phone_records[:-1], + 'Search on phone_mobile_search: != False: record at least one value set') + + # test Truthy -> is set / is not set + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', True)]) + self.assertEqual(results, test_phone_records[:-1], + 'Search on phone_mobile_search: = True: record at least one value set') + results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '!=', True)]) + self.assertEqual(results, test_phone_records[-1], + 'Search on phone_mobile_search: != True: record with two void values') + @users('employee') def test_search_phone_mobile_search_equal(self): """ Test searching by phone/mobile with direct search """ From 9a5046ba0aeb9434bcc4bd157c0f79000ffa1fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 11 Oct 2022 10:15:13 +0000 Subject: [PATCH 4/4] [FIX] phone_validation: correctly support phone/mobile search parameters Currently the search method defined on 'phone_mobile_search' supports either a boolean (is set / is not set) search, either considers all searches to be "like TERM". This is coming from the main usage that is the search view that sends domains like "[('phone_mobile_search', 'ilike', term)]". In this commit we improve the method to correctly support * direct check (=) * negative operators (not (i)like) * like / ilike (previously like were considered as ilike) Idea is: a positive operator check that any of the phone fields respects the domain (phone or mobile is TERM). A negative operator checks that all phone fields respects the negative domain (both phone and mobile do not contain TERM). Tests are added. Task-3012789 X-original-commit: 8e905343f38a4dae828ccaddc62ce5c6a2f754db Part-of: odoo/odoo#104111 --- addons/crm/tests/test_crm_lead.py | 8 ++- .../models/mail_thread_phone.py | 69 +++++++++++-------- .../tests/test_mail_thread_phone.py | 12 +--- 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/addons/crm/tests/test_crm_lead.py b/addons/crm/tests/test_crm_lead.py index ae0b9b10ccc..f4e32a50d90 100644 --- a/addons/crm/tests/test_crm_lead.py +++ b/addons/crm/tests/test_crm_lead.py @@ -735,10 +735,14 @@ class TestCRMLead(TestCrmCommon): lead_3, 'Should behave like a text field' ) - self.assertEqual( + self.assertFalse( self.env['crm.lead'].search([('phone_mobile_search', 'like', 'Hello')]), + 'Should behave like a text field (case sensitive)' + ) + self.assertEqual( + self.env['crm.lead'].search([('phone_mobile_search', 'ilike', 'Hello')]), lead_3, - 'Should behave like a text field' + 'Should behave like a text field (case insensitive)' ) self.assertEqual( self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello123')]), diff --git a/addons/phone_validation/models/mail_thread_phone.py b/addons/phone_validation/models/mail_thread_phone.py index 931dee5f565..059eeb399f2 100644 --- a/addons/phone_validation/models/mail_thread_phone.py +++ b/addons/phone_validation/models/mail_thread_phone.py @@ -68,37 +68,52 @@ class PhoneMixin(models.AbstractModel): raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.')) pattern = r'[\s\\./\(\)\-]' + sql_operator = {'=like': 'LIKE', '=ilike': 'ILIKE'}.get(operator, operator) + if value.startswith('+') or value.startswith('00'): - # searching on +32485112233 should also finds 0032485112233 (and vice versa) - # we therefore remove it from input value and search for both of them in db - where_str = ' OR '.join( - f"""model.{phone_field} IS NOT NULL AND ( - REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s OR - REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s - )""" - for phone_field in phone_fields - ) - query = f""" - SELECT model.id - FROM {self._table} model - WHERE {where_str}; - """ - term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:]) + '%' + if operator in expression.NEGATIVE_TERM_OPERATORS: + # searching on +32485112233 should also finds 0032485112233 (and vice versa) + # we therefore remove it from input value and search for both of them in db + where_str = ' AND '.join( + f"""model.{phone_field} IS NULL OR ( + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s OR + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s + )""" + for phone_field in phone_fields + ) + else: + # searching on +32485112233 should also finds 0032485112233 (and vice versa) + # we therefore remove it from input value and search for both of them in db + where_str = ' OR '.join( + f"""model.{phone_field} IS NOT NULL AND ( + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s OR + REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s + )""" + for phone_field in phone_fields + ) + query = f"SELECT model.id FROM {self._table} model WHERE {where_str};" + + term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:]) + if operator not in ('=', '!='): # for like operators + term = f'{term}%' self._cr.execute( - query, - (pattern, '00' + term, pattern, '+' + term) * len(phone_fields) + query, (pattern, '00' + term, pattern, '+' + term) * len(phone_fields) ) else: - where_str = ' OR '.join( - f"REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s" - for phone_field in phone_fields - ) - query = f""" - SELECT model.id - FROM {self._table} model - WHERE {where_str}; - """ - term = '%' + re.sub(pattern, '', value) + '%' + if operator in expression.NEGATIVE_TERM_OPERATORS: + where_str = ' AND '.join( + f"(model.{phone_field} IS NULL OR REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s)" + for phone_field in phone_fields + ) + else: + where_str = ' OR '.join( + f"(model.{phone_field} IS NOT NULL AND REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s)" + for phone_field in phone_fields + ) + query = f"SELECT model.id FROM {self._table} model WHERE {where_str};" + term = re.sub(pattern, '', value) + if operator not in ('=', '!='): # for like operators + term = f'%{term}%' self._cr.execute(query, (pattern, term) * len(phone_fields)) res = self._cr.fetchall() if not res: diff --git a/addons/test_mail_sms/tests/test_mail_thread_phone.py b/addons/test_mail_sms/tests/test_mail_thread_phone.py index cd5350a45de..0ad61f0e279 100644 --- a/addons/test_mail_sms/tests/test_mail_thread_phone.py +++ b/addons/test_mail_sms/tests/test_mail_thread_phone.py @@ -75,9 +75,8 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients): test_phone_records = self.test_phone_records.with_env(self.env) # test "=" search - # Currently not suported, returning results for "ilike" results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475')]) - self.assertEqual(results, test_phone_records[:5]) + self.assertFalse(results, 'Search on phone_mobile_search: = should return only matching results') results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475000000')]) self.assertEqual(results, test_phone_records[0]) results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0032475110606')]) @@ -105,12 +104,7 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients): 'Search on phone_mobile_search: +32/0032 likeliness') # test inverse ilike search - # Currently not supported, returning results for "ilike" results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '0475')]) - # self.assertEqual(results, test_phone_records - test_phone_records[:5], - self.assertEqual(results, test_phone_records[:5], - 'Seach on phone_mobile_search: not ilike not supported') + self.assertEqual(results, test_phone_records - test_phone_records[:5]) results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '101')]) - # self.assertEqual(results, test_phone_records - test_phone_records[1], - self.assertEqual(results, test_phone_records[1], - 'Seach on phone_mobile_search: not ilike not supported') + self.assertEqual(results, test_phone_records - test_phone_records[1])