source: main/waeup.ikoba/trunk/src/waeup/ikoba/utils/tests/test_converters.py @ 11997

Last change on this file since 11997 was 11954, checked in by Henrik Bettermann, 10 years ago

Remove tools.

rename institution company.

Remove some apis from docs.

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