source: main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/tests/test_accesscode.py @ 7059

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

Create empty school fee, clearance and hostel application AC batches during initialization of university instance. These batches start with 0.

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