## $Id: test_converters.py 14008 2016-07-02 17:43:58Z uli $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """ Tests for converterts. """ import datetime import shutil import tempfile import unittest from zope import schema from zope.component import provideUtility from zope.component.factory import Factory from zope.component.hooks import clearSite from zope.component.interfaces import IFactory from zope.formlib import form from zope.interface import ( Interface, implements, invariant, Invalid, implementedBy, verify) from waeup.kofa.app import University from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase from waeup.kofa.interfaces import ( SimpleKofaVocabulary, SubjectSource, GradeSource, IFieldConverter, DELETION_MARKER, IGNORE_MARKER) from waeup.kofa.schoolgrades import ResultEntryField from waeup.kofa.utils.converters import ( IObjectConverter, DefaultFieldConverter, ListFieldConverter, PhoneNumberFieldConverter, ResultEntryConverter, DefaultObjectConverter) from waeup.kofa.utils.helpers import attrs_to_fields colors = SimpleKofaVocabulary( ('Red', u'red'), ('Green', u'green'), ('Blue', u'blue'), ) car_nums = SimpleKofaVocabulary( ('None', 0), ('One', 1), ('Two', 2), ('Three', 3), ) class IContact(Interface): """Sample interface for sample content type used here in tests. """ name = schema.TextLine( title=u'Name', default=u'Manfred', readonly=True, ) age = schema.Int( title=u'Age', default=23, required=True, ) city = schema.TextLine( title=u'City', required=True, ) vip = schema.Bool( title=u'Celebrity', default=False, required=True, ) birthday = schema.Date( title=u'Birthday', default=None, ) fav_color = schema.Choice( title=u'Favourite color', default=u'red', vocabulary=colors, ) num_cars = schema.Choice( title=u'Number of cars owned', default=None, vocabulary=car_nums, ) grades = schema.List( title=u'School Grades', value_type=ResultEntryField(), required=True, defaultFactory=list ) fav_colors = schema.List( title=u'Favourite colors', value_type=schema.Choice( vocabulary=colors ), required=True, defaultFactory=list ) friends = schema.List( title=u'Friends', value_type=schema.TextLine( title=u'Name', ) ) @invariant def kevinIsYoung(contact): if contact.age > 16 and contact.name == 'Kevin': raise Invalid('Kevins are age 16 or below.') class Contact(object): """Sample content type. """ implements(IContact) Contact = attrs_to_fields(Contact) form_fields_select = form.Fields(IContact).select('name', 'vip') form_fields_omit = form.Fields(IContact).omit('name', 'vip') class TestContacts(unittest.TestCase): # make sure the Contact class inhere works as expected def test_default_lists_not_shared(self): # 'grades' is a Contact property with empty list as default. # Ensure lists are unique if set as default this test fails if # `default` is set to empty list in interface. c1 = Contact() c2 = Contact() self.assertTrue(c1.grades is not c2.grades) class FieldConverterTests(unittest.TestCase): def test_iface(self): # make sure we fullfill interface contracts obj1 = DefaultFieldConverter(None) obj2 = ListFieldConverter(None) obj3 = PhoneNumberFieldConverter(None) obj4 = ResultEntryConverter(None) verify.verifyObject(IFieldConverter, obj1) verify.verifyObject(IFieldConverter, obj2) verify.verifyObject(IFieldConverter, obj3) verify.verifyObject(IFieldConverter, obj4) verify.verifyClass(IFieldConverter, DefaultFieldConverter) verify.verifyClass(IFieldConverter, ListFieldConverter) verify.verifyClass(IFieldConverter, PhoneNumberFieldConverter) verify.verifyClass(IFieldConverter, ResultEntryConverter) return class ConverterTests(FunctionalTestCase): layer = FunctionalLayer def setUp(self): super(ConverterTests, self).setUp() # Setup a sample site for each test app = University() self.dc_root = tempfile.mkdtemp() app['datacenter'].setStoragePath(self.dc_root) # Prepopulate the ZODB... self.getRootFolder()['app'] = app self.app = self.getRootFolder()['app'] self.workdir = tempfile.mkdtemp() # Create a factory for contacts and register it as global utility factory = Factory(Contact) provideUtility(factory, IFactory, 'contact') return def tearDown(self): super(ConverterTests, self).tearDown() shutil.rmtree(self.workdir) shutil.rmtree(self.dc_root) clearSite() return def test_iface(self): # make sure we fullfill interface contracts obj = DefaultObjectConverter(IContact) verify.verifyObject(IObjectConverter, obj) verify.verifyClass(IObjectConverter, DefaultObjectConverter) return def test_valid_data(self): contact = Contact() contact.age = 33 input_data = dict(name='Rudi', age='99') converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( input_data, contact) assert data['name'] == 'Rudi' assert data['age'] == 99 return def test_bool(self): contact1 = Contact() contact2 = Contact() input_data1 = dict(vip='on') input_data2 = dict(vip='') converter = IObjectConverter(IContact) # a converter to IContact err1, inv_err1, data1 = converter.fromStringDict( input_data1, contact1) err2, inv_err2, data2 = converter.fromStringDict( input_data2, contact2) assert data1['vip'] is True assert data2['vip'] is False def test_bool_nonstandard_values1(self): # We accept 'true', 'True', 'tRuE', 'faLSE' and similar. contact1 = Contact() contact2 = Contact() input_data1 = dict(vip='True') input_data2 = dict(vip='false') converter = IObjectConverter(IContact) # a converter to IContact err1, inv_err1, data1 = converter.fromStringDict( input_data1, contact1) err2, inv_err2, data2 = converter.fromStringDict( input_data2, contact2) assert data1['vip'] is True assert data2['vip'] is False def test_bool_nonstandard_values2(self): # We accept '1' and '0' as bool values. contact1 = Contact() contact2 = Contact() input_data1 = dict(vip='1') input_data2 = dict(vip='0') converter = IObjectConverter(IContact) # a converter to IContact err1, inv_err1, data1 = converter.fromStringDict( input_data1, contact1) err2, inv_err2, data2 = converter.fromStringDict( input_data2, contact2) assert data1['vip'] is True assert data2['vip'] is False def test_bool_nonstandard_values3(self): # We accept 'yEs', 'no' and similar as bool values. contact1 = Contact() contact2 = Contact() input_data1 = dict(vip='Yes') input_data2 = dict(vip='no') converter = IObjectConverter(IContact) # a converter to IContact err1, inv_err1, data1 = converter.fromStringDict( input_data1, contact1) err2, inv_err2, data2 = converter.fromStringDict( input_data2, contact2) assert data1['vip'] is True assert data2['vip'] is False def test_int(self): contact = Contact() input_data = dict(age='99') converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( input_data, contact) assert data['age'] == 99 return def test_int_invalid(self): contact = Contact() input_data = dict(age='sweet sixteen') converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, new_contact = converter.fromStringDict( input_data, contact) self.assertEqual(err, [('age', u'Invalid integer data')]) return def test_textline(self): contact = Contact() input_data = dict(name='Rudi') converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( input_data, contact) self.assertEqual(data['name'], u'Rudi') assert isinstance(data['name'], unicode) return def test_invariant(self): contact = Contact() input_data = dict(name='Kevin', age='22') converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, new_contact = converter.fromStringDict( input_data, contact) self.assertEqual(inv_err, ['Kevins are age 16 or below.']) return def test_date(self): contact = Contact() converter = IObjectConverter(IContact) # a converter to IContact # The input format for dates: YYYY-MM-DD err, inv_err, data = converter.fromStringDict( dict(birthday='1945-12-23'), contact) assert data['birthday'] == datetime.date(1945, 12, 23) assert isinstance(data['birthday'], datetime.date) err, inv_err, data = converter.fromStringDict( dict(birthday='1945-23-12'), contact) #assert data['birthday'] == datetime.date(1945, 12, 23) assert err[0][1] == 'Invalid datetime data' # '08' is not interpreted as octal number err, inv_err, data = converter.fromStringDict( dict(birthday='1945-12-08'), contact) assert data['birthday'] == datetime.date(1945, 12, 8) return def test_date_invalid(self): contact = Contact() converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( dict(birthday='not-a-date'), contact) self.assertEqual(err, [('birthday', u'Invalid datetime data')]) def test_inject_formfields_select(self): # We can use our own formfields and select only a subset of fields contact = Contact() converter = IObjectConverter(IContact) # a converter to IContact input_data = dict(name='Bruno', age='99', vip='on') err, inv_err, data = converter.fromStringDict( input_data, contact, form_fields=form_fields_select) self.assertEqual(data['name'], 'Bruno') assert 'age' not in data.keys() assert data['vip'] is True return def test_inject_formfields_omit(self): # We can use our own formfields and omit some fields contact = Contact() converter = IObjectConverter(IContact) # a converter to IContact input_data = dict(name='Bruno', age='99', vip='on') err, inv_err, data = converter.fromStringDict( input_data, contact, form_fields=form_fields_omit) self.assertEqual(data['age'], 99) assert 'name' not in data.keys() assert 'vip' not in data.keys() return def test_factory(self): # We can use factories to convert values converter = IObjectConverter(IContact) # a converter to IContact # pass string ``contact`` instead of a real object err, inv_err, data = converter.fromStringDict( dict(name='Gabi', age='23'), 'contact') self.assertEqual(data['age'], 23) self.assertEqual(data['name'], u'Gabi') return def test_choice_vocab(self): # We can handle vocabularies converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( dict(fav_color='blue'), 'contact') assert data['fav_color'] == u'blue' assert isinstance(data['fav_color'], unicode) return def test_choice_vocab_invalid_value(self): # We can handle vocabularies converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( dict(fav_color='magenta'), 'contact') self.assertEqual(err, [('fav_color', u'Invalid value')]) assert 'fav_color' not in data.keys() return def test_non_string_choice(self): # We can handle vocabs with non-string values converter = IObjectConverter(IContact) # a converter to IContact err, inv_err, data = converter.fromStringDict( dict(num_cars='1'), 'contact') assert data['num_cars'] == 1 return def test_list_of_textlines(self): # We can convert lists of text lines converter = IObjectConverter(IContact) err, inv_err, data = converter.fromStringDict( {"friends": "['Fred', 'Wilma']"}, 'contact') self.assertEqual( data, {'friends': ['Fred', 'Wilma']}) return def test_list_of_resultentries(self): # We can handle lists of result entries converter = IObjectConverter(IContact) # get currently valid values s_src, g_src = SubjectSource(), GradeSource() s_val1, s_val2 = list(s_src.factory.getValues())[0:2] g_val1, g_val2 = list(g_src.factory.getValues())[0:2] req_string = u"[('%s', '%s'), ('%s', '%s')]" % ( s_val1, g_val1, s_val2, g_val2) err, inv_err, data = converter.fromStringDict( {"grades": req_string, }, 'contact') result_grades = data['grades'] self.assertTrue(isinstance(result_grades, list)) self.assertEqual(len(result_grades), 2) self.assertEqual(result_grades[0].subject, s_val1) self.assertEqual(result_grades[0].grade, g_val1) return def test_list_of_choices(self): # We cannot handle lists of choices because we are using # a widget which is not yet supported. converter = IObjectConverter(IContact) err, inv_err, data = converter.fromStringDict( {"fav_colors": "['red', 'green']"}, 'contact') self.assertEqual( data, {}) return def test_ignore_values(self): # in update mode we ignore marked values converter = IObjectConverter(IContact) err, inv_err, data = converter.fromStringDict( {"friends": IGNORE_MARKER}, Contact(), mode='update') # the ignored key/value are not part of the result self.assertEqual(data, {}) return def test_delete_values(self): # in update mode we delete values marked accordingly # 'deleting' means setting to missing_value or to default if required. converter = IObjectConverter(IContact) err, inv_err, data = converter.fromStringDict( {"grades": DELETION_MARKER, "friends": DELETION_MARKER}, 'contact', mode='update') # grades are about to be set to default, friends to None self.assertEqual(data, {'grades': [], 'friends': None}) return