source: main/waeup.kofa/trunk/src/waeup/kofa/utils/tests/test_converters.py @ 13958

Last change on this file since 13958 was 13159, checked in by Henrik Bettermann, 9 years ago

Enable import of list-of-choices fields.

  • Property svn:keywords set to Id
File size: 15.7 KB
Line 
1## $Id: test_converters.py 13159 2015-07-10 11:33:03Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Tests for converterts.
20"""
21import datetime
22import shutil
23import tempfile
24import unittest
25from zope import schema
26from zope.component import provideUtility
27from zope.component.factory import Factory
28from zope.component.hooks import clearSite
29from zope.component.interfaces import IFactory
30from zope.formlib import form
31from zope.interface import (
32    Interface, implements, invariant, Invalid, implementedBy, verify)
33
34from waeup.kofa.app import University
35from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
36from waeup.kofa.interfaces import (
37    SimpleKofaVocabulary, SubjectSource, GradeSource, IFieldConverter,
38    DELETION_MARKER, IGNORE_MARKER)
39from waeup.kofa.schoolgrades import ResultEntryField
40from waeup.kofa.university import Faculty
41from waeup.kofa.utils.converters import (
42    IObjectConverter, IFieldConverter, DefaultFieldConverter,
43    ListFieldConverter, PhoneNumberFieldConverter, ResultEntryConverter,
44    DefaultObjectConverter)
45from waeup.kofa.utils.helpers import attrs_to_fields
46
47colors = SimpleKofaVocabulary(
48    ('Red', u'red'),
49    ('Green', u'green'),
50    ('Blue', u'blue'),
51    )
52car_nums = SimpleKofaVocabulary(
53    ('None', 0),
54    ('One', 1),
55    ('Two', 2),
56    ('Three', 3),
57    )
58class IContact(Interface):
59    """Sample interface for sample content type used here in tests.
60    """
61    name = schema.TextLine(
62        title = u'Name',
63        default = u'Manfred',
64        readonly = True,
65        )
66    age = schema.Int(
67        title = u'Age',
68        default = 23,
69        required = True,
70        )
71    city = schema.TextLine(
72        title = u'City',
73        required = True,
74        )
75    vip = schema.Bool(
76        title = u'Celebrity',
77        default = False,
78        required = True,
79        )
80    birthday = schema.Date(
81        title = u'Birthday',
82        default = None,
83        )
84    fav_color = schema.Choice(
85        title = u'Favourite color',
86        default = u'red',
87        vocabulary = colors,
88        )
89    num_cars = schema.Choice(
90        title = u'Number of cars owned',
91        default = None,
92        vocabulary = car_nums,
93        )
94    grades = schema.List(
95        title = u'School Grades',
96        value_type = ResultEntryField(),
97        required = True,
98        default = [],
99        )
100    fav_colors = schema.List(
101        title = u'Favourite colors',
102        value_type = schema.Choice(
103            vocabulary = colors
104            ),
105        required = True,
106        default = [],
107        )
108    friends = schema.List(
109        title = u'Friends',
110        value_type = schema.TextLine(
111            title = u'Name',
112            )
113        )
114
115    @invariant
116    def kevinIsYoung(contact):
117        if contact.age > 16 and contact.name == 'Kevin':
118            raise Invalid('Kevins are age 16 or below.')
119
120class Contact(object):
121    """Sample content type.
122    """
123    implements(IContact)
124Contact = attrs_to_fields(Contact)
125
126form_fields_select = form.Fields(IContact).select('name', 'vip')
127form_fields_omit = form.Fields(IContact).omit('name', 'vip')
128
129class ContactFactory(object):
130    """A factory for faculty containers.
131    """
132    implements(IContact)
133
134    def __call__(self, *args, **kw):
135        return Faculty()
136
137    def getInterfaces(self):
138        return implementedBy(Faculty)
139
140class FieldConverterTests(unittest.TestCase):
141
142    def test_iface(self):
143        # make sure we fullfill interface contracts
144        obj1 = DefaultFieldConverter(None)
145        obj2 = ListFieldConverter(None)
146        obj3 = PhoneNumberFieldConverter(None)
147        obj4 = ResultEntryConverter(None)
148        verify.verifyObject(IFieldConverter, obj1)
149        verify.verifyObject(IFieldConverter, obj2)
150        verify.verifyObject(IFieldConverter, obj3)
151        verify.verifyObject(IFieldConverter, obj4)
152        verify.verifyClass(IFieldConverter, DefaultFieldConverter)
153        verify.verifyClass(IFieldConverter, ListFieldConverter)
154        verify.verifyClass(IFieldConverter, PhoneNumberFieldConverter)
155        verify.verifyClass(IFieldConverter, ResultEntryConverter)
156        return
157
158class ConverterTests(FunctionalTestCase):
159
160    layer = FunctionalLayer
161
162    def setUp(self):
163        super(ConverterTests, self).setUp()
164
165        # Setup a sample site for each test
166        app = University()
167        self.dc_root = tempfile.mkdtemp()
168        app['datacenter'].setStoragePath(self.dc_root)
169
170        # Prepopulate the ZODB...
171        self.getRootFolder()['app'] = app
172        self.app = self.getRootFolder()['app']
173
174        self.workdir = tempfile.mkdtemp()
175
176        # Create a factory for contacts and register it as global utility
177        factory = Factory(Contact)
178        provideUtility(factory, IFactory, 'contact')
179        return
180
181    def tearDown(self):
182        super(ConverterTests, self).tearDown()
183        shutil.rmtree(self.workdir)
184        shutil.rmtree(self.dc_root)
185        clearSite()
186        return
187
188    def test_iface(self):
189        # make sure we fullfill interface contracts
190        obj = DefaultObjectConverter(IContact)
191        verify.verifyObject(IObjectConverter, obj)
192        verify.verifyClass(IObjectConverter, DefaultObjectConverter)
193        return
194
195    def test_valid_data(self):
196        contact = Contact()
197        contact.age = 33
198        input_data = dict(name='Rudi', age='99')
199        converter = IObjectConverter(IContact) # a converter to IContact
200        err, inv_err, data = converter.fromStringDict(
201            input_data, contact)
202        assert data['name'] == 'Rudi'
203        assert data['age'] == 99
204        return
205
206    def test_bool(self):
207        contact1 = Contact()
208        contact2 = Contact()
209        input_data1 = dict(vip='on')
210        input_data2 = dict(vip='')
211        converter = IObjectConverter(IContact) # a converter to IContact
212        err1, inv_err1, data1 = converter.fromStringDict(
213            input_data1, contact1)
214        err2, inv_err2, data2 = converter.fromStringDict(
215            input_data2, contact2)
216        assert data1['vip'] is True
217        assert data2['vip'] is False
218
219    def test_bool_nonstandard_values1(self):
220        # We accept 'true', 'True', 'tRuE', 'faLSE' and similar.
221        contact1 = Contact()
222        contact2 = Contact()
223        input_data1 = dict(vip='True')
224        input_data2 = dict(vip='false')
225        converter = IObjectConverter(IContact) # a converter to IContact
226        err1, inv_err1, data1 = converter.fromStringDict(
227            input_data1, contact1)
228        err2, inv_err2, data2 = converter.fromStringDict(
229            input_data2, contact2)
230        assert data1['vip'] is True
231        assert data2['vip'] is False
232
233    def test_bool_nonstandard_values2(self):
234        # We accept '1' and '0' as bool values.
235        contact1 = Contact()
236        contact2 = Contact()
237        input_data1 = dict(vip='1')
238        input_data2 = dict(vip='0')
239        converter = IObjectConverter(IContact) # a converter to IContact
240        err1, inv_err1, data1 = converter.fromStringDict(
241            input_data1, contact1)
242        err2, inv_err2, data2 = converter.fromStringDict(
243            input_data2, contact2)
244        assert data1['vip'] is True
245        assert data2['vip'] is False
246
247    def test_bool_nonstandard_values3(self):
248        # We accept 'yEs', 'no' and similar as bool values.
249        contact1 = Contact()
250        contact2 = Contact()
251        input_data1 = dict(vip='Yes')
252        input_data2 = dict(vip='no')
253        converter = IObjectConverter(IContact) # a converter to IContact
254        err1, inv_err1, data1 = converter.fromStringDict(
255            input_data1, contact1)
256        err2, inv_err2, data2 = converter.fromStringDict(
257            input_data2, contact2)
258        assert data1['vip'] is True
259        assert data2['vip'] is False
260
261    def test_int(self):
262        contact = Contact()
263        input_data = dict(age='99')
264        converter = IObjectConverter(IContact) # a converter to IContact
265        err, inv_err, data = converter.fromStringDict(
266            input_data, contact)
267        assert data['age'] == 99
268        return
269
270    def test_int_invalid(self):
271        contact = Contact()
272        input_data = dict(age='sweet sixteen')
273        converter = IObjectConverter(IContact) # a converter to IContact
274        err, inv_err, new_contact = converter.fromStringDict(
275            input_data, contact)
276        self.assertEqual(err, [('age', u'Invalid integer data')])
277        return
278
279    def test_textline(self):
280        contact = Contact()
281        input_data = dict(name='Rudi')
282        converter = IObjectConverter(IContact) # a converter to IContact
283        err, inv_err, data = converter.fromStringDict(
284            input_data, contact)
285        self.assertEqual(data['name'], u'Rudi')
286        assert isinstance(data['name'], unicode)
287        return
288
289    def test_invariant(self):
290        contact = Contact()
291        input_data = dict(name='Kevin', age='22')
292        converter = IObjectConverter(IContact) # a converter to IContact
293        err, inv_err, new_contact = converter.fromStringDict(
294            input_data, contact)
295        self.assertEqual(inv_err, ['Kevins are age 16 or below.'])
296        return
297
298    def test_date(self):
299        contact = Contact()
300        converter = IObjectConverter(IContact) # a converter to IContact
301
302        # The input format for dates: YYYY-MM-DD
303        err, inv_err, data = converter.fromStringDict(
304            dict(birthday='1945-12-23'), contact)
305        assert data['birthday'] == datetime.date(1945, 12, 23)
306        assert isinstance(data['birthday'], datetime.date)
307
308        err, inv_err, data = converter.fromStringDict(
309            dict(birthday='1945-23-12'), contact)
310        #assert data['birthday'] == datetime.date(1945, 12, 23)
311        assert err[0][1] =='Invalid datetime data'
312
313        # '08' is not interpreted as octal number
314        err, inv_err, data = converter.fromStringDict(
315            dict(birthday='1945-12-08'), contact)
316        assert data['birthday'] == datetime.date(1945, 12, 8)
317        return
318
319    def test_date_invalid(self):
320        contact = Contact()
321        converter = IObjectConverter(IContact) # a converter to IContact
322        err, inv_err, data = converter.fromStringDict(
323            dict(birthday='not-a-date'), contact)
324        self.assertEqual(err, [('birthday', u'Invalid datetime data')])
325
326    def test_inject_formfields_select(self):
327        # We can use our own formfields and select only a subset of fields
328        contact = Contact()
329        converter = IObjectConverter(IContact) # a converter to IContact
330        input_data = dict(name='Bruno', age='99', vip='on')
331        err, inv_err, data = converter.fromStringDict(
332            input_data, contact, form_fields=form_fields_select)
333        self.assertEqual(data['name'], 'Bruno')
334        assert 'age' not in data.keys()
335        assert data['vip'] is True
336        return
337
338    def test_inject_formfields_omit(self):
339        # We can use our own formfields and omit some fields
340        contact = Contact()
341        converter = IObjectConverter(IContact) # a converter to IContact
342        input_data = dict(name='Bruno', age='99', vip='on')
343        err, inv_err, data = converter.fromStringDict(
344            input_data, contact, form_fields=form_fields_omit)
345        self.assertEqual(data['age'], 99)
346        assert 'name' not in data.keys()
347        assert 'vip' not in data.keys()
348        return
349
350    def test_factory(self):
351        # We can use factories to convert values
352        converter = IObjectConverter(IContact) # a converter to IContact
353        # pass string ``contact`` instead of a real object
354        err, inv_err, data = converter.fromStringDict(
355            dict(name='Gabi', age='23'), 'contact')
356        self.assertEqual(data['age'], 23)
357        self.assertEqual(data['name'], u'Gabi')
358        return
359
360    def test_choice_vocab(self):
361        # We can handle vocabularies
362        converter = IObjectConverter(IContact) # a converter to IContact
363        err, inv_err, data = converter.fromStringDict(
364            dict(fav_color='blue'), 'contact')
365        assert data['fav_color'] == u'blue'
366        assert isinstance(data['fav_color'], unicode)
367        return
368
369    def test_choice_vocab_invalid_value(self):
370        # We can handle vocabularies
371        converter = IObjectConverter(IContact) # a converter to IContact
372        err, inv_err, data = converter.fromStringDict(
373            dict(fav_color='magenta'), 'contact')
374        self.assertEqual(err, [('fav_color', u'Invalid value')])
375        assert 'fav_color' not in data.keys()
376        return
377
378    def test_non_string_choice(self):
379        # We can handle vocabs with non-string values
380        converter = IObjectConverter(IContact) # a converter to IContact
381        err, inv_err, data = converter.fromStringDict(
382            dict(num_cars='1'), 'contact')
383        assert data['num_cars'] == 1
384        return
385
386    def test_list_of_textlines(self):
387        # We can convert lists of text lines
388        converter = IObjectConverter(IContact)
389        err, inv_err, data = converter.fromStringDict(
390            {"friends": "['Fred', 'Wilma']"}, 'contact')
391        self.assertEqual(
392            data, {'friends': ['Fred', 'Wilma']})
393        return
394
395    def test_list_of_resultentries(self):
396        # We can handle lists of result entries
397        converter = IObjectConverter(IContact)
398        # get currently valid values
399        s_src, g_src = SubjectSource(), GradeSource()
400        s_val1, s_val2 = list(s_src.factory.getValues())[0:2]
401        g_val1, g_val2 = list(g_src.factory.getValues())[0:2]
402        req_string = u"[('%s', '%s'), ('%s', '%s')]" % (
403                s_val1, g_val1, s_val2, g_val2)
404        err, inv_err, data = converter.fromStringDict(
405            {"grades": req_string,
406             },
407            'contact')
408        result_grades = data['grades']
409        self.assertTrue(isinstance(result_grades, list))
410        self.assertEqual(len(result_grades), 2)
411        self.assertEqual(result_grades[0].subject, s_val1)
412        self.assertEqual(result_grades[0].grade, g_val1)
413        return
414
415    def test_list_of_choices(self):
416        # We cannot handle lists of choices because we are using
417        # a widget which is not yet supported.
418        converter = IObjectConverter(IContact)
419        err, inv_err, data = converter.fromStringDict(
420            {"fav_colors": "['red', 'green']"}, 'contact')
421        self.assertEqual(
422            data, {})
423        return
424
425    def test_ignore_values(self):
426        # in update mode we ignore marked values
427        converter = IObjectConverter(IContact)
428        err, inv_err, data = converter.fromStringDict(
429            {"friends": IGNORE_MARKER},
430            Contact(),
431            mode='update')
432        # the ignored key/value are not part of the result
433        self.assertEqual(data, {})
434        return
435
436    def test_delete_values(self):
437        # in update mode we delete values marked accordingly
438        # 'deleting' means setting to missing_value or to default if required.
439        converter = IObjectConverter(IContact)
440        err, inv_err, data = converter.fromStringDict(
441            {"grades": DELETION_MARKER,
442             "friends": DELETION_MARKER},
443            'contact', mode='update')
444        # grades are about to be set to default, friends to None
445        self.assertEqual(data, {'grades': [], 'friends': None})
446        return
Note: See TracBrowser for help on using the repository browser.