Launchpad automatic translations update.
[odoo/odoo.git] / addons / base_import / tests / test_cases.py
1 # -*- encoding: utf-8 -*-
2 import unittest2
3 from openerp.tests.common import TransactionCase
4
5 from .. import models
6
7 ID_FIELD = {'id': 'id', 'name': 'id', 'string': "External ID", 'required': False, 'fields': []}
8 def make_field(name='value', string='unknown', required=False, fields=[]):
9     return [
10         ID_FIELD,
11         {'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields},
12     ]
13
14 class test_basic_fields(TransactionCase):
15     def get_fields(self, field):
16         return self.registry('base_import.import')\
17             .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
18
19     def test_base(self):
20         """ A basic field is not required """
21         self.assertEqual(self.get_fields('char'), make_field())
22
23     def test_required(self):
24         """ Required fields should be flagged (so they can be fill-required) """
25         self.assertEqual(self.get_fields('char.required'), make_field(required=True))
26
27     def test_readonly(self):
28         """ Readonly fields should be filtered out"""
29         self.assertEqual(self.get_fields('char.readonly'), [ID_FIELD])
30
31     def test_readonly_states(self):
32         """ Readonly fields with states should not be filtered out"""
33         self.assertEqual(self.get_fields('char.states'), make_field())
34
35     def test_readonly_states_noreadonly(self):
36         """ Readonly fields with states having nothing to do with
37         readonly should still be filtered out"""
38         self.assertEqual(self.get_fields('char.noreadonly'), [ID_FIELD])
39
40     def test_readonly_states_stillreadonly(self):
41         """ Readonly fields with readonly states leaving them readonly
42         always... filtered out"""
43         self.assertEqual(self.get_fields('char.stillreadonly'), [ID_FIELD])
44
45     def test_m2o(self):
46         """ M2O fields should allow import of themselves (name_get),
47         their id and their xid"""
48         self.assertEqual(self.get_fields('m2o'), make_field(fields=[
49             {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
50             {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
51         ]))
52     
53     def test_m2o_required(self):
54         """ If an m2o field is required, its three sub-fields are
55         required as well (the client has to handle that: requiredness
56         is id-based)
57         """
58         self.assertEqual(self.get_fields('m2o.required'), make_field(required=True, fields=[
59             {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []},
60             {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []},
61         ]))
62
63 class test_o2m(TransactionCase):
64     def get_fields(self, field):
65         return self.registry('base_import.import')\
66             .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
67
68     def test_shallow(self):
69         self.assertEqual(self.get_fields('o2m'), make_field(fields=[
70             {'id': 'id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
71             # FIXME: should reverse field be ignored?
72             {'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [
73                 {'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
74                 {'id': 'parent_id', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
75             ]},
76             {'id': 'value', 'name': 'value', 'string': 'unknown', 'required': False, 'fields': []},
77         ]))
78
79 class test_match_headers_single(TransactionCase):
80     def test_match_by_name(self):
81         match = self.registry('base_import.import')._match_header(
82             'f0', [{'name': 'f0'}], {})
83
84         self.assertEqual(match, [{'name': 'f0'}])
85
86     def test_match_by_string(self):
87         match = self.registry('base_import.import')._match_header(
88             'some field', [{'name': 'bob', 'string': "Some Field"}], {})
89
90         self.assertEqual(match, [{'name': 'bob', 'string': "Some Field"}])
91
92     def test_nomatch(self):
93         match = self.registry('base_import.import')._match_header(
94             'should not be', [{'name': 'bob', 'string': "wheee"}], {})
95
96         self.assertEqual(match, [])
97
98     def test_recursive_match(self):
99         f = {
100             'name': 'f0',
101             'string': "My Field",
102             'fields': [
103                 {'name': 'f0', 'string': "Sub field 0", 'fields': []},
104                 {'name': 'f1', 'string': "Sub field 2", 'fields': []},
105             ]
106         }
107         match = self.registry('base_import.import')._match_header(
108             'f0/f1', [f], {})
109
110         self.assertEqual(match, [f, f['fields'][1]])
111
112     def test_recursive_nomatch(self):
113         """ Match first level, fail to match second level
114         """
115         f = {
116             'name': 'f0',
117             'string': "My Field",
118             'fields': [
119                 {'name': 'f0', 'string': "Sub field 0", 'fields': []},
120                 {'name': 'f1', 'string': "Sub field 2", 'fields': []},
121             ]
122         }
123         match = self.registry('base_import.import')._match_header(
124             'f0/f2', [f], {})
125
126         self.assertEqual(match, [])
127
128 class test_match_headers_multiple(TransactionCase):
129     def test_noheaders(self):
130         self.assertEqual(
131             self.registry('base_import.import')._match_headers(
132                 [], [], {}),
133             (None, None)
134         )
135     def test_nomatch(self):
136         self.assertEqual(
137             self.registry('base_import.import')._match_headers(
138                 iter([
139                     ['foo', 'bar', 'baz', 'qux'],
140                     ['v1', 'v2', 'v3', 'v4'],
141                 ]),
142                 [],
143                 {'headers': True}),
144             (
145                 ['foo', 'bar', 'baz', 'qux'],
146                 dict.fromkeys(range(4))
147             )
148         )
149
150     def test_mixed(self):
151         self.assertEqual(
152             self.registry('base_import.import')._match_headers(
153                 iter(['foo bar baz qux/corge'.split()]),
154                 [
155                     {'name': 'bar', 'string': 'Bar'},
156                     {'name': 'bob', 'string': 'Baz'},
157                     {'name': 'qux', 'string': 'Qux', 'fields': [
158                         {'name': 'corge', 'fields': []},
159                      ]}
160                 ],
161                 {'headers': True}),
162             (['foo', 'bar', 'baz', 'qux/corge'], {
163                 0: None,
164                 1: ['bar'],
165                 2: ['bob'],
166                 3: ['qux', 'corge'],
167             })
168         )
169
170 class test_preview(TransactionCase):
171     def make_import(self):
172         Import = self.registry('base_import.import')
173         id = Import.create(self.cr, self.uid, {
174             'res_model': 'res.users',
175             'file': u"로그인,언어\nbob,1\n".encode('euc_kr'),
176         })
177         return Import, id
178
179     def test_encoding(self):
180         Import, id = self.make_import()
181         result = Import.parse_preview(self.cr, self.uid, id, {
182                 'quoting': '"',
183                 'separator': ',',
184         })
185         self.assertTrue('error' in result)
186
187     def test_csv_errors(self):
188         Import, id = self.make_import()
189
190         result = Import.parse_preview(self.cr, self.uid, id, {
191                 'quoting': 'foo',
192                 'separator': ',',
193                 'encoding': 'euc_kr',
194         })
195         self.assertTrue('error' in result)
196
197     def test_csv_errors(self):
198         Import, id = self.make_import()
199
200         result = Import.parse_preview(self.cr, self.uid, id, {
201                 'quoting': '"',
202                 'separator': 'bob',
203                 'encoding': 'euc_kr',
204         })
205         self.assertTrue('error' in result)
206
207     def test_success(self):
208         Import = self.registry('base_import.import')
209         id = Import.create(self.cr, self.uid, {
210             'res_model': 'base_import.tests.models.preview',
211             'file': 'name,Some Value,Counter\n'
212                     'foo,1,2\n'
213                     'bar,3,4\n'
214                     'qux,5,6\n'
215         })
216
217         result = Import.parse_preview(self.cr, self.uid, id, {
218             'quoting': '"',
219             'separator': ',',
220             'headers': True,
221         })
222
223         self.assertEqual(result['matches'], {0: ['name'], 1: ['somevalue'], 2: None})
224         self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter'])
225         # Order depends on iteration order of fields_get
226         self.assertItemsEqual(result['fields'], [
227             {'id': 'id', 'name': 'id', 'string': 'External ID', 'required':False, 'fields': []},
228             {'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []},
229             {'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []},
230             {'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []},
231         ])
232         self.assertEqual(result['preview'], [
233             ['foo', '1', '2'],
234             ['bar', '3', '4'],
235             ['qux', '5', '6'],
236         ])
237         # Ensure we only have the response fields we expect
238         self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview'])
239
240 class test_convert_import_data(TransactionCase):
241     """ Tests conversion of base_import.import input into data which
242     can be fed to Model.import_data
243     """
244     def test_all(self):
245         Import = self.registry('base_import.import')
246         id = Import.create(self.cr, self.uid, {
247             'res_model': 'base_import.tests.models.preview',
248             'file': 'name,Some Value,Counter\n'
249                     'foo,1,2\n'
250                     'bar,3,4\n'
251                     'qux,5,6\n'
252         })
253         record = Import.browse(self.cr, self.uid, id)
254         data, fields = Import._convert_import_data(
255             record, ['name', 'somevalue', 'othervalue'],
256             {'quoting': '"', 'separator': ',', 'headers': True,})
257
258         self.assertItemsEqual(fields, ['name', 'somevalue', 'othervalue'])
259         self.assertItemsEqual(data, [
260             ('foo', '1', '2'),
261             ('bar', '3', '4'),
262             ('qux', '5', '6'),
263         ])
264
265     def test_filtered(self):
266         """ If ``False`` is provided as field mapping for a column,
267         that column should be removed from importable data
268         """
269         Import = self.registry('base_import.import')
270         id = Import.create(self.cr, self.uid, {
271             'res_model': 'base_import.tests.models.preview',
272             'file': 'name,Some Value,Counter\n'
273                     'foo,1,2\n'
274                     'bar,3,4\n'
275                     'qux,5,6\n'
276         })
277         record = Import.browse(self.cr, self.uid, id)
278         data, fields = Import._convert_import_data(
279             record, ['name', False, 'othervalue'],
280             {'quoting': '"', 'separator': ',', 'headers': True,})
281
282         self.assertItemsEqual(fields, ['name', 'othervalue'])
283         self.assertItemsEqual(data, [
284             ('foo', '2'),
285             ('bar', '4'),
286             ('qux', '6'),
287         ])
288
289     def test_norow(self):
290         """ If a row is composed only of empty values (due to having
291         filtered out non-empty values from it), it should be removed
292         """
293         Import = self.registry('base_import.import')
294         id = Import.create(self.cr, self.uid, {
295             'res_model': 'base_import.tests.models.preview',
296             'file': 'name,Some Value,Counter\n'
297                     'foo,1,2\n'
298                     ',3,\n'
299                     ',5,6\n'
300         })
301         record = Import.browse(self.cr, self.uid, id)
302         data, fields = Import._convert_import_data(
303             record, ['name', False, 'othervalue'],
304             {'quoting': '"', 'separator': ',', 'headers': True,})
305
306         self.assertItemsEqual(fields, ['name', 'othervalue'])
307         self.assertItemsEqual(data, [
308             ('foo', '2'),
309             ('', '6'),
310         ])
311
312     def test_nofield(self):
313         Import = self.registry('base_import.import')
314
315         id = Import.create(self.cr, self.uid, {
316             'res_model': 'base_import.tests.models.preview',
317             'file': 'name,Some Value,Counter\n'
318                     'foo,1,2\n'
319         })
320
321         record = Import.browse(self.cr, self.uid, id)
322         self.assertRaises(
323             ValueError,
324             Import._convert_import_data,
325             record, [],
326             {'quoting': '"', 'separator': ',', 'headers': True,})
327
328     def test_falsefields(self):
329         Import = self.registry('base_import.import')
330
331         id = Import.create(self.cr, self.uid, {
332             'res_model': 'base_import.tests.models.preview',
333             'file': 'name,Some Value,Counter\n'
334                     'foo,1,2\n'
335         })
336
337         record = Import.browse(self.cr, self.uid, id)
338         self.assertRaises(
339             ValueError,
340             Import._convert_import_data,
341             record, [False, False, False],
342             {'quoting': '"', 'separator': ',', 'headers': True,})