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

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

Rename applicants.py, accesscodes.py and students.py modules. Their names should be singular like in the university package.

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