[IMP] use model._fields instead of model._all_columns to cover all fields
[odoo/odoo.git] / openerp / addons / base / tests / test_api.py
1
2 from openerp import models
3 from openerp.tools import mute_logger
4 from openerp.osv.orm import except_orm
5 from openerp.tests import common
6
7
8 class TestAPI(common.TransactionCase):
9     """ test the new API of the ORM """
10
11     def assertIsRecordset(self, value, model):
12         self.assertIsInstance(value, models.BaseModel)
13         self.assertEqual(value._name, model)
14
15     def assertIsRecord(self, value, model):
16         self.assertIsRecordset(value, model)
17         self.assertTrue(len(value) <= 1)
18
19     def assertIsNull(self, value, model):
20         self.assertIsRecordset(value, model)
21         self.assertFalse(value)
22
23     @mute_logger('openerp.models')
24     def test_00_query(self):
25         """ Build a recordset, and check its contents. """
26         domain = [('name', 'ilike', 'j')]
27         ids = self.registry('res.partner').search(self.cr, self.uid, domain)
28         partners = self.env['res.partner'].search(domain)
29
30         # partners is a collection of browse records corresponding to ids
31         self.assertTrue(ids)
32         self.assertTrue(partners)
33
34         # partners and its contents are instance of the model, and share its ormcache
35         self.assertIsRecordset(partners, 'res.partner')
36         self.assertIs(partners._ormcache, self.env['res.partner']._ormcache)
37         for p in partners:
38             self.assertIsRecord(p, 'res.partner')
39             self.assertIs(p._ormcache, self.env['res.partner']._ormcache)
40
41         self.assertEqual([p.id for p in partners], ids)
42         self.assertEqual(self.env['res.partner'].browse(ids), partners)
43
44     @mute_logger('openerp.models')
45     def test_01_query_offset(self):
46         """ Build a recordset with offset, and check equivalence. """
47         partners1 = self.env['res.partner'].search([], offset=10)
48         partners2 = self.env['res.partner'].search([])[10:]
49         self.assertIsRecordset(partners1, 'res.partner')
50         self.assertIsRecordset(partners2, 'res.partner')
51         self.assertEqual(list(partners1), list(partners2))
52
53     @mute_logger('openerp.models')
54     def test_02_query_limit(self):
55         """ Build a recordset with offset, and check equivalence. """
56         partners1 = self.env['res.partner'].search([], limit=10)
57         partners2 = self.env['res.partner'].search([])[:10]
58         self.assertIsRecordset(partners1, 'res.partner')
59         self.assertIsRecordset(partners2, 'res.partner')
60         self.assertEqual(list(partners1), list(partners2))
61
62     @mute_logger('openerp.models')
63     def test_03_query_offset_limit(self):
64         """ Build a recordset with offset and limit, and check equivalence. """
65         partners1 = self.env['res.partner'].search([], offset=3, limit=7)
66         partners2 = self.env['res.partner'].search([])[3:10]
67         self.assertIsRecordset(partners1, 'res.partner')
68         self.assertIsRecordset(partners2, 'res.partner')
69         self.assertEqual(list(partners1), list(partners2))
70
71     @mute_logger('openerp.models')
72     def test_05_immutable(self):
73         """ Check that a recordset remains the same, even after updates. """
74         domain = [('name', 'ilike', 'j')]
75         partners = self.env['res.partner'].search(domain)
76         self.assertTrue(partners)
77         ids = map(int, partners)
78
79         # modify those partners, and check that partners has not changed
80         self.registry('res.partner').write(self.cr, self.uid, ids, {'active': False})
81         self.assertEqual(ids, map(int, partners))
82
83         # redo the search, and check that the result is now empty
84         partners2 = self.env['res.partner'].search(domain)
85         self.assertFalse(partners2)
86
87     @mute_logger('openerp.models')
88     def test_06_fields(self):
89         """ Check that relation fields return records, recordsets or nulls. """
90         user = self.registry('res.users').browse(self.cr, self.uid, self.uid)
91         self.assertIsRecord(user, 'res.users')
92         self.assertIsRecord(user.partner_id, 'res.partner')
93         self.assertIsRecordset(user.groups_id, 'res.groups')
94
95         partners = self.env['res.partner'].search([])
96         for name, field in partners._fields.iteritems():
97             if field.type == 'many2one':
98                 for p in partners:
99                     self.assertIsRecord(p[name], field.comodel_name)
100             elif field.type == 'reference':
101                 for p in partners:
102                     if p[name]:
103                         self.assertIsRecord(p[name], field.comodel_name)
104             elif field.type in ('one2many', 'many2many'):
105                 for p in partners:
106                     self.assertIsRecordset(p[name], field.comodel_name)
107
108     @mute_logger('openerp.models')
109     def test_07_null(self):
110         """ Check behavior of null instances. """
111         # select a partner without a parent
112         partner = self.env['res.partner'].search([('parent_id', '=', False)])[0]
113
114         # check partner and related null instances
115         self.assertTrue(partner)
116         self.assertIsRecord(partner, 'res.partner')
117
118         self.assertFalse(partner.parent_id)
119         self.assertIsNull(partner.parent_id, 'res.partner')
120
121         self.assertIs(partner.parent_id.id, False)
122
123         self.assertFalse(partner.parent_id.user_id)
124         self.assertIsNull(partner.parent_id.user_id, 'res.users')
125
126         self.assertIs(partner.parent_id.user_id.name, False)
127
128         self.assertFalse(partner.parent_id.user_id.groups_id)
129         self.assertIsRecordset(partner.parent_id.user_id.groups_id, 'res.groups')
130
131     @mute_logger('openerp.models')
132     def test_10_old_old(self):
133         """ Call old-style methods in the old-fashioned way. """
134         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
135         self.assertTrue(partners)
136         ids = map(int, partners)
137
138         # call method name_get on partners' model, and check its effect
139         res = partners._model.name_get(self.cr, self.uid, ids)
140         self.assertEqual(len(res), len(ids))
141         self.assertEqual(set(val[0] for val in res), set(ids))
142
143     @mute_logger('openerp.models')
144     def test_20_old_new(self):
145         """ Call old-style methods in the new API style. """
146         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
147         self.assertTrue(partners)
148
149         # call method name_get on partners itself, and check its effect
150         res = partners.name_get()
151         self.assertEqual(len(res), len(partners))
152         self.assertEqual(set(val[0] for val in res), set(map(int, partners)))
153
154     @mute_logger('openerp.models')
155     def test_25_old_new(self):
156         """ Call old-style methods on records (new API style). """
157         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
158         self.assertTrue(partners)
159
160         # call method name_get on partner records, and check its effect
161         for p in partners:
162             res = p.name_get()
163             self.assertTrue(isinstance(res, list) and len(res) == 1)
164             self.assertTrue(isinstance(res[0], tuple) and len(res[0]) == 2)
165             self.assertEqual(res[0][0], p.id)
166
167     @mute_logger('openerp.models')
168     def test_30_new_old(self):
169         """ Call new-style methods in the old-fashioned way. """
170         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
171         self.assertTrue(partners)
172         ids = map(int, partners)
173
174         # call method write on partners' model, and check its effect
175         partners._model.write(self.cr, self.uid, ids, {'active': False})
176         for p in partners:
177             self.assertFalse(p.active)
178
179     @mute_logger('openerp.models')
180     def test_40_new_new(self):
181         """ Call new-style methods in the new API style. """
182         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
183         self.assertTrue(partners)
184
185         # call method write on partners itself, and check its effect
186         partners.write({'active': False})
187         for p in partners:
188             self.assertFalse(p.active)
189
190     @mute_logger('openerp.models')
191     def test_45_new_new(self):
192         """ Call new-style methods on records (new API style). """
193         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
194         self.assertTrue(partners)
195
196         # call method write on partner records, and check its effects
197         for p in partners:
198             p.write({'active': False})
199         for p in partners:
200             self.assertFalse(p.active)
201
202     @mute_logger('openerp.models')
203     @mute_logger('openerp.addons.base.ir.ir_model')
204     def test_50_environment(self):
205         """ Test environment on records. """
206         # partners and reachable records are attached to self.env
207         partners = self.env['res.partner'].search([('name', 'ilike', 'j')])
208         self.assertEqual(partners.env, self.env)
209         for x in (partners, partners[0], partners[0].company_id):
210             self.assertEqual(x.env, self.env)
211         for p in partners:
212             self.assertEqual(p.env, self.env)
213
214         # check that the current user can read and modify company data
215         partners[0].company_id.name
216         partners[0].company_id.write({'name': 'Fools'})
217
218         # create an environment with the demo user
219         demo = self.env['res.users'].search([('login', '=', 'demo')])[0]
220         demo_env = self.env(user=demo)
221         self.assertNotEqual(demo_env, self.env)
222
223         # partners and related records are still attached to self.env
224         self.assertEqual(partners.env, self.env)
225         for x in (partners, partners[0], partners[0].company_id):
226             self.assertEqual(x.env, self.env)
227         for p in partners:
228             self.assertEqual(p.env, self.env)
229
230         # create record instances attached to demo_env
231         demo_partners = partners.sudo(demo)
232         self.assertEqual(demo_partners.env, demo_env)
233         for x in (demo_partners, demo_partners[0], demo_partners[0].company_id):
234             self.assertEqual(x.env, demo_env)
235         for p in demo_partners:
236             self.assertEqual(p.env, demo_env)
237
238         # demo user can read but not modify company data
239         demo_partners[0].company_id.name
240         with self.assertRaises(except_orm):
241             demo_partners[0].company_id.write({'name': 'Pricks'})
242
243         # remove demo user from all groups
244         demo.write({'groups_id': [(5,)]})
245
246         # demo user can no longer access partner data
247         with self.assertRaises(except_orm):
248             demo_partners[0].company_id.name
249
250     @mute_logger('openerp.models')
251     def test_55_draft(self):
252         """ Test draft mode nesting. """
253         env = self.env
254         self.assertFalse(env.in_draft)
255         with env.do_in_draft():
256             self.assertTrue(env.in_draft)
257             with env.do_in_draft():
258                 self.assertTrue(env.in_draft)
259                 with env.do_in_draft():
260                     self.assertTrue(env.in_draft)
261                 self.assertTrue(env.in_draft)
262             self.assertTrue(env.in_draft)
263         self.assertFalse(env.in_draft)
264
265     @mute_logger('openerp.models')
266     def test_60_cache(self):
267         """ Check the record cache behavior """
268         partners = self.env['res.partner'].search([('child_ids', '!=', False)])
269         partner1, partner2 = partners[0], partners[1]
270         children1, children2 = partner1.child_ids, partner2.child_ids
271         self.assertTrue(children1)
272         self.assertTrue(children2)
273
274         # take a child contact
275         child = children1[0]
276         self.assertEqual(child.parent_id, partner1)
277         self.assertIn(child, partner1.child_ids)
278         self.assertNotIn(child, partner2.child_ids)
279
280         # fetch data in the cache
281         for p in partners:
282             p.name, p.company_id.name, p.user_id.name, p.contact_address
283         self.env.check_cache()
284
285         # change its parent
286         child.write({'parent_id': partner2.id})
287         self.env.check_cache()
288
289         # check recordsets
290         self.assertEqual(child.parent_id, partner2)
291         self.assertNotIn(child, partner1.child_ids)
292         self.assertIn(child, partner2.child_ids)
293         self.assertEqual(set(partner1.child_ids + child), set(children1))
294         self.assertEqual(set(partner2.child_ids), set(children2 + child))
295         self.env.check_cache()
296
297         # delete it
298         child.unlink()
299         self.env.check_cache()
300
301         # check recordsets
302         self.assertEqual(set(partner1.child_ids), set(children1) - set([child]))
303         self.assertEqual(set(partner2.child_ids), set(children2))
304         self.env.check_cache()
305
306     @mute_logger('openerp.models')
307     def test_60_cache_prefetching(self):
308         """ Check the record cache prefetching """
309         self.env.invalidate_all()
310
311         # all the records of an instance already have an entry in cache
312         partners = self.env['res.partner'].search([])
313         partner_ids = self.env.prefetch['res.partner']
314         self.assertEqual(set(partners.ids), set(partner_ids))
315
316         # countries have not been fetched yet; their cache must be empty
317         countries = self.env['res.country'].browse()
318         self.assertFalse(self.env.prefetch['res.country'])
319
320         # reading ONE partner should fetch them ALL
321         countries |= partners[0].country_id
322         country_cache = self.env.cache[partners._fields['country_id']]
323         self.assertLessEqual(set(partners._ids), set(country_cache))
324
325         # read all partners, and check that the cache already contained them
326         country_ids = list(self.env.prefetch['res.country'])
327         for p in partners:
328             countries |= p.country_id
329         self.assertLessEqual(set(countries.ids), set(country_ids))
330
331     @mute_logger('openerp.models')
332     def test_70_one(self):
333         """ Check method one(). """
334         # check with many records
335         ps = self.env['res.partner'].search([('name', 'ilike', 'a')])
336         self.assertTrue(len(ps) > 1)
337         with self.assertRaises(except_orm):
338             ps.ensure_one()
339
340         p1 = ps[0]
341         self.assertEqual(len(p1), 1)
342         self.assertEqual(p1.ensure_one(), p1)
343
344         p0 = self.env['res.partner'].browse()
345         self.assertEqual(len(p0), 0)
346         with self.assertRaises(except_orm):
347             p0.ensure_one()
348
349     @mute_logger('openerp.models')
350     def test_80_contains(self):
351         """ Test membership on recordset. """
352         p1 = self.env['res.partner'].search([('name', 'ilike', 'a')], limit=1).ensure_one()
353         ps = self.env['res.partner'].search([('name', 'ilike', 'a')])
354         self.assertTrue(p1 in ps)
355
356     @mute_logger('openerp.models')
357     def test_80_set_operations(self):
358         """ Check set operations on recordsets. """
359         pa = self.env['res.partner'].search([('name', 'ilike', 'a')])
360         pb = self.env['res.partner'].search([('name', 'ilike', 'b')])
361         self.assertTrue(pa)
362         self.assertTrue(pb)
363         self.assertTrue(set(pa) & set(pb))
364
365         concat = pa + pb
366         self.assertEqual(list(concat), list(pa) + list(pb))
367         self.assertEqual(len(concat), len(pa) + len(pb))
368
369         difference = pa - pb
370         self.assertEqual(len(difference), len(set(difference)))
371         self.assertEqual(set(difference), set(pa) - set(pb))
372         self.assertLessEqual(difference, pa)
373
374         intersection = pa & pb
375         self.assertEqual(len(intersection), len(set(intersection)))
376         self.assertEqual(set(intersection), set(pa) & set(pb))
377         self.assertLessEqual(intersection, pa)
378         self.assertLessEqual(intersection, pb)
379
380         union = pa | pb
381         self.assertEqual(len(union), len(set(union)))
382         self.assertEqual(set(union), set(pa) | set(pb))
383         self.assertGreaterEqual(union, pa)
384         self.assertGreaterEqual(union, pb)
385
386         # one cannot mix different models with set operations
387         ps = pa
388         ms = self.env['ir.ui.menu'].search([])
389         self.assertNotEqual(ps._name, ms._name)
390         self.assertNotEqual(ps, ms)
391
392         with self.assertRaises(except_orm):
393             res = ps + ms
394         with self.assertRaises(except_orm):
395             res = ps - ms
396         with self.assertRaises(except_orm):
397             res = ps & ms
398         with self.assertRaises(except_orm):
399             res = ps | ms
400         with self.assertRaises(except_orm):
401             res = ps < ms
402         with self.assertRaises(except_orm):
403             res = ps <= ms
404         with self.assertRaises(except_orm):
405             res = ps > ms
406         with self.assertRaises(except_orm):
407             res = ps >= ms
408
409     @mute_logger('openerp.models')
410     def test_80_filter(self):
411         """ Check filter on recordsets. """
412         ps = self.env['res.partner'].search([])
413         customers = ps.browse([p.id for p in ps if p.customer])
414
415         # filter on a single field
416         self.assertEqual(ps.filtered(lambda p: p.customer), customers)
417         self.assertEqual(ps.filtered('customer'), customers)
418
419         # filter on a sequence of fields
420         self.assertEqual(
421             ps.filtered(lambda p: p.parent_id.customer),
422             ps.filtered('parent_id.customer')
423         )
424
425     @mute_logger('openerp.models')
426     def test_80_map(self):
427         """ Check map on recordsets. """
428         ps = self.env['res.partner'].search([])
429         parents = ps.browse()
430         for p in ps: parents |= p.parent_id
431
432         # map a single field
433         self.assertEqual(ps.mapped(lambda p: p.parent_id), parents)
434         self.assertEqual(ps.mapped('parent_id'), parents)
435
436         # map a sequence of fields
437         self.assertEqual(
438             ps.mapped(lambda p: p.parent_id.name),
439             [p.parent_id.name for p in ps]
440         )
441         self.assertEqual(
442             ps.mapped('parent_id.name'),
443             [p.name for p in parents]
444         )