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

Last change on this file since 17497 was 16818, checked in by Henrik Bettermann, 3 years ago

Don't complain but remove leading and trailing whitespaces while converting values during import.

  • Property svn:keywords set to Id
File size: 15.9 KB
Line 
1## $Id: test_converters.py 16818 2022-02-21 06:21:10Z 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.utils.converters import (
41    IObjectConverter, DefaultFieldConverter, ListFieldConverter,
42    PhoneNumberFieldConverter, ResultEntryConverter, DefaultObjectConverter)
43from waeup.kofa.utils.helpers import attrs_to_fields
44
45colors = SimpleKofaVocabulary(
46    ('Red', u'red'),
47    ('Green', u'green'),
48    ('Blue', u'blue'),
49    )
50car_nums = SimpleKofaVocabulary(
51    ('None', 0),
52    ('One', 1),
53    ('Two', 2),
54    ('Three', 3),
55    )
56
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        defaultFactory=list
99        )
100    fav_colors = schema.List(
101        title=u'Favourite colors',
102        value_type=schema.Choice(
103            vocabulary=colors
104            ),
105        required=True,
106        defaultFactory=list
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
120
121class Contact(object):
122    """Sample content type.
123    """
124    implements(IContact)
125Contact = attrs_to_fields(Contact)
126
127form_fields_select = form.Fields(IContact).select('name', 'vip')
128form_fields_omit = form.Fields(IContact).omit('name', 'vip')
129
130
131class TestContacts(unittest.TestCase):
132    # make sure the Contact class inhere works as expected
133
134    def test_default_lists_not_shared(self):
135        # 'grades' is a Contact property with empty list as default.
136        # Ensure lists are unique if set as default this test fails if
137        # `default` is set to empty list in interface.
138        c1 = Contact()
139        c2 = Contact()
140        self.assertTrue(c1.grades is not c2.grades)
141
142
143class FieldConverterTests(unittest.TestCase):
144
145    def test_iface(self):
146        # make sure we fullfill interface contracts
147        obj1 = DefaultFieldConverter(None)
148        obj2 = ListFieldConverter(None)
149        obj3 = PhoneNumberFieldConverter(None)
150        obj4 = ResultEntryConverter(None)
151        verify.verifyObject(IFieldConverter, obj1)
152        verify.verifyObject(IFieldConverter, obj2)
153        verify.verifyObject(IFieldConverter, obj3)
154        verify.verifyObject(IFieldConverter, obj4)
155        verify.verifyClass(IFieldConverter, DefaultFieldConverter)
156        verify.verifyClass(IFieldConverter, ListFieldConverter)
157        verify.verifyClass(IFieldConverter, PhoneNumberFieldConverter)
158        verify.verifyClass(IFieldConverter, ResultEntryConverter)
159        return
160
161
162class ConverterTests(FunctionalTestCase):
163
164    layer = FunctionalLayer
165
166    def setUp(self):
167        super(ConverterTests, self).setUp()
168
169        # Setup a sample site for each test
170        app = University()
171        self.dc_root = tempfile.mkdtemp()
172        app['datacenter'].setStoragePath(self.dc_root)
173
174        # Prepopulate the ZODB...
175        self.getRootFolder()['app'] = app
176        self.app = self.getRootFolder()['app']
177
178        self.workdir = tempfile.mkdtemp()
179
180        # Create a factory for contacts and register it as global utility
181        factory = Factory(Contact)
182        provideUtility(factory, IFactory, 'contact')
183        return
184
185    def tearDown(self):
186        super(ConverterTests, self).tearDown()
187        shutil.rmtree(self.workdir)
188        shutil.rmtree(self.dc_root)
189        clearSite()
190        return
191
192    def test_iface(self):
193        # make sure we fullfill interface contracts
194        obj = DefaultObjectConverter(IContact)
195        verify.verifyObject(IObjectConverter, obj)
196        verify.verifyClass(IObjectConverter, DefaultObjectConverter)
197        return
198
199    def test_valid_data(self):
200        contact = Contact()
201        contact.age = 33
202        input_data = dict(name='  Rudi    ', age=' 99 ')
203        converter = IObjectConverter(IContact)  # a converter to IContact
204        err, inv_err, data = converter.fromStringDict(
205            input_data, contact)
206        assert data['name'] == 'Rudi'
207        assert data['age'] == 99
208        return
209
210    def test_bool(self):
211        contact1 = Contact()
212        contact2 = Contact()
213        input_data1 = dict(vip='on')
214        input_data2 = dict(vip='')
215        converter = IObjectConverter(IContact)  # a converter to IContact
216        err1, inv_err1, data1 = converter.fromStringDict(
217            input_data1, contact1)
218        err2, inv_err2, data2 = converter.fromStringDict(
219            input_data2, contact2)
220        assert data1['vip'] is True
221        assert data2['vip'] is False
222
223    def test_bool_nonstandard_values1(self):
224         # We accept 'true', 'True', 'tRuE', 'faLSE' and similar.
225        contact1 = Contact()
226        contact2 = Contact()
227        input_data1 = dict(vip='True')
228        input_data2 = dict(vip='false')
229        converter = IObjectConverter(IContact)  # a converter to IContact
230        err1, inv_err1, data1 = converter.fromStringDict(
231            input_data1, contact1)
232        err2, inv_err2, data2 = converter.fromStringDict(
233            input_data2, contact2)
234        assert data1['vip'] is True
235        assert data2['vip'] is False
236
237    def test_bool_nonstandard_values2(self):
238        # We accept '1' and '0' as bool values.
239        contact1 = Contact()
240        contact2 = Contact()
241        input_data1 = dict(vip='1')
242        input_data2 = dict(vip='0')
243        converter = IObjectConverter(IContact)  # a converter to IContact
244        err1, inv_err1, data1 = converter.fromStringDict(
245            input_data1, contact1)
246        err2, inv_err2, data2 = converter.fromStringDict(
247            input_data2, contact2)
248        assert data1['vip'] is True
249        assert data2['vip'] is False
250
251    def test_bool_nonstandard_values3(self):
252        # We accept 'yEs', 'no' and similar as bool values.
253        contact1 = Contact()
254        contact2 = Contact()
255        input_data1 = dict(vip='Yes')
256        input_data2 = dict(vip='no')
257        converter = IObjectConverter(IContact)  # a converter to IContact
258        err1, inv_err1, data1 = converter.fromStringDict(
259            input_data1, contact1)
260        err2, inv_err2, data2 = converter.fromStringDict(
261            input_data2, contact2)
262        assert data1['vip'] is True
263        assert data2['vip'] is False
264
265    def test_int(self):
266        contact = Contact()
267        input_data = dict(age='99')
268        converter = IObjectConverter(IContact)  # a converter to IContact
269        err, inv_err, data = converter.fromStringDict(
270            input_data, contact)
271        assert data['age'] == 99
272        return
273
274    def test_int_invalid(self):
275        contact = Contact()
276        input_data = dict(age='sweet sixteen')
277        converter = IObjectConverter(IContact)  # a converter to IContact
278        err, inv_err, new_contact = converter.fromStringDict(
279            input_data, contact)
280        self.assertEqual(err, [('age', u'Invalid integer data')])
281        return
282
283    def test_textline(self):
284        contact = Contact()
285        input_data = dict(name=' Rudi     ')
286        converter = IObjectConverter(IContact)  # a converter to IContact
287        err, inv_err, data = converter.fromStringDict(
288            input_data, contact)
289        self.assertEqual(data['name'], u'Rudi')
290        assert isinstance(data['name'], unicode)
291        return
292
293    def test_invariant(self):
294        contact = Contact()
295        input_data = dict(name='Kevin', age='22')
296        converter = IObjectConverter(IContact)  # a converter to IContact
297        err, inv_err, new_contact = converter.fromStringDict(
298            input_data, contact)
299        self.assertEqual(inv_err, ['Kevins are age 16 or below.'])
300        return
301
302    def test_date(self):
303        contact = Contact()
304        converter = IObjectConverter(IContact)  # a converter to IContact
305
306        # The input format for dates: YYYY-MM-DD
307        err, inv_err, data = converter.fromStringDict(
308            dict(birthday='1945-12-23'), contact)
309        assert data['birthday'] == datetime.date(1945, 12, 23)
310        assert isinstance(data['birthday'], datetime.date)
311
312        err, inv_err, data = converter.fromStringDict(
313            dict(birthday='1945-23-12'), contact)
314        #assert data['birthday'] == datetime.date(1945, 12, 23)
315        assert err[0][1] == 'Invalid datetime data'
316
317        # '08' is not interpreted as octal number
318        err, inv_err, data = converter.fromStringDict(
319            dict(birthday='1945-12-08'), contact)
320        assert data['birthday'] == datetime.date(1945, 12, 8)
321        return
322
323    def test_date_invalid(self):
324        contact = Contact()
325        converter = IObjectConverter(IContact)  # a converter to IContact
326        err, inv_err, data = converter.fromStringDict(
327            dict(birthday='not-a-date'), contact)
328        self.assertEqual(err, [('birthday', u'Invalid datetime data')])
329
330    def test_inject_formfields_select(self):
331        # We can use our own formfields and select only a subset of fields
332        contact = Contact()
333        converter = IObjectConverter(IContact)  # a converter to IContact
334        input_data = dict(name='Bruno', age='99', vip='on')
335        err, inv_err, data = converter.fromStringDict(
336            input_data, contact, form_fields=form_fields_select)
337        self.assertEqual(data['name'], 'Bruno')
338        assert 'age' not in data.keys()
339        assert data['vip'] is True
340        return
341
342    def test_inject_formfields_omit(self):
343        # We can use our own formfields and omit some fields
344        contact = Contact()
345        converter = IObjectConverter(IContact)  # a converter to IContact
346        input_data = dict(name='Bruno', age='99', vip='on')
347        err, inv_err, data = converter.fromStringDict(
348            input_data, contact, form_fields=form_fields_omit)
349        self.assertEqual(data['age'], 99)
350        assert 'name' not in data.keys()
351        assert 'vip' not in data.keys()
352        return
353
354    def test_factory(self):
355        # We can use factories to convert values
356        converter = IObjectConverter(IContact)  # a converter to IContact
357        # pass string ``contact`` instead of a real object
358        err, inv_err, data = converter.fromStringDict(
359            dict(name='  Gabi  ', age='23  '), 'contact')
360        self.assertEqual(data['age'], 23)
361        self.assertEqual(data['name'], u'Gabi')
362        return
363
364    def test_choice_vocab(self):
365        # We can handle vocabularies
366        converter = IObjectConverter(IContact)  # a converter to IContact
367        err, inv_err, data = converter.fromStringDict(
368            dict(fav_color='blue'), 'contact')
369        assert data['fav_color'] == u'blue'
370        assert isinstance(data['fav_color'], unicode)
371        return
372
373    def test_choice_vocab_invalid_value(self):
374        # We can handle vocabularies
375        converter = IObjectConverter(IContact)  # a converter to IContact
376        err, inv_err, data = converter.fromStringDict(
377            dict(fav_color='magenta'), 'contact')
378        self.assertEqual(err, [('fav_color', u'Invalid value')])
379        assert 'fav_color' not in data.keys()
380        return
381
382    def test_non_string_choice(self):
383        # We can handle vocabs with non-string values
384        converter = IObjectConverter(IContact)  # a converter to IContact
385        err, inv_err, data = converter.fromStringDict(
386            dict(num_cars='1'), 'contact')
387        assert data['num_cars'] == 1
388        return
389
390    def test_list_of_textlines(self):
391        # We can convert lists of text lines
392        converter = IObjectConverter(IContact)
393        err, inv_err, data = converter.fromStringDict(
394            {"friends": "['Fred', 'Wilma']"}, 'contact')
395        self.assertEqual(
396            data, {'friends': ['Fred', 'Wilma']})
397        return
398
399    def test_list_of_resultentries(self):
400        # We can handle lists of result entries
401        converter = IObjectConverter(IContact)
402        # get currently valid values
403        s_src, g_src = SubjectSource(), GradeSource()
404        s_val1, s_val2 = list(s_src.factory.getValues())[0:2]
405        g_val1, g_val2 = list(g_src.factory.getValues())[0:2]
406        req_string = u"[('%s', '%s'), ('%s', '%s')]" % (
407            s_val1, g_val1, s_val2, g_val2)
408        err, inv_err, data = converter.fromStringDict(
409            {"grades": req_string,
410             },
411            'contact')
412        result_grades = data['grades']
413        self.assertTrue(isinstance(result_grades, list))
414        self.assertEqual(len(result_grades), 2)
415        self.assertEqual(result_grades[0].subject, s_val1)
416        self.assertEqual(result_grades[0].grade, g_val1)
417        return
418
419    def test_list_of_choices(self):
420        # We cannot handle lists of choices because we are using
421        # a widget which is not yet supported.
422        converter = IObjectConverter(IContact)
423        err, inv_err, data = converter.fromStringDict(
424            {"fav_colors": "['red', 'green']"}, 'contact')
425        self.assertEqual(
426            data, {})
427        return
428
429    def test_ignore_values(self):
430        # in update mode we ignore marked values
431        converter = IObjectConverter(IContact)
432        err, inv_err, data = converter.fromStringDict(
433            {"friends": IGNORE_MARKER},
434            Contact(),
435            mode='update')
436        # the ignored key/value are not part of the result
437        self.assertEqual(data, {})
438        return
439
440    def test_delete_values(self):
441        # in update mode we delete values marked accordingly
442        # 'deleting' means setting to missing_value or to default if required.
443        converter = IObjectConverter(IContact)
444        err, inv_err, data = converter.fromStringDict(
445            {"grades": DELETION_MARKER,
446             "friends": DELETION_MARKER},
447            'contact', mode='update')
448        # grades are about to be set to default, friends to None
449        self.assertEqual(data, {'grades': [], 'friends': None})
450        return
Note: See TracBrowser for help on using the repository browser.