[FIX] tests: make sure that a failed tests does not leave the environment dirty
[odoo/odoo.git] / openerp / addons / test_new_api / tests / test_new_fields.py
1 #
2 # test cases for new-style fields
3 #
4 from datetime import date, datetime
5 from collections import defaultdict
6
7 from openerp.tests import common
8 from openerp.exceptions import except_orm
9
10
11 class TestNewFields(common.TransactionCase):
12
13     def test_00_basics(self):
14         """ test accessing new fields """
15         # find a discussion
16         discussion = self.env.ref('test_new_api.discussion_0')
17
18         # read field as a record attribute or as a record item
19         self.assertIsInstance(discussion.name, basestring)
20         self.assertIsInstance(discussion['name'], basestring)
21         self.assertEqual(discussion['name'], discussion.name)
22
23         # read it with method read()
24         values = discussion.read(['name'])[0]
25         self.assertEqual(values['name'], discussion.name)
26
27     def test_01_basic_get_assertion(self):
28         """ test item getter """
29         # field access works on single record
30         record = self.env.ref('test_new_api.message_0_0')
31         self.assertEqual(len(record), 1)
32         ok = record.body
33
34         # field access fails on multiple records
35         records = self.env['test_new_api.message'].search([])
36         assert len(records) > 1
37         with self.assertRaises(except_orm):
38             faulty = records.body
39
40     def test_01_basic_set_assertion(self):
41         """ test item setter """
42         # field assignment works on single record
43         record = self.env.ref('test_new_api.message_0_0')
44         self.assertEqual(len(record), 1)
45         record.body = 'OK'
46
47         # field assignment fails on multiple records
48         records = self.env['test_new_api.message'].search([])
49         assert len(records) > 1
50         with self.assertRaises(except_orm):
51             records.body = 'Faulty'
52
53     def test_10_computed(self):
54         """ check definition of computed fields """
55         # by default function fields are not stored and readonly
56         field = self.env['test_new_api.message']._fields['size']
57         self.assertFalse(field.store)
58         self.assertTrue(field.readonly)
59
60         field = self.env['test_new_api.message']._fields['name']
61         self.assertTrue(field.store)
62         self.assertTrue(field.readonly)
63
64     def test_10_non_stored(self):
65         """ test non-stored fields """
66         # find messages
67         for message in self.env['test_new_api.message'].search([]):
68             # check definition of field
69             self.assertEqual(message.size, len(message.body or ''))
70
71             # check recomputation after record is modified
72             size = message.size
73             message.write({'body': (message.body or '') + "!!!"})
74             self.assertEqual(message.size, size + 3)
75
76         # special case: computed field without dependency must be computed
77         record = self.env['test_new_api.mixed'].create({})
78         self.assertTrue(record.now)
79
80     def test_11_stored(self):
81         """ test stored fields """
82         # find the demo discussion
83         discussion = self.env.ref('test_new_api.discussion_0')
84         self.assertTrue(len(discussion.messages) > 0)
85
86         # check messages
87         name0 = discussion.name or ""
88         for message in discussion.messages:
89             self.assertEqual(message.name, "[%s] %s" % (name0, message.author.name))
90
91         # modify discussion name, and check again messages
92         discussion.name = name1 = 'Talking about stuff...'
93         for message in discussion.messages:
94             self.assertEqual(message.name, "[%s] %s" % (name1, message.author.name))
95
96         # switch message from discussion, and check again
97         name2 = 'Another discussion'
98         discussion2 = discussion.copy({'name': name2})
99         message2 = discussion.messages[0]
100         message2.discussion = discussion2
101         for message in discussion2.messages:
102             self.assertEqual(message.name, "[%s] %s" % (name2, message.author.name))
103
104     def test_12_recursive(self):
105         """ test recursively dependent fields """
106         Category = self.env['test_new_api.category']
107         abel = Category.create({'name': 'Abel'})
108         beth = Category.create({'name': 'Bethany'})
109         cath = Category.create({'name': 'Catherine'})
110         dean = Category.create({'name': 'Dean'})
111         ewan = Category.create({'name': 'Ewan'})
112         finn = Category.create({'name': 'Finnley'})
113         gabe = Category.create({'name': 'Gabriel'})
114
115         cath.parent = finn.parent = gabe
116         abel.parent = beth.parent = cath
117         dean.parent = ewan.parent = finn
118
119         self.assertEqual(abel.display_name, "Gabriel / Catherine / Abel")
120         self.assertEqual(beth.display_name, "Gabriel / Catherine / Bethany")
121         self.assertEqual(cath.display_name, "Gabriel / Catherine")
122         self.assertEqual(dean.display_name, "Gabriel / Finnley / Dean")
123         self.assertEqual(ewan.display_name, "Gabriel / Finnley / Ewan")
124         self.assertEqual(finn.display_name, "Gabriel / Finnley")
125         self.assertEqual(gabe.display_name, "Gabriel")
126
127         ewan.parent = cath
128         self.assertEqual(ewan.display_name, "Gabriel / Catherine / Ewan")
129
130         cath.parent = finn
131         self.assertEqual(ewan.display_name, "Gabriel / Finnley / Catherine / Ewan")
132
133     def test_12_cascade(self):
134         """ test computed field depending on computed field """
135         message = self.env.ref('test_new_api.message_0_0')
136         message.invalidate_cache()
137         double_size = message.double_size
138         self.assertEqual(double_size, message.size)
139
140     def test_13_inverse(self):
141         """ test inverse computation of fields """
142         Category = self.env['test_new_api.category']
143         abel = Category.create({'name': 'Abel'})
144         beth = Category.create({'name': 'Bethany'})
145         cath = Category.create({'name': 'Catherine'})
146         dean = Category.create({'name': 'Dean'})
147         ewan = Category.create({'name': 'Ewan'})
148         finn = Category.create({'name': 'Finnley'})
149         gabe = Category.create({'name': 'Gabriel'})
150         self.assertEqual(ewan.display_name, "Ewan")
151
152         ewan.display_name = "Abel / Bethany / Catherine / Erwan"
153
154         self.assertEqual(beth.parent, abel)
155         self.assertEqual(cath.parent, beth)
156         self.assertEqual(ewan.parent, cath)
157         self.assertEqual(ewan.name, "Erwan")
158
159     def test_14_search(self):
160         """ test search on computed fields """
161         discussion = self.env.ref('test_new_api.discussion_0')
162
163         # determine message sizes
164         sizes = set(message.size for message in discussion.messages)
165
166         # search for messages based on their size
167         for size in sizes:
168             messages0 = self.env['test_new_api.message'].search(
169                 [('discussion', '=', discussion.id), ('size', '<=', size)])
170
171             messages1 = self.env['test_new_api.message'].browse()
172             for message in discussion.messages:
173                 if message.size <= size:
174                     messages1 += message
175
176             self.assertEqual(messages0, messages1)
177
178     def test_15_constraint(self):
179         """ test new-style Python constraints """
180         discussion = self.env.ref('test_new_api.discussion_0')
181
182         # remove oneself from discussion participants: we can no longer create
183         # messages in discussion
184         discussion.participants -= self.env.user
185         with self.assertRaises(Exception):
186             self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'})
187
188         # make sure that assertRaises() does not leave fields to recompute
189         self.assertFalse(self.env.has_todo())
190
191         # put back oneself into discussion participants: now we can create
192         # messages in discussion
193         discussion.participants += self.env.user
194         self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'})
195
196     def test_20_float(self):
197         """ test float fields """
198         record = self.env['test_new_api.mixed'].create({})
199
200         # assign value, and expect rounding
201         record.write({'number': 2.4999999999999996})
202         self.assertEqual(record.number, 2.50)
203
204         # same with field setter
205         record.number = 2.4999999999999996
206         self.assertEqual(record.number, 2.50)
207
208     def test_21_date(self):
209         """ test date fields """
210         record = self.env['test_new_api.mixed'].create({})
211
212         # one may assign False or None
213         record.date = None
214         self.assertFalse(record.date)
215
216         # one may assign date and datetime objects
217         record.date = date(2012, 05, 01)
218         self.assertEqual(record.date, '2012-05-01')
219
220         record.date = datetime(2012, 05, 01, 10, 45, 00)
221         self.assertEqual(record.date, '2012-05-01')
222
223         # one may assign dates in the default format, and it must be checked
224         record.date = '2012-05-01'
225         self.assertEqual(record.date, '2012-05-01')
226
227         with self.assertRaises(ValueError):
228             record.date = '12-5-1'
229
230     def test_22_selection(self):
231         """ test selection fields """
232         record = self.env['test_new_api.mixed'].create({})
233
234         # one may assign False or None
235         record.lang = None
236         self.assertFalse(record.lang)
237
238         # one may assign a value, and it must be checked
239         for language in self.env['res.lang'].search([]):
240             record.lang = language.code
241         with self.assertRaises(ValueError):
242             record.lang = 'zz_ZZ'
243
244     def test_23_relation(self):
245         """ test relation fields """
246         demo = self.env.ref('base.user_demo')
247         message = self.env.ref('test_new_api.message_0_0')
248
249         # check environment of record and related records
250         self.assertEqual(message.env, self.env)
251         self.assertEqual(message.discussion.env, self.env)
252
253         demo_env = self.env(user=demo)
254         self.assertNotEqual(demo_env, self.env)
255
256         # check environment of record and related records
257         self.assertEqual(message.env, self.env)
258         self.assertEqual(message.discussion.env, self.env)
259
260         # "migrate" message into demo_env, and check again
261         demo_message = message.sudo(demo)
262         self.assertEqual(demo_message.env, demo_env)
263         self.assertEqual(demo_message.discussion.env, demo_env)
264
265         # assign record's parent to a record in demo_env
266         message.discussion = message.discussion.copy({'name': 'Copy'})
267
268         # both message and its parent field must be in self.env
269         self.assertEqual(message.env, self.env)
270         self.assertEqual(message.discussion.env, self.env)
271
272     def test_24_reference(self):
273         """ test reference fields. """
274         record = self.env['test_new_api.mixed'].create({})
275
276         # one may assign False or None
277         record.reference = None
278         self.assertFalse(record.reference)
279
280         # one may assign a user or a partner...
281         record.reference = self.env.user
282         self.assertEqual(record.reference, self.env.user)
283         record.reference = self.env.user.partner_id
284         self.assertEqual(record.reference, self.env.user.partner_id)
285         # ... but no record from a model that starts with 'ir.'
286         with self.assertRaises(ValueError):
287             record.reference = self.env['ir.model'].search([], limit=1)
288
289     def test_25_related(self):
290         """ test related fields. """
291         message = self.env.ref('test_new_api.message_0_0')
292         discussion = message.discussion
293
294         # by default related fields are not stored
295         field = message._fields['discussion_name']
296         self.assertFalse(field.store)
297         self.assertTrue(field.readonly)
298
299         # check value of related field
300         self.assertEqual(message.discussion_name, discussion.name)
301
302         # change discussion name, and check result
303         discussion.name = 'Foo'
304         self.assertEqual(message.discussion_name, 'Foo')
305
306         # change discussion name via related field, and check result
307         message.discussion_name = 'Bar'
308         self.assertEqual(discussion.name, 'Bar')
309         self.assertEqual(message.discussion_name, 'Bar')
310
311         # search on related field, and check result
312         search_on_related = self.env['test_new_api.message'].search([('discussion_name', '=', 'Bar')])
313         search_on_regular = self.env['test_new_api.message'].search([('discussion.name', '=', 'Bar')])
314         self.assertEqual(search_on_related, search_on_regular)
315
316         # check that field attributes are copied
317         message_field = message.fields_get(['discussion_name'])['discussion_name']
318         discussion_field = discussion.fields_get(['name'])['name']
319         self.assertEqual(message_field['help'], discussion_field['help'])
320
321     def test_26_inherited(self):
322         """ test inherited fields. """
323         # a bunch of fields are inherited from res_partner
324         for user in self.env['res.users'].search([]):
325             partner = user.partner_id
326             for field in ('is_company', 'name', 'email', 'country_id'):
327                 self.assertEqual(getattr(user, field), getattr(partner, field))
328                 self.assertEqual(user[field], partner[field])
329
330     def test_30_read(self):
331         """ test computed fields as returned by read(). """
332         discussion = self.env.ref('test_new_api.discussion_0')
333
334         for message in discussion.messages:
335             display_name = message.display_name
336             size = message.size
337
338             data = message.read(['display_name', 'size'])[0]
339             self.assertEqual(data['display_name'], display_name)
340             self.assertEqual(data['size'], size)
341
342     def test_40_new(self):
343         """ test new records. """
344         discussion = self.env.ref('test_new_api.discussion_0')
345
346         # create a new message
347         message = self.env['test_new_api.message'].new()
348         self.assertFalse(message.id)
349
350         # assign some fields; should have no side effect
351         message.discussion = discussion
352         message.body = BODY = "May the Force be with you."
353         self.assertEqual(message.discussion, discussion)
354         self.assertEqual(message.body, BODY)
355         self.assertFalse(message.author)
356         self.assertNotIn(message, discussion.messages)
357
358         # check computed values of fields
359         self.assertEqual(message.name, "[%s] %s" % (discussion.name, ''))
360         self.assertEqual(message.size, len(BODY))
361
362     def test_41_defaults(self):
363         """ test default values. """
364         fields = ['discussion', 'body', 'author', 'size']
365         defaults = self.env['test_new_api.message'].default_get(fields)
366         self.assertEqual(defaults, {'author': self.env.uid})
367
368         defaults = self.env['test_new_api.mixed'].default_get(['number'])
369         self.assertEqual(defaults, {'number': 3.14})
370
371
372 class TestMagicFields(common.TransactionCase):
373
374     def test_write_date(self):
375         record = self.env['test_new_api.discussion'].create({'name': 'Booba'})
376         self.assertEqual(record.create_uid, self.env.user)
377         self.assertEqual(record.write_uid, self.env.user)
378
379
380 class TestInherits(common.TransactionCase):
381
382     def test_inherits(self):
383         """ Check that a many2one field with delegate=True adds an entry in _inherits """
384         Talk = self.env['test_new_api.talk']
385         self.assertEqual(Talk._inherits, {'test_new_api.discussion': 'parent'})
386         self.assertIn('name', Talk._fields)
387         self.assertEqual(Talk._fields['name'].related, ('parent', 'name'))
388
389         talk = Talk.create({'name': 'Foo'})
390         discussion = talk.parent
391         self.assertTrue(discussion)
392         self.assertEqual(talk._name, 'test_new_api.talk')
393         self.assertEqual(discussion._name, 'test_new_api.discussion')
394         self.assertEqual(talk.name, discussion.name)