2 Testing for hierarchical search in M2M
4 !python {model: res.partner }: |
5 ids = self.search(cr, uid, [('category_id', 'child_of','supplier')])
6 assert len(ids) >= 1, ids
9 Test hierarchical search in M2M with child ID1
11 !python {model: res.partner }: |
12 ids = self.search(cr, uid, [('category_id', 'child_of','Components Supplier')])
13 assert len(ids) >= 1, ids
15 Test hierarchical search in M2M with child ID2
17 !python {model: res.partner }: |
18 ids = self.search(cr, uid, [('category_id', 'child_of','Miscellaneous Suppliers')])
19 assert len(ids) >= 1, ids
22 "1.0 Setup test partner categories: parent root"
24 !record {model: res.partner.category, id: categ_root}:
27 "1.1 Setup test partner categories: parent category"
29 !record {model: res.partner.category, id: categ_0}:
33 "1.2 Setup test partner categories: child 1"
35 !record {model: res.partner.category, id: categ_1}:
39 Test hierarchical search in M2M with child ID (list of ids)
41 !python {model: res.partner.category }: |
42 ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_root')])])
43 assert len(ids) == 3, ids
45 Test hierarchical search in M2M with child ID (single id)
47 !python {model: res.partner.category }: |
48 ids = self.search(cr, uid, [('id', 'child_of',ref('categ_root'))])
49 assert len(ids) == 3, ids
51 Test hierarchical search in M2M with child IDs
53 !python {model: res.partner.category }: |
54 ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1'), ref('categ_0')])])
55 assert len(ids) == 2, ids
57 Test hierarchical search in M2M with child IDs
59 !python {model: res.partner.category }: |
60 ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_0')])])
61 assert len(ids) == 2, ids
63 Test hierarchical search in M2M with child IDs
65 !python {model: res.partner.category }: |
66 ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1')])])
67 assert len(ids) == 1, ids
69 Testing that some domain expressions work
71 !python {model: res.partner.address }: |
72 ids = self.search(cr, uid, [('partner_id','=','Agrolait')])
73 assert len(ids) >= 1, ids
75 Trying the "in" operator, for scalar value
77 !python {model: res.partner.address }: |
78 ids = self.search(cr, uid, [('partner_id','in','Agrolait')])
79 assert len(ids) >= 1, ids
81 Trying the "in" operator for list value
83 !python {model: res.partner.address }: |
84 ids = self.search(cr, uid, [('partner_id','in',['Agrolait','ASUStek'])])
85 assert len(ids) >= 1, ids
87 Check we can use "in" operator for plain fields.
89 !python {model: ir.ui.menu }: |
90 ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
91 assert len(ids) >= 1, ids
93 Test one2many operator with empty search list
95 !assert {model: res.partner, search: "[('address', 'in', [])]", count: 0, string: "Ids should be empty"}
97 Test one2many operator with False
99 !assert {model: res.partner, search: "[('address', '=', False)]"}:
100 - address in (False, None, [])
102 Test many2many operator with empty search list
104 !assert {model: res.partner, search: "[('category_id', 'in', [])]", count: 0, string: "Ids should be empty"}
106 Test many2many operator with False
108 !assert {model: res.partner, search: "[('category_id', '=', False)]"}:
109 - category_id in (False, None, [])
111 Filtering on invalid value across x2many relationship should return an empty set
113 !assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
115 Check if many2one works with empty search list
117 !assert {model: res.partner, search: "[('company_id','in', [])]", count: 0, string: "Searching for company_id in [] should be empty!" }
119 For the sake of the following tests, I will create a second company
121 !record {model: res.company, id: ymltest_company2}:
124 And create a few partners with that company or no company
126 !python {model: res.partner }: |
128 self.create(cr, uid, { 'name': 'P of Acme %d' % r,
129 'company_id': ref('ymltest_company2') })
131 self.create(cr, uid, { 'name': 'P of All %d' % r,
132 'company_id': False })
134 Check if many2one works with negative empty list
136 !python {model: res.partner }: |
137 all_ids = self.search(cr, uid, [])
139 res_ids = self.search(cr, uid,['|',('company_id','not in', []), ('company_id','=',False)])
141 assert all_ids == res_ids, "not in [] fails"
143 Check that many2one will pick the correct records with a list
145 !python {model: res.partner }: |
146 res_ids = self.search(cr, uid, [('company_id', 'in', [False,])])
147 assert len(res_ids) >= 4, "We created 4 partners w/company, why find %d? %r" % \
148 (len(res_ids), res_ids)
150 Check that many2one will exclude the correct records with a list
152 !python {model: res.partner }: |
153 # assuming that the default company is #1
154 res_ids = self.search(cr, uid, [('company_id', 'not in', [1])])
155 assert len(res_ids) >= 4, "We should have found 4 records at least, only have %d! %r" % \
156 (len(res_ids), res_ids)
158 Check that we exclude the correct records, + False
160 !python {model: res.partner }: |
161 # assuming that the default company is #1
162 res_ids = self.search(cr, uid, ['|', ('company_id', 'not in', [1]), ('company_id', '=', False)])
163 assert len(res_ids) >= 8, "We should have found 8 records at least, only have %d! %r" % \
164 (len(res_ids), res_ids)
166 Check that multi-level expressions also work
168 !python {model: res.partner }: |
169 res_ids = self.search(cr, uid, [('company_id.partner_id', 'in', [])])
170 assert res_ids == [], "Searching an empty set should return empty result, not %r" % res_ids
172 Check that multi-level expressions with negative op work
174 !python {model: res.partner }: |
175 all_ids = self.search(cr, uid, [('company_id', '!=', False)])
177 res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])])
179 assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids
181 Test the '(not) like/in' behavior. res.partner and its parent_id column are used because
182 parent_id is a many2one, allowing to test the Null value, and there are actually some
183 null and non-null values in the demo data.
185 !python {model: res.partner }: |
186 partner_ids = self.search(cr, uid, [])
188 max_partner_id = max(partner_ids)
190 # Grab test sample data without using a normal
191 # search domain, because we want to test these later,
192 # so we can't rely on them!
193 partners = self.browse(cr, uid, partner_ids)
199 with_parent.append(x.id)
201 without_parent.append(x.id)
203 with_website.append(x.id)
205 without_parent.sort()
208 # We treat null values differently than in SQL. For instance in SQL:
209 # SELECT id FROM res_partner WHERE parent_id NOT IN (0)
210 # will return only the records with non-null parent_id.
211 # SELECT id FROM res_partner WHERE parent_id IN (0)
212 # will return expectedly nothing (our ids always begin at 1).
213 # This means the union of those two results will give only some
214 # records, but not all present in database.
216 # When using domains and the ORM's search method, we think it is
217 # more intuitive that the union returns all the records, and that
218 # a domain like ('parent_id', 'not in', [0]) will return all
219 # the records. For instance, if you perform a search for the companies
220 # that don't have OpenERP has a parent company, you expect to find,
221 # among others, the companies that don't have parent company.
223 # ('parent_id', 'not in', [0]) must give the same result than
224 # ('parent_id', 'not in', []), i.e. a empty set or a set with non-
225 # existing values be treated similarly if we simply check that some
226 # existing value belongs to them.
228 res_0 = self.search(cr, uid, [('parent_id', 'not like', 'probably_unexisting_name')]) # get all rows, included null parent_id
230 res_1 = self.search(cr, uid, [('parent_id', 'not in', [max_partner_id + 1])]) # get all rows, included null parent_id
232 res_2 = self.search(cr, uid, [('parent_id', 'not in', False)]) # get rows with not null parent_id, deprecated syntax
234 res_3 = self.search(cr, uid, [('parent_id', 'not in', [])]) # get all rows, included null parent_id
236 res_4 = self.search(cr, uid, [('parent_id', 'not in', [False])]) # get rows with not null parent_id
238 res_4b = self.search(cr, uid, [('parent_id', 'not ilike', '')]) # get only rows without parent
240 assert res_0 == partner_ids, 'res0: expected %r, got %r' % (partner_ids, res_0)
241 assert res_1 == partner_ids, 'res1: expected %r, got %r' % (partner_ids, res_1)
242 assert res_2 == with_parent, 'res2: expected %r, got %r' % (with_parent, res_2)
243 assert res_3 == partner_ids, 'res3: expected %r, got %r' % (partner_ids, res_3)
244 assert res_4 == with_parent, 'res4: expected %r, got %r' % (with_parent, res_4)
245 assert res_4b == without_parent, 'res4b: expected %r, got %r' % (without_parent, res_4b)
246 # The results of these queries, when combined with queries 0..4 must
247 # give the whole set of ids.
248 res_5 = self.search(cr, uid, [('parent_id', 'like', 'probably_unexisting_name')])
250 res_6 = self.search(cr, uid, [('parent_id', 'in', [max_partner_id + 1])])
252 res_7 = self.search(cr, uid, [('parent_id', 'in', False)])
254 res_8 = self.search(cr, uid, [('parent_id', 'in', [])])
256 res_9 = self.search(cr, uid, [('parent_id', 'in', [False])])
258 res_9b = self.search(cr, uid, [('parent_id', 'ilike', '')]) # get those with a parent
261 assert res_5 == [], 'res5: expected %r, got %r' % ([], res_5)
262 assert res_6 == [], 'res6: expected %r, got %r' % ([], res_6)
263 assert res_7 == without_parent, 'res7: expected %r, got %r' % (without_parent, res_7)
264 assert res_8 == [], 'res8: expected %r, got %r' % ([], res_8)
265 assert res_9 == without_parent, 'res9: expected %r, got %r' % (without_parent, res_9)
266 assert res_9b == with_parent, 'res9b: expected %r, got %r' % (with_parent, res_9b)
267 # These queries must return exactly the results than the queries 0..4,
268 # i.e. not ... in ... must be the same as ... not in ... .
269 res_10 = self.search(cr, uid, ['!', ('parent_id', 'like', 'probably_unexisting_name')])
271 res_11 = self.search(cr, uid, ['!', ('parent_id', 'in', [max_partner_id + 1])])
273 res_12 = self.search(cr, uid, ['!', ('parent_id', 'in', False)])
275 res_13 = self.search(cr, uid, ['!', ('parent_id', 'in', [])])
277 res_14 = self.search(cr, uid, ['!', ('parent_id', 'in', [False])])
279 assert res_0 == res_10
280 assert res_1 == res_11
281 assert res_2 == res_12
282 assert res_3 == res_13
283 assert res_4 == res_14
285 # Testing many2one field is not enough, a regular char field is tested
286 # with in [] and must not return any result.
287 res_15 = self.search(cr, uid, [('website', 'in', [])])
289 # not in [] must return everything.
290 res_16 = self.search(cr, uid, [('website', 'not in', [])])
292 assert res_16 == partner_ids
294 res_17 = self.search(cr, uid, [('website', 'not in', False)])
296 assert res_17 == with_website
299 Check behavior for required many2one fields
301 !python {model: res.company }: |
302 company_ids = sorted(self.search(cr, uid, []))
303 # currency_id is required
304 res_101 = sorted(self.search(cr, uid, [('currency_id', 'not ilike', '')])) # get no companies
305 res_102 = sorted(self.search(cr, uid, [('currency_id', 'ilike', '')])) # get all companies
306 assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
307 assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
309 Property of the query (one2many not in False).
311 !python {model: res.currency }: |
312 ids = self.search(cr, uid, [])
313 referenced_companies = set([x.company_id.id for x in self.browse(cr, uid, ids)])
314 companies = set(self.pool.get('res.company').search(cr, uid, [('currency_ids', 'not in', False)]))
315 assert referenced_companies == companies
317 Property of the query (one2many in False).
319 !python {model: res.currency }: |
320 ids = self.search(cr, uid, [])
321 referenced_companies = set([x.company_id.id for x in self.browse(cr, uid, ids)])
322 unreferenced_companies = set(self.pool.get('res.company').search(cr, uid, [])).difference(referenced_companies)
323 companies = set(self.pool.get('res.company').search(cr, uid, [('currency_ids', 'in', False)]))
324 assert unreferenced_companies == companies
328 !python {model: res.currency }: |
329 max_currency_id = max(self.search(cr, uid, []))
330 res_0 = self.search(cr, uid, [])
331 res_1 = self.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
332 res_2 = self.search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
333 res_3 = self.search(cr, uid, [('id', 'not in', [])])
334 res_4 = self.search(cr, uid, [('id', 'not in', False)])
340 assert res_0 == res_1
341 assert res_0 == res_2
342 assert res_0 == res_3
343 assert res_0 == res_4
345 Equivalent queries, integer and string.
347 !python {model: res.partner }: |
348 all_ids = self.search(cr, uid, [])
351 record = self.browse(cr, uid, one)
353 res_1 = self.search(cr, uid, [('id', '=', one)])
354 # self.search(cr, uid, [('id', '!=', others)]) # not permitted
355 res_2 = self.search(cr, uid, [('id', 'not in', others)])
356 res_3 = self.search(cr, uid, ['!', ('id', '!=', one)])
357 res_4 = self.search(cr, uid, ['!', ('id', 'in', others)])
358 # res_5 = self.search(cr, uid, [('id', 'in', one)]) # TODO make it permitted, just like for child_of
359 res_6 = self.search(cr, uid, [('id', 'in', [one])])
360 res_7 = self.search(cr, uid, [('name', '=', record.name)])
361 res_8 = self.search(cr, uid, [('name', 'in', [record.name])])
362 # res_9 = self.search(cr, uid, [('name', 'in', record.name)]) # TODO
363 assert [one] == res_1
364 assert [one] == res_2
365 assert [one] == res_3
366 assert [one] == res_4
367 #assert [one] == res_5
368 assert [one] == res_6
369 assert [one] == res_7
371 Need a company with a parent_id.
373 !record {model: res.company, id: ymltest_company3}:
376 Need a company with a parent_id.
378 !record {model: res.company, id: ymltest_company4}:
380 parent_id: ymltest_company3
382 Equivalent queries, one2many.
384 !python {model: res.company }: |
385 # Search the company via its one2many (the one2many must point back at the company).
386 company = self.browse(cr, uid, ref('ymltest_company3'))
387 max_currency_id = max(self.pool.get('res.currency').search(cr, uid, []))
388 currency_ids1 = self.pool.get('res.currency').search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
389 currency_ids2 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
390 currency_ids3 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])])
391 assert currency_ids1 == currency_ids2 == currency_ids3, 'All 3 results should have be the same: all currencies'
392 default_company = self.browse(cr, uid, 1)
393 # one2many towards same model
394 res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child
395 res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child
396 # one2many towards another model
397 res_3 = self.search(cr, uid, [('currency_ids', 'in', [x.id for x in default_company.currency_ids])]) # companies having a currency of main company
398 res_4 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].id])]) # companies having first currency of main company
399 res_5 = self.search(cr, uid, [('currency_ids', 'in', default_company.currency_ids[0].id)]) # companies having first currency of main company
400 # res_6 = self.search(cr, uid, [('currency_ids', 'in', [default_company.currency_ids[0].name])]) # TODO
401 res_7 = self.search(cr, uid, [('currency_ids', '=', default_company.currency_ids[0].name)])
402 res_8 = self.search(cr, uid, [('currency_ids', 'like', default_company.currency_ids[0].name)])
403 res_9 = self.search(cr, uid, [('currency_ids', 'like', 'probably_unexisting_name')])
404 # self.search(cr, uid, [('currency_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception
405 assert res_1 == [ref('ymltest_company3')]
406 assert res_2 == [ref('ymltest_company3')]
414 # get the companies referenced by some currency (this is normally the main company)
415 res_10 = self.search(cr, uid, [('currency_ids', 'not like', 'probably_unexisting_name')])
416 res_11 = self.search(cr, uid, [('currency_ids', 'not in', [max_currency_id + 1])])
417 res_12 = self.search(cr, uid, [('currency_ids', 'not in', False)])
418 res_13 = self.search(cr, uid, [('currency_ids', 'not in', [])])
423 assert res_10 == res_11
424 assert res_10 == res_12
425 assert res_10 == res_13
427 # child_of x returns x and its children (direct or not).
428 company = self.browse(cr, uid, ref('ymltest_company3'))
429 expected = [ref('ymltest_company3'), ref('ymltest_company4')]
431 res_1 = self.search(cr, uid, [('id', 'child_of', [ref('ymltest_company3')])])
433 res_2 = self.search(cr, uid, [('id', 'child_of', ref('ymltest_company3'))])
435 res_3 = self.search(cr, uid, [('id', 'child_of', [company.name])])
437 res_4 = self.search(cr, uid, [('id', 'child_of', company.name)])
439 assert res_1 == expected
440 assert res_2 == expected
441 assert res_3 == expected
442 assert res_4 == expected
444 Verify that normalize_domain() works.
446 !python {model: res.partner}: |
447 from osv import expression
448 norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
449 assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
450 domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
451 norm_domain = ['&','&','&'] + domain
452 assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
454 Unaccent. Create a company with an accent in its name.
456 !record {model: res.company, id: ymltest_unaccent_company}:
459 Test the unaccent-enabled 'ilike'.
461 !python {model: res.company}: |
462 if self.pool.has_unaccent:
463 ids = self.search(cr, uid, [('name','ilike','Helene')], {})
464 assert ids == [ref('ymltest_unaccent_company')]
465 ids = self.search(cr, uid, [('name','ilike','hélène')], {})
466 assert ids == [ref('ymltest_unaccent_company')]
467 ids = self.search(cr, uid, [('name','not ilike','Helene')], {})
468 assert ref('ymltest_unaccent_company') not in ids
469 ids = self.search(cr, uid, [('name','not ilike','hélène')], {})
470 assert ref('ymltest_unaccent_company') not in ids
472 Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on an untranslated field.
474 !python {model: res.partner }: |
475 all_ids = self.search(cr, uid, [('name', '=like', 'A_e_or')])
476 assert len(all_ids) == 1, "Must match one partner (Axelor), got %r"%all_ids
477 all_ids = self.search(cr, uid, [('name', '=ilike', 'm_____')])
478 assert len(all_ids) == 1, "Must match *only* one partner (Maxtor), got %r"%all_ids
480 Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on translated field.
482 !python {model: res.country }: |
483 all_ids = self.search(cr, uid, [('name', '=like', 'Ind__')])
484 assert len(all_ids) == 1, "Must match India only, got %r"%all_ids
485 all_ids = self.search(cr, uid, [('name', '=ilike', 'z%')])
486 assert len(all_ids) == 3, "Must match only countries with names starting with Z (currently 3), got %r"%all_ids
488 Use the create_date column on res.country (which doesn't declare it in _columns).
490 !python {model: res.country }: |
491 ids = self.search(cr, uid, [('create_date', '<', '2001-01-01 12:00:00')])
495 Verify that invalid expressions are refused, even for magic fields
497 !python {model: res.country }: |
499 self.search(cr, uid, [('does_not_exist', '=', 'foo')])
500 raise AssertionError('Invalid fields should not be accepted')
505 self.search(cr, uid, [('create_date', '>>', 'foo')])
506 raise AssertionError('Invalid operators should not be accepted')
512 cr._default_log_exceptions = False
513 cr.execute('SAVEPOINT expression_failure_test')
514 self.search(cr, uid, [('create_date', '=', "1970-01-01'); --")])
515 # if the above search gives no error, the operand was not escaped!
516 cr.execute('RELEASE SAVEPOINT expression_failure_test')
517 raise AssertionError('Operands should always be SQL escaped')
518 except psycopg2.DataError:
519 # Should give: 'DataError: invalid input syntax for type timestamp' or similar
520 cr.execute('ROLLBACK TO SAVEPOINT expression_failure_test')
522 Testing for Many2Many field with category supplier and active=False
524 !python {model: res.partner }: |
525 vals = {'category_id': [(6, 0, [ref("base.res_partner_category_8")])],
526 'name': 'OpenERP Test',
528 'address': [(0, 0, {'country_id': ref("base.be")})]
530 self.create(cr, uid, vals, context=context)
531 res_ids = self.search(cr, uid, [('category_id', 'ilike', 'supplier'), ('active', '=', False)])
532 assert len(res_ids) != 0, "Record not Found with category supplier and active False."
534 Testing for One2Many field with country Belgium and active=False
536 !python {model: res.partner }: |
537 res_ids = self.search(cr, uid, [('address.country_id','=','Belgium'),('active','=',False)])
538 assert len(res_ids) != 0, "Record not Found with country Belgium and active False."