2 # test cases for new-style fields
4 from datetime import date, datetime
5 from collections import defaultdict
7 from openerp.tests import common
8 from openerp.exceptions import except_orm
11 class TestNewFields(common.TransactionCase):
13 def test_00_basics(self):
14 """ test accessing new fields """
16 discussion = self.env.ref('test_new_api.discussion_0')
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)
23 # read it with method read()
24 values = discussion.read(['name'])[0]
25 self.assertEqual(values['name'], discussion.name)
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)
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):
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)
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'
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)
60 field = self.env['test_new_api.message']._fields['name']
61 self.assertTrue(field.store)
62 self.assertTrue(field.readonly)
64 def test_10_non_stored(self):
65 """ test non-stored fields """
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 ''))
71 # check recomputation after record is modified
73 message.write({'body': (message.body or '') + "!!!"})
74 self.assertEqual(message.size, size + 3)
76 # special case: computed field without dependency must be computed
77 record = self.env['test_new_api.mixed'].create({})
78 self.assertTrue(record.now)
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)
87 name0 = discussion.name or ""
88 for message in discussion.messages:
89 self.assertEqual(message.name, "[%s] %s" % (name0, message.author.name))
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))
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))
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'})
115 cath.parent = finn.parent = gabe
116 abel.parent = beth.parent = cath
117 dean.parent = ewan.parent = finn
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")
128 self.assertEqual(ewan.display_name, "Gabriel / Catherine / Ewan")
131 self.assertEqual(ewan.display_name, "Gabriel / Finnley / Catherine / Ewan")
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)
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")
152 ewan.display_name = "Abel / Bethany / Catherine / Erwan"
154 self.assertEqual(beth.parent, abel)
155 self.assertEqual(cath.parent, beth)
156 self.assertEqual(ewan.parent, cath)
157 self.assertEqual(ewan.name, "Erwan")
159 def test_14_search(self):
160 """ test search on computed fields """
161 discussion = self.env.ref('test_new_api.discussion_0')
163 # determine message sizes
164 sizes = set(message.size for message in discussion.messages)
166 # search for messages based on their size
168 messages0 = self.env['test_new_api.message'].search(
169 [('discussion', '=', discussion.id), ('size', '<=', size)])
171 messages1 = self.env['test_new_api.message'].browse()
172 for message in discussion.messages:
173 if message.size <= size:
176 self.assertEqual(messages0, messages1)
178 def test_15_constraint(self):
179 """ test new-style Python constraints """
180 discussion = self.env.ref('test_new_api.discussion_0')
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'})
188 # make sure that assertRaises() does not leave fields to recompute
189 self.assertFalse(self.env.has_todo())
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'})
196 def test_20_float(self):
197 """ test float fields """
198 record = self.env['test_new_api.mixed'].create({})
200 # assign value, and expect rounding
201 record.write({'number': 2.4999999999999996})
202 self.assertEqual(record.number, 2.50)
204 # same with field setter
205 record.number = 2.4999999999999996
206 self.assertEqual(record.number, 2.50)
208 def test_21_date(self):
209 """ test date fields """
210 record = self.env['test_new_api.mixed'].create({})
212 # one may assign False or None
214 self.assertFalse(record.date)
216 # one may assign date and datetime objects
217 record.date = date(2012, 05, 01)
218 self.assertEqual(record.date, '2012-05-01')
220 record.date = datetime(2012, 05, 01, 10, 45, 00)
221 self.assertEqual(record.date, '2012-05-01')
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')
227 with self.assertRaises(ValueError):
228 record.date = '12-5-1'
230 def test_22_selection(self):
231 """ test selection fields """
232 record = self.env['test_new_api.mixed'].create({})
234 # one may assign False or None
236 self.assertFalse(record.lang)
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'
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')
249 # check environment of record and related records
250 self.assertEqual(message.env, self.env)
251 self.assertEqual(message.discussion.env, self.env)
253 demo_env = self.env(user=demo)
254 self.assertNotEqual(demo_env, self.env)
256 # check environment of record and related records
257 self.assertEqual(message.env, self.env)
258 self.assertEqual(message.discussion.env, self.env)
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)
265 # assign record's parent to a record in demo_env
266 message.discussion = message.discussion.copy({'name': 'Copy'})
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)
272 def test_24_reference(self):
273 """ test reference fields. """
274 record = self.env['test_new_api.mixed'].create({})
276 # one may assign False or None
277 record.reference = None
278 self.assertFalse(record.reference)
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)
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
294 # by default related fields are not stored
295 field = message._fields['discussion_name']
296 self.assertFalse(field.store)
297 self.assertTrue(field.readonly)
299 # check value of related field
300 self.assertEqual(message.discussion_name, discussion.name)
302 # change discussion name, and check result
303 discussion.name = 'Foo'
304 self.assertEqual(message.discussion_name, 'Foo')
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')
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)
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'])
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])
330 def test_30_read(self):
331 """ test computed fields as returned by read(). """
332 discussion = self.env.ref('test_new_api.discussion_0')
334 for message in discussion.messages:
335 display_name = message.display_name
338 data = message.read(['display_name', 'size'])[0]
339 self.assertEqual(data['display_name'], display_name)
340 self.assertEqual(data['size'], size)
342 def test_40_new(self):
343 """ test new records. """
344 discussion = self.env.ref('test_new_api.discussion_0')
346 # create a new message
347 message = self.env['test_new_api.message'].new()
348 self.assertFalse(message.id)
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)
358 # check computed values of fields
359 self.assertEqual(message.name, "[%s] %s" % (discussion.name, ''))
360 self.assertEqual(message.size, len(BODY))
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})
368 defaults = self.env['test_new_api.mixed'].default_get(['number'])
369 self.assertEqual(defaults, {'number': 3.14})
372 class TestMagicFields(common.TransactionCase):
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)
380 class TestInherits(common.TransactionCase):
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'))
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)