source: main/waeup.kofa/trunk/src/waeup/kofa/accesscodes/tests/test_accesscode.py @ 8305

Last change on this file since 8305 was 8234, checked in by Henrik Bettermann, 13 years ago

Show only time zone name.

  • Property svn:keywords set to Id
File size: 15.9 KB
Line 
1## $Id: test_accesscode.py 8234 2012-04-20 11:27:59Z 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##
18import doctest
19import os
20import re
21import shutil
22import tempfile
23import unittest
24
25from datetime import datetime
26from hurry.workflow.interfaces import InvalidTransitionError, IWorkflowState
27from zope.component import getUtility
28from zope.component.hooks import setSite, clearSite
29from zope.interface.verify import verifyObject, verifyClass
30from zope.testing import renormalizing
31from waeup.kofa.app import University
32from waeup.kofa.interfaces import IObjectHistory, IKofaPluggable, IKofaUtils
33from waeup.kofa.testing import (
34    FunctionalLayer, FunctionalTestCase, setUp, tearDown, getRootFolder)
35from waeup.kofa.accesscodes.accesscode import (
36    AccessCodeBatch, get_access_code, invalidate_accesscode, AccessCode,
37    disable_accesscode, reenable_accesscode, fire_transition,
38    AccessCodeBatchContainer, AccessCodePlugin)
39from waeup.kofa.accesscodes.interfaces import (
40    IAccessCode, IAccessCodeBatch,  IAccessCodeBatchContainer,)
41from waeup.kofa.accesscodes.workflow import INITIALIZED, USED, DISABLED
42
43
44optionflags = (
45    doctest.REPORT_NDIFF + doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE)
46
47class AccessCodeHelpersTests(FunctionalTestCase):
48    # Tests for helpers like get_access_code, disable_accesscode, ...
49
50    layer = FunctionalLayer
51
52    def setUp(self):
53        super(AccessCodeHelpersTests, self).setUp()
54
55        # Prepopulate ZODB
56        app = University()
57        self.dc_root = tempfile.mkdtemp()
58        app['datacenter'].setStoragePath(self.dc_root)
59
60        # Prepopulate the ZODB...
61        self.getRootFolder()['app'] = app
62        self.app = self.getRootFolder()['app']
63
64        # Create batch
65        batch = AccessCodeBatch('now', 'manfred', 'APP', 6.6, 0)
66        self.app['accesscodes'].addBatch(batch)
67
68        # Fill batch with accesscodes
69        batch.addAccessCode(0, '11111111')
70        batch.addAccessCode(1, '22222222')
71        batch.addAccessCode(2, '33333333')
72        self.ac1 = batch.getAccessCode('APP-1-11111111')
73        self.ac2 = batch.getAccessCode('APP-1-22222222')
74        self.ac3 = batch.getAccessCode('APP-1-33333333')
75
76        setSite(self.app)
77        return
78
79    def tearDown(self):
80        shutil.rmtree(self.dc_root)
81        super(AccessCodeHelpersTests, self).tearDown()
82        return
83
84    def test_get_access_code(self):
85        ac = get_access_code('APP-1-11111111')
86        assert ac is self.ac1
87
88    def test_get_access_code_not_string(self):
89        ac = get_access_code(object())
90        assert ac is None
91
92    def test_get_access_code_no_proper_pin(self):
93        ac = get_access_code('APP-without_pin')
94        assert ac is None
95
96    def test_get_access_code_invalid_batch_num(self):
97        ac = get_access_code('APP-invalid-11111111')
98        assert ac is None
99
100    def test_get_access_code_invalid_pin(self):
101        ac = get_access_code('APP-1-notexistent')
102        assert ac is None
103
104    def test_invalidate_accesscode(self):
105        assert self.ac1.state != USED
106        result = invalidate_accesscode('APP-1-11111111')
107        assert self.ac1.state == USED
108        assert result is True
109
110    def test_disable_accesscode_unused(self):
111        # we can disable initialized acs
112        assert self.ac1.state != USED
113        disable_accesscode('APP-1-11111111')
114        assert self.ac1.state == DISABLED
115
116    def test_disable_accesscode_used(self):
117        # we can disable already used acs
118        assert self.ac1.state != DISABLED
119        invalidate_accesscode('APP-1-11111111')
120        disable_accesscode('APP-1-11111111')
121        assert self.ac1.state == DISABLED
122
123    def test_reenable_accesscode(self):
124        # we can reenable disabled acs
125        disable_accesscode('APP-1-11111111')
126        result = reenable_accesscode('APP-1-11111111')
127        assert result is True
128        assert self.ac1.state != USED
129
130    def test_fire_transition(self):
131        # we can fire transitions generally
132        fire_transition('APP-1-11111111', 'use')
133        assert IWorkflowState(self.ac1).getState() is USED
134
135    def test_fire_transition_toward(self):
136        # the `toward` keyword is respected
137        fire_transition('APP-1-11111111', DISABLED, toward=True)
138        assert IWorkflowState(self.ac1).getState() is DISABLED
139
140    def test_fire_transition_no_site(self):
141        # when no site is available, we will get a TypeError
142        clearSite()
143        self.assertRaises(
144            KeyError,
145            fire_transition, 'APP-1-11111111', 'use')
146
147    def test_fire_transition_broken_ac_id(self):
148        # if we get an invalid access code id (of wrong format) we get
149        # ValueErrors
150        self.assertRaises(
151            ValueError,
152            fire_transition, '11111111', 'use')
153
154    def test_fire_transition_invalid_batch_id(self):
155        # if we request a non-existent batch_id, we'll get a KeyError
156        self.assertRaises(
157            KeyError,
158            fire_transition, 'FOO-1-11111111', 'use')
159
160    def test_fire_transition_invalid_ac(self):
161        # if we request a non-exitent access-code, we'll get a KeyError
162        self.assertRaises(
163            KeyError,
164            fire_transition, 'APP-1-NONSENSE', 'use')
165
166    def test_fire_transition_undef_trans_id(self):
167        # asking for undefined transition id means a KeyError
168        self.assertRaises(
169            KeyError,
170            fire_transition, 'APP-1-11111111', 'nonsense')
171
172    def test_fire_transition_invalid_transition(self):
173        # asking for a forbidden transition will result in
174        # InvalidTransitionError
175        self.assertRaises(
176            InvalidTransitionError,
177            fire_transition, 'APP-1-11111111', 'init') # already initialized
178
179    def test_fire_transition_comment(self):
180        # when we request a comment, it will also appear in history
181        fire_transition('APP-1-11111111', 'use', comment='Hi there!')
182        history = IObjectHistory(self.ac1)
183        msgs = history.messages
184        assert 'Hi there!' in msgs[-1]
185
186    def test_fire_transition_no_comment(self):
187        # without comment, the history should be without trailing garbage
188        fire_transition('APP-1-11111111', 'use')
189        history = IObjectHistory(self.ac1)
190        msgs = history.messages
191        assert msgs[-1].endswith('used by system')
192
193class AccessCodeTests(FunctionalTestCase):
194    # Tests for AccessCode class
195
196    layer = FunctionalLayer
197
198    def setUp(self):
199        super(AccessCodeTests, self).setUp()
200
201        # Prepopulate ZODB
202        app = University()
203        self.dc_root = tempfile.mkdtemp()
204        app['datacenter'].setStoragePath(self.dc_root)
205
206        # Prepopulate the ZODB...
207        self.getRootFolder()['app'] = app
208        self.app = self.getRootFolder()['app']
209
210        # Create batch
211        batch = AccessCodeBatch('now', 'manfred', 'APP', 6.6, 0)
212        self.app['accesscodes'].addBatch(batch)
213
214        # Fill batch with accesscodes
215        batch.addAccessCode(0, '11111111')
216
217        self.ac1 = batch.getAccessCode('APP-1-11111111')
218        setSite(self.app)
219        return
220
221    def tearDown(self):
222        shutil.rmtree(self.dc_root)
223        super(AccessCodeTests, self).tearDown()
224        return
225
226    def test_iface(self):
227        # AccessCodes fullfill their iface promises.
228        ac = AccessCode('1', '12345678')
229        assert verifyObject(IAccessCode, ac)
230        assert verifyClass(IAccessCode, AccessCode)
231
232    def test_history(self):
233        # Access codes have a history.
234        match = re.match(
235            '^....-..-.. ..:..:.. .+ - initialized by system',
236            self.ac1.history)
237        assert match is not None
238
239    def test_cost(self):
240        # We get the cost set in batch
241        cost = self.ac1.cost
242        assert cost == 6.6
243
244class AccessCodeBatchTests(FunctionalTestCase):
245    # Tests for AccessCodeBatch class
246
247    layer = FunctionalLayer
248
249    def setUp(self):
250        super(AccessCodeBatchTests, self).setUp()
251
252        # Prepopulate ZODB
253        app = University()
254        self.dc_root = tempfile.mkdtemp()
255        app['datacenter'].setStoragePath(self.dc_root)
256
257        # Prepopulate the ZODB...
258        self.getRootFolder()['app'] = app
259        self.app = self.getRootFolder()['app']
260
261        batch = AccessCodeBatch(    # create batch with zero entries
262            datetime.utcnow(), 'testuser', 'FOO', 9.99, 0)
263        self.app['accesscodes'].addBatch(batch)
264
265        self.ac1 = AccessCode(0, '11111111')
266        self.ac2 = AccessCode(1, '22222222')
267        self.ac3 = AccessCode(2, '33333333')
268        batch['FOO-1-11111111'] = self.ac1
269        batch['FOO-1-22222222'] = self.ac2
270        batch['FOO-1-33333333'] = self.ac3
271        self.batch = batch
272
273        setSite(self.app)
274        return
275
276    def tearDown(self):
277        shutil.rmtree(self.dc_root)
278        super(AccessCodeBatchTests, self).tearDown()
279        return
280
281    def test_iface(self):
282        batch = AccessCodeBatch(
283            datetime(2009, 12, 23), 'Fred','APP', 12.12, 3, num=10)
284        assert verifyObject(IAccessCodeBatch, batch)
285        assert verifyClass(IAccessCodeBatch, AccessCodeBatch)
286
287    def test_container_contents(self):
288        assert 'SFE-0' in self.app['accesscodes']
289        assert 'HOS-0' in self.app['accesscodes']
290        assert 'CLR-0' in self.app['accesscodes']
291
292    def test_csv_export(self):
293        # Make sure CSV export of accesscodes works
294        batch = self.batch
295        invalidate_accesscode('FOO-1-11111111', comment='comment with "quotes"')
296        disable_accesscode('FOO-1-33333333')
297        basename = batch.archive()
298        result_path = os.path.join(batch._getStoragePath(), basename)
299        expected = '''
300"prefix","serial","ac","state","history"
301"FOO","9.99","1","0"
302"FOO","0","FOO-1-11111111","used","<YYYY-MM-DD hh:mm:ss> UTC - ..."
303"FOO","1","FOO-1-22222222","initialized","<YYYY-MM-DD hh:mm:ss> UTC - ..."
304"FOO","2","FOO-1-33333333","disabled","<YYYY-MM-DD hh:mm:ss> UTC - ..."
305'''[1:]
306        contents = open(result_path, 'rb').read()
307        self.assertMatches(expected, contents)
308
309class AccessCodeBatchContainerTests(FunctionalTestCase):
310    # Tests for AccessCodeContainer class
311
312    layer = FunctionalLayer
313
314    def setUp(self):
315        super(AccessCodeBatchContainerTests, self).setUp()
316
317        # Prepopulate ZODB
318        app = University()
319        self.dc_root = tempfile.mkdtemp()
320        app['datacenter'].setStoragePath(self.dc_root)
321
322        # Prepopulate the ZODB...
323        self.getRootFolder()['app'] = app
324        self.app = self.getRootFolder()['app']
325
326        self.import_sample1_src = os.path.join(
327            os.path.dirname(__file__), 'sample_import.csv')
328
329        batch = AccessCodeBatch(    # create batch with zero entries
330            datetime.now(), 'testuser', 'BAR', 9.99, 0)
331        self.app['accesscodes'].addBatch(batch)
332
333        self.ac1 = AccessCode(0, '11111111')
334        self.ac2 = AccessCode(1, '22222222')
335        self.ac3 = AccessCode(2, '33333333')
336        batch['BAR-1-11111111'] = self.ac1
337        batch['BAR-1-22222222'] = self.ac2
338        batch['BAR-1-33333333'] = self.ac3
339        self.batch = batch
340
341        setSite(self.app)
342        return
343
344    def tearDown(self):
345        shutil.rmtree(self.dc_root)
346        super(AccessCodeBatchContainerTests, self).tearDown()
347        return
348
349    def test_iface(self):
350        accesscodes = AccessCodeBatchContainer()
351        assert verifyObject(IAccessCodeBatchContainer, accesscodes)
352        assert verifyClass(IAccessCodeBatchContainer, AccessCodeBatchContainer)
353
354    def test_csv_import(self):
355        # Make sure we can reimport sample data from local sample_import.csv
356        batchcontainer = self.app['accesscodes']
357        shutil.copyfile(        # Copy sample to import dir
358            os.path.join(os.path.dirname(__file__), 'sample_import.csv'),
359            os.path.join(batchcontainer._getStoragePath(), 'sample_import.csv')
360            )
361        batchcontainer.reimport('sample_import.csv')
362        batch = batchcontainer.get(u'FOO-1', None)
363        self.assertTrue(batch is not None)
364        keys = [x for x in batch.keys()]
365        self.assertEqual(
366            keys,
367            [u'FOO-1-11111111', u'FOO-1-22222222', u'FOO-1-33333333'])
368
369    def test_getAccessCode(self):
370        batchcontainer = self.app['accesscodes']
371        result1 = batchcontainer.getAccessCode('BAR-1-11111111')
372        result2 = batchcontainer.getAccessCode('BAR-1-not-existent')
373        assert isinstance(result1, AccessCode)
374        assert result2 is None
375
376    def test_disableAccessCode(self):
377        batchcontainer = self.app['accesscodes']
378        result1 = batchcontainer.disable('BAR-1-11111111')
379        result2 = batchcontainer.disable('BAR-1-not-existent')
380        assert self.ac1.state is DISABLED
381        assert result2 is None
382
383    def test_enableAccessCode(self):
384        batchcontainer = self.app['accesscodes']
385        batchcontainer.disable('BAR-1-11111111')
386        result1 = batchcontainer.enable('BAR-1-11111111')
387        result2 = batchcontainer.enable('BAR-1-not-existent')
388        assert self.ac1.state is INITIALIZED
389        assert result2 is None
390
391class AccessCodePluginTests(FunctionalTestCase):
392    # Tests for AccessCodeContainer class
393
394    layer = FunctionalLayer
395
396    def setUp(self):
397        super(AccessCodePluginTests, self).setUp()
398
399        # Prepopulate ZODB
400        app = University()
401        self.dc_root = tempfile.mkdtemp()
402        app['datacenter'].setStoragePath(self.dc_root)
403
404        # Prepopulate the ZODB...
405        self.getRootFolder()['app'] = app
406        self.app = self.getRootFolder()['app']
407
408    def tearDown(self):
409        shutil.rmtree(self.dc_root)
410        super(AccessCodePluginTests, self).tearDown()
411        return
412
413    def test_iface(self):
414        plugin = AccessCodePlugin()
415        assert verifyObject(IKofaPluggable, plugin)
416        assert verifyClass(IKofaPluggable, AccessCodePlugin)
417
418    def test_update_w_ac_container(self):
419        # The plugin changes nothing, if there is already a container
420        plugin = AccessCodePlugin()
421        site = self.app
422        logger = site.logger
423        accesscodes = site['accesscodes']
424        plugin.update(site, 'app', logger)
425        assert site['accesscodes'] is accesscodes
426
427    def test_update_wo_ac_container(self):
428        # The plugin creates a new accesscodes container if it is missing
429        plugin = AccessCodePlugin()
430        site = self.app
431        logger = site.logger
432        del site['accesscodes']
433        plugin.update(site, 'app', logger)
434        assert 'accesscodes' in site
435
436checker = renormalizing.RENormalizing([
437        (re.compile('[\d]{10}'), '<10-DIGITS>'),
438        ])
439
440
441def test_suite():
442    suite = unittest.TestSuite()
443    for testcase in [
444        AccessCodeHelpersTests,
445        AccessCodeTests,
446        AccessCodeBatchTests,
447        AccessCodeBatchContainerTests,
448        AccessCodePluginTests,
449        ]:
450        suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testcase))
451    for filename in [
452        #'accesscodes.txt',
453        'browser.txt'
454        ]:
455        path = os.path.join(
456            os.path.dirname(os.path.dirname(__file__)), filename)
457        test = doctest.DocFileSuite(
458            path,
459            module_relative=False,
460            setUp=setUp, tearDown=tearDown,
461            globs = dict(getRootFolder = getRootFolder),
462            optionflags = doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE,
463            checker = checker,
464            )
465        test.layer = FunctionalLayer
466        suite.addTest(test)
467    return suite
Note: See TracBrowser for help on using the repository browser.