[IMP] models: do not use compute methods to determine default values anymore
[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         # put back oneself into discussion participants: now we can create
189         # messages in discussion
190         discussion.participants += self.env.user
191         self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'})
192
193     def test_20_float(self):
194         """ test float fields """
195         record = self.env['test_new_api.mixed'].create({})
196
197         # assign value, and expect rounding
198         record.write({'number': 2.4999999999999996})
199         self.assertEqual(record.number, 2.50)
200
201         # same with field setter
202         record.number = 2.4999999999999996
203         self.assertEqual(record.number, 2.50)
204
205     def test_21_date(self):
206         """ test date fields """
207         record = self.env['test_new_api.mixed'].create({})
208
209         # one may assign False or None
210         record.date = None
211         self.assertFalse(record.date)
212
213         # one may assign date and datetime objects
214         record.date = date(2012, 05, 01)
215         self.assertEqual(record.date, '2012-05-01')
216
217         record.date = datetime(2012, 05, 01, 10, 45, 00)
218         self.assertEqual(record.date, '2012-05-01')
219
220         # one may assign dates in the default format, and it must be checked
221         record.date = '2012-05-01'
222         self.assertEqual(record.date, '2012-05-01')
223
224         with self.assertRaises(ValueError):
225             record.date = '12-5-1'
226
227     def test_22_selection(self):
228         """ test selection fields """
229         record = self.env['test_new_api.mixed'].create({})
230
231         # one may assign False or None
232         record.lang = None
233         self.assertFalse(record.lang)
234
235         # one may assign a value, and it must be checked
236         for language in self.env['res.lang'].search([]):
237             record.lang = language.code
238         with self.assertRaises(ValueError):
239             record.lang = 'zz_ZZ'
240
241     def test_23_relation(self):
242         """ test relation fields """
243         demo = self.env.ref('base.user_demo')
244         message = self.env.ref('test_new_api.message_0_0')
245
246         # check environment of record and related records
247         self.assertEqual(message.env, self.env)
248         self.assertEqual(message.discussion.env, self.env)
249
250         demo_env = self.env(user=demo)
251         self.assertNotEqual(demo_env, self.env)
252
253         # check environment of record and related records
254         self.assertEqual(message.env, self.env)
255         self.assertEqual(message.discussion.env, self.env)
256
257         # "migrate" message into demo_env, and check again
258         demo_message = message.sudo(demo)
259         self.assertEqual(demo_message.env, demo_env)
260         self.assertEqual(demo_message.discussion.env, demo_env)
261
262         # assign record's parent to a record in demo_env
263         message.discussion = message.discussion.copy({'name': 'Copy'})
264
265         # both message and its parent field must be in self.env
266         self.assertEqual(message.env, self.env)
267         self.assertEqual(message.discussion.env, self.env)
268
269     def test_24_reference(self):
270         """ test reference fields. """
271         record = self.env['test_new_api.mixed'].create({})
272
273         # one may assign False or None
274         record.reference = None
275         self.assertFalse(record.reference)
276
277         # one may assign a user or a partner...
278         record.reference = self.env.user
279         self.assertEqual(record.reference, self.env.user)
280         record.reference = self.env.user.partner_id
281         self.assertEqual(record.reference, self.env.user.partner_id)
282         # ... but no record from a model that starts with 'ir.'
283         with self.assertRaises(ValueError):
284             record.reference = self.env['ir.model'].search([], limit=1)
285
286     def test_25_related(self):
287         """ test related fields. """
288         message = self.env.ref('test_new_api.message_0_0')
289         discussion = message.discussion
290
291         # by default related fields are not stored
292         field = message._fields['discussion_name']
293         self.assertFalse(field.store)
294         self.assertTrue(field.readonly)
295
296         # check value of related field
297         self.assertEqual(message.discussion_name, discussion.name)
298
299         # change discussion name, and check result
300         discussion.name = 'Foo'
301         self.assertEqual(message.discussion_name, 'Foo')
302
303         # change discussion name via related field, and check result
304         message.discussion_name = 'Bar'
305         self.assertEqual(discussion.name, 'Bar')
306         self.assertEqual(message.discussion_name, 'Bar')
307
308         # search on related field, and check result
309         search_on_related = self.env['test_new_api.message'].search([('discussion_name', '=', 'Bar')])
310         search_on_regular = self.env['test_new_api.message'].search([('discussion.name', '=', 'Bar')])
311         self.assertEqual(search_on_related, search_on_regular)
312
313         # check that field attributes are copied
314         message_field = message.fields_get(['discussion_name'])['discussion_name']
315         discussion_field = discussion.fields_get(['name'])['name']
316         self.assertEqual(message_field['help'], discussion_field['help'])
317
318     def test_26_inherited(self):
319         """ test inherited fields. """
320         # a bunch of fields are inherited from res_partner
321         for user in self.env['res.users'].search([]):
322             partner = user.partner_id
323             for field in ('is_company', 'name', 'email', 'country_id'):
324                 self.assertEqual(getattr(user, field), getattr(partner, field))
325                 self.assertEqual(user[field], partner[field])
326
327     def test_30_read(self):
328         """ test computed fields as returned by read(). """
329         discussion = self.env.ref('test_new_api.discussion_0')
330
331         for message in discussion.messages:
332             display_name = message.display_name
333             size = message.size
334
335             data = message.read(['display_name', 'size'])[0]
336             self.assertEqual(data['display_name'], display_name)
337             self.assertEqual(data['size'], size)
338
339     def test_40_new(self):
340         """ test new records. """
341         discussion = self.env.ref('test_new_api.discussion_0')
342
343         # create a new message
344         message = self.env['test_new_api.message'].new()
345         self.assertFalse(message.id)
346
347         # assign some fields; should have no side effect
348         message.discussion = discussion
349         message.body = BODY = "May the Force be with you."
350         self.assertEqual(message.discussion, discussion)
351         self.assertEqual(message.body, BODY)
352         self.assertFalse(message.author)
353         self.assertNotIn(message, discussion.messages)
354
355         # check computed values of fields
356         self.assertEqual(message.name, "[%s] %s" % (discussion.name, ''))
357         self.assertEqual(message.size, len(BODY))
358
359     def test_41_defaults(self):
360         """ test default values. """
361         fields = ['discussion', 'body', 'author', 'size']
362         defaults = self.env['test_new_api.message'].default_get(fields)
363         self.assertEqual(defaults, {'author': self.env.uid})
364
365         defaults = self.env['test_new_api.mixed'].default_get(['number'])
366         self.assertEqual(defaults, {'number': 3.14})
367
368
369 class TestMagicFields(common.TransactionCase):
370
371     def test_write_date(self):
372         record = self.env['test_new_api.discussion'].create({'name': 'Booba'})
373         self.assertEqual(record.create_uid, self.env.user)
374         self.assertEqual(record.write_uid, self.env.user)
375
376
377 class TestInherits(common.TransactionCase):
378
379     def test_inherits(self):
380         """ Check that a many2one field with delegate=True adds an entry in _inherits """
381         Talk = self.env['test_new_api.talk']
382         self.assertEqual(Talk._inherits, {'test_new_api.discussion': 'parent'})
383         self.assertIn('name', Talk._fields)
384         self.assertEqual(Talk._fields['name'].related, ('parent', 'name'))
385
386         talk = Talk.create({'name': 'Foo'})
387         discussion = talk.parent
388         self.assertTrue(discussion)
389         self.assertEqual(talk._name, 'test_new_api.talk')
390         self.assertEqual(discussion._name, 'test_new_api.discussion')
391         self.assertEqual(talk.name, discussion.name)