# -*- encoding: utf-8 -*- import unittest2 from openerp.tests.common import TransactionCase from .. import models ID_FIELD = { 'id': 'id', 'name': 'id', 'string': "External ID", 'required': False, 'fields': [], } DISPLAY_NAME_FIELD = { 'id': 'display_name', 'name': 'display_name', 'string': "Name", 'required': False, 'fields': [], } def make_field(name='value', string='unknown', required=False, fields=[]): return [ ID_FIELD, DISPLAY_NAME_FIELD, {'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields}, ] def sorted_fields(fields): """ recursively sort field lists to ease comparison """ recursed = [dict(field, fields=sorted_fields(field['fields'])) for field in fields] return sorted(recursed, key=lambda field: field['id']) class BaseImportCase(TransactionCase): def assertEqualFields(self, fields1, fields2): self.assertEqual(sorted_fields(fields1), sorted_fields(fields2)) class test_basic_fields(BaseImportCase): def get_fields(self, field): return self.registry('base_import.import')\ .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field) def test_base(self): """ A basic field is not required """ self.assertEqualFields(self.get_fields('char'), make_field()) def test_required(self): """ Required fields should be flagged (so they can be fill-required) """ self.assertEqualFields(self.get_fields('char.required'), make_field(required=True)) def test_readonly(self): """ Readonly fields should be filtered out""" self.assertEqualFields(self.get_fields('char.readonly'), [ID_FIELD, DISPLAY_NAME_FIELD]) def test_readonly_states(self): """ Readonly fields with states should not be filtered out""" self.assertEqualFields(self.get_fields('char.states'), make_field()) def test_readonly_states_noreadonly(self): """ Readonly fields with states having nothing to do with readonly should still be filtered out""" self.assertEqualFields(self.get_fields('char.noreadonly'), [ID_FIELD, DISPLAY_NAME_FIELD]) def test_readonly_states_stillreadonly(self): """ Readonly fields with readonly states leaving them readonly always... filtered out""" self.assertEqualFields(self.get_fields('char.stillreadonly'), [ID_FIELD, DISPLAY_NAME_FIELD]) def test_m2o(self): """ M2O fields should allow import of themselves (name_get), their id and their xid""" self.assertEqualFields(self.get_fields('m2o'), make_field(fields=[ {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []}, {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []}, ])) def test_m2o_required(self): """ If an m2o field is required, its three sub-fields are required as well (the client has to handle that: requiredness is id-based) """ self.assertEqualFields(self.get_fields('m2o.required'), make_field(required=True, fields=[ {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []}, {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []}, ])) class test_o2m(BaseImportCase): def get_fields(self, field): return self.registry('base_import.import')\ .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field) def test_shallow(self): self.assertEqualFields(self.get_fields('o2m'), make_field(fields=[ ID_FIELD, DISPLAY_NAME_FIELD, # FIXME: should reverse field be ignored? {'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [ {'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []}, {'id': 'parent_id', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []}, ]}, {'id': 'value', 'name': 'value', 'string': 'unknown', 'required': False, 'fields': []}, ])) class test_match_headers_single(TransactionCase): def test_match_by_name(self): match = self.registry('base_import.import')._match_header( 'f0', [{'name': 'f0'}], {}) self.assertEqual(match, [{'name': 'f0'}]) def test_match_by_string(self): match = self.registry('base_import.import')._match_header( 'some field', [{'name': 'bob', 'string': "Some Field"}], {}) self.assertEqual(match, [{'name': 'bob', 'string': "Some Field"}]) def test_nomatch(self): match = self.registry('base_import.import')._match_header( 'should not be', [{'name': 'bob', 'string': "wheee"}], {}) self.assertEqual(match, []) def test_recursive_match(self): f = { 'name': 'f0', 'string': "My Field", 'fields': [ {'name': 'f0', 'string': "Sub field 0", 'fields': []}, {'name': 'f1', 'string': "Sub field 2", 'fields': []}, ] } match = self.registry('base_import.import')._match_header( 'f0/f1', [f], {}) self.assertEqual(match, [f, f['fields'][1]]) def test_recursive_nomatch(self): """ Match first level, fail to match second level """ f = { 'name': 'f0', 'string': "My Field", 'fields': [ {'name': 'f0', 'string': "Sub field 0", 'fields': []}, {'name': 'f1', 'string': "Sub field 2", 'fields': []}, ] } match = self.registry('base_import.import')._match_header( 'f0/f2', [f], {}) self.assertEqual(match, []) class test_match_headers_multiple(TransactionCase): def test_noheaders(self): self.assertEqual( self.registry('base_import.import')._match_headers( [], [], {}), (None, None) ) def test_nomatch(self): self.assertEqual( self.registry('base_import.import')._match_headers( iter([ ['foo', 'bar', 'baz', 'qux'], ['v1', 'v2', 'v3', 'v4'], ]), [], {'headers': True}), ( ['foo', 'bar', 'baz', 'qux'], dict.fromkeys(range(4)) ) ) def test_mixed(self): self.assertEqual( self.registry('base_import.import')._match_headers( iter(['foo bar baz qux/corge'.split()]), [ {'name': 'bar', 'string': 'Bar'}, {'name': 'bob', 'string': 'Baz'}, {'name': 'qux', 'string': 'Qux', 'fields': [ {'name': 'corge', 'fields': []}, ]} ], {'headers': True}), (['foo', 'bar', 'baz', 'qux/corge'], { 0: None, 1: ['bar'], 2: ['bob'], 3: ['qux', 'corge'], }) ) class test_preview(TransactionCase): def make_import(self): Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'res.users', 'file': u"로그인,언어\nbob,1\n".encode('euc_kr'), }) return Import, id def test_encoding(self): Import, id = self.make_import() result = Import.parse_preview(self.cr, self.uid, id, { 'quoting': '"', 'separator': ',', }) self.assertTrue('error' in result) def test_csv_errors(self): Import, id = self.make_import() result = Import.parse_preview(self.cr, self.uid, id, { 'quoting': 'foo', 'separator': ',', 'encoding': 'euc_kr', }) self.assertTrue('error' in result) def test_csv_errors(self): Import, id = self.make_import() result = Import.parse_preview(self.cr, self.uid, id, { 'quoting': '"', 'separator': 'bob', 'encoding': 'euc_kr', }) self.assertTrue('error' in result) def test_success(self): Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' 'bar,3,4\n' 'qux,5,6\n' }) result = Import.parse_preview(self.cr, self.uid, id, { 'quoting': '"', 'separator': ',', 'headers': True, }) self.assertEqual(result['matches'], {0: ['name'], 1: ['somevalue'], 2: None}) self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter']) # Order depends on iteration order of fields_get self.assertItemsEqual(result['fields'], [ ID_FIELD, DISPLAY_NAME_FIELD, {'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []}, {'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []}, {'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []}, ]) self.assertEqual(result['preview'], [ ['foo', '1', '2'], ['bar', '3', '4'], ['qux', '5', '6'], ]) # Ensure we only have the response fields we expect self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview']) class test_convert_import_data(TransactionCase): """ Tests conversion of base_import.import input into data which can be fed to Model.import_data """ def test_all(self): Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' 'bar,3,4\n' 'qux,5,6\n' }) record = Import.browse(self.cr, self.uid, id) data, fields = Import._convert_import_data( record, ['name', 'somevalue', 'othervalue'], {'quoting': '"', 'separator': ',', 'headers': True,}) self.assertItemsEqual(fields, ['name', 'somevalue', 'othervalue']) self.assertItemsEqual(data, [ ('foo', '1', '2'), ('bar', '3', '4'), ('qux', '5', '6'), ]) def test_filtered(self): """ If ``False`` is provided as field mapping for a column, that column should be removed from importable data """ Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' 'bar,3,4\n' 'qux,5,6\n' }) record = Import.browse(self.cr, self.uid, id) data, fields = Import._convert_import_data( record, ['name', False, 'othervalue'], {'quoting': '"', 'separator': ',', 'headers': True,}) self.assertItemsEqual(fields, ['name', 'othervalue']) self.assertItemsEqual(data, [ ('foo', '2'), ('bar', '4'), ('qux', '6'), ]) def test_norow(self): """ If a row is composed only of empty values (due to having filtered out non-empty values from it), it should be removed """ Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' ',3,\n' ',5,6\n' }) record = Import.browse(self.cr, self.uid, id) data, fields = Import._convert_import_data( record, ['name', False, 'othervalue'], {'quoting': '"', 'separator': ',', 'headers': True,}) self.assertItemsEqual(fields, ['name', 'othervalue']) self.assertItemsEqual(data, [ ('foo', '2'), ('', '6'), ]) def test_nofield(self): Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' }) record = Import.browse(self.cr, self.uid, id) self.assertRaises( ValueError, Import._convert_import_data, record, [], {'quoting': '"', 'separator': ',', 'headers': True,}) def test_falsefields(self): Import = self.registry('base_import.import') id = Import.create(self.cr, self.uid, { 'res_model': 'base_import.tests.models.preview', 'file': 'name,Some Value,Counter\n' 'foo,1,2\n' }) record = Import.browse(self.cr, self.uid, id) self.assertRaises( ValueError, Import._convert_import_data, record, [False, False, False], {'quoting': '"', 'separator': ',', 'headers': True,})