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

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

Replace the term 'WAeUP' by SIRP which is a WAeUP product.

  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1## $Id: test_accesscode.py 7321 2011-12-10 06:15: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##
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.hooks import setSite, clearSite
28from zope.interface.verify import verifyObject, verifyClass
29from zope.testing import renormalizing
30from waeup.sirp.app import University
31from waeup.sirp.interfaces import IObjectHistory, ISIRPPluggable
32from waeup.sirp.testing import (
33    FunctionalLayer, FunctionalTestCase, setUp, tearDown, getRootFolder)
34from waeup.sirp.accesscodes.accesscode import (
35    AccessCodeBatch, get_access_code, invalidate_accesscode, AccessCode,
36    disable_accesscode, reenable_accesscode, fire_transition,
37    AccessCodeBatchContainer, AccessCodePlugin)
38from waeup.sirp.accesscodes.interfaces import (
39    IAccessCode, IAccessCodeBatch,  IAccessCodeBatchContainer,)
40from waeup.sirp.accesscodes.workflow import INITIALIZED, USED, DISABLED
41
42
43optionflags = (
44    doctest.REPORT_NDIFF + doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE)
45
46class AccessCodeHelpersTests(FunctionalTestCase):
47    # Tests for helpers like get_access_code, disable_accesscode, ...
48
49    layer = FunctionalLayer
50
51    def setUp(self):
52        super(AccessCodeHelpersTests, self).setUp()
53
54        # Prepopulate ZODB
55        app = University()
56        self.dc_root = tempfile.mkdtemp()
57        app['datacenter'].setStoragePath(self.dc_root)
58
59        # Prepopulate the ZODB...
60        self.getRootFolder()['app'] = app
61        self.app = self.getRootFolder()['app']
62
63        # Create batch
64        batch = AccessCodeBatch('now', 'manfred', 'APP', 6.6, 0)
65        self.app['accesscodes'].addBatch(batch)
66
67        # Fill batch with accesscodes
68        batch.addAccessCode(0, '11111111')
69        batch.addAccessCode(1, '22222222')
70        batch.addAccessCode(2, '33333333')
71        self.ac1 = batch.getAccessCode('APP-1-11111111')
72        self.ac2 = batch.getAccessCode('APP-1-22222222')
73        self.ac3 = batch.getAccessCode('APP-1-33333333')
74
75        setSite(self.app)
76        return
77
78    def tearDown(self):
79        shutil.rmtree(self.dc_root)
80        super(AccessCodeHelpersTests, self).tearDown()
81        return
82
83    def test_get_access_code(self):
84        ac = get_access_code('APP-1-11111111')
85        assert ac is self.ac1
86
87    def test_get_access_code_not_string(self):
88        ac = get_access_code(object())
89        assert ac is None
90
91    def test_get_access_code_no_proper_pin(self):
92        ac = get_access_code('APP-without_pin')
93        assert ac is None
94
95    def test_get_access_code_invalid_batch_num(self):
96        ac = get_access_code('APP-invalid-11111111')
97        assert ac is None
98
99    def test_get_access_code_invalid_pin(self):
100        ac = get_access_code('APP-1-notexistent')
101        assert ac is None
102
103    def test_invalidate_accesscode(self):
104        assert self.ac1.state != USED
105        result = invalidate_accesscode('APP-1-11111111')
106        assert self.ac1.state == USED
107        assert result is True
108
109    def test_disable_accesscode_unused(self):
110        # we can disable initialized acs
111        assert self.ac1.state != USED
112        disable_accesscode('APP-1-11111111')
113        assert self.ac1.state == DISABLED
114
115    def test_disable_accesscode_used(self):
116        # we can disable already used acs
117        assert self.ac1.state != DISABLED
118        invalidate_accesscode('APP-1-11111111')
119        disable_accesscode('APP-1-11111111')
120        assert self.ac1.state == DISABLED
121
122    def test_reenable_accesscode(self):
123        # we can reenable disabled acs
124        disable_accesscode('APP-1-11111111')
125        result = reenable_accesscode('APP-1-11111111')
126        assert result is True
127        assert self.ac1.state != USED
128
129    def test_fire_transition(self):
130        # we can fire transitions generally
131        fire_transition('APP-1-11111111', 'use')
132        assert IWorkflowState(self.ac1).getState() is USED
133
134    def test_fire_transition_toward(self):
135        # the `toward` keyword is respected
136        fire_transition('APP-1-11111111', DISABLED, toward=True)
137        assert IWorkflowState(self.ac1).getState() is DISABLED
138
139    def test_fire_transition_no_site(self):
140        # when no site is available, we will get a TypeError
141        clearSite()
142        self.assertRaises(
143            KeyError,
144            fire_transition, 'APP-1-11111111', 'use')
145
146    def test_fire_transition_broken_ac_id(self):
147        # if we get an invalid access code id (of wrong format) we get
148        # ValueErrors
149        self.assertRaises(
150            ValueError,
151            fire_transition, '11111111', 'use')
152
153    def test_fire_transition_invalid_batch_id(self):
154        # if we request a non-existent batch_id, we'll get a KeyError
155        self.assertRaises(
156            KeyError,
157            fire_transition, 'FOO-1-11111111', 'use')
158
159    def test_fire_transition_invalid_ac(self):
160        # if we request a non-exitent access-code, we'll get a KeyError
161        self.assertRaises(
162            KeyError,
163            fire_transition, 'APP-1-NONSENSE', 'use')
164
165    def test_fire_transition_undef_trans_id(self):
166        # asking for undefined transition id means a KeyError
167        self.assertRaises(
168            KeyError,
169            fire_transition, 'APP-1-11111111', 'nonsense')
170
171    def test_fire_transition_invalid_transition(self):
172        # asking for a forbidden transition will result in
173        # InvalidTransitionError
174        self.assertRaises(
175            InvalidTransitionError,
176            fire_transition, 'APP-1-11111111', 'init') # already initialized
177
178    def test_fire_transition_comment(self):
179        # when we request a comment, it will also appear in history
180        fire_transition('APP-1-11111111', 'use', comment='Hi there!')
181        history = IObjectHistory(self.ac1)
182        msgs = history.messages
183        assert 'Hi there!' in msgs[-1]
184
185    def test_fire_transition_no_comment(self):
186        # without comment, the history should be without trailing garbage
187        fire_transition('APP-1-11111111', 'use')
188        history = IObjectHistory(self.ac1)
189        msgs = history.messages
190        assert msgs[-1].endswith('AC used by system')
191
192class AccessCodeTests(FunctionalTestCase):
193    # Tests for AccessCode class
194
195    layer = FunctionalLayer
196
197    def setUp(self):
198        super(AccessCodeTests, self).setUp()
199
200        # Prepopulate ZODB
201        app = University()
202        self.dc_root = tempfile.mkdtemp()
203        app['datacenter'].setStoragePath(self.dc_root)
204
205        # Prepopulate the ZODB...
206        self.getRootFolder()['app'] = app
207        self.app = self.getRootFolder()['app']
208
209        # Create batch
210        batch = AccessCodeBatch('now', 'manfred', 'APP', 6.6, 0)
211        self.app['accesscodes'].addBatch(batch)
212
213        # Fill batch with accesscodes
214        batch.addAccessCode(0, '11111111')
215
216        self.ac1 = batch.getAccessCode('APP-1-11111111')
217        setSite(self.app)
218        return
219
220    def tearDown(self):
221        shutil.rmtree(self.dc_root)
222        super(AccessCodeTests, self).tearDown()
223        return
224
225    def test_iface(self):
226        # AccessCodes fullfill their iface promises.
227        ac = AccessCode('1', '12345678')
228        assert verifyObject(IAccessCode, ac)
229        assert verifyClass(IAccessCode, AccessCode)
230
231    def test_history(self):
232        # Access codes have a history.
233        match = re.match(
234            '^....-..-.. ..:..:.. - AC initialized by system',
235            self.ac1.history)
236        assert match is not None
237
238    def test_cost(self):
239        # We get the cost set in batch
240        cost = self.ac1.cost
241        assert cost == 6.6
242
243class AccessCodeBatchTests(FunctionalTestCase):
244    # Tests for AccessCodeBatch class
245
246    layer = FunctionalLayer
247
248    def setUp(self):
249        super(AccessCodeBatchTests, self).setUp()
250
251        # Prepopulate ZODB
252        app = University()
253        self.dc_root = tempfile.mkdtemp()
254        app['datacenter'].setStoragePath(self.dc_root)
255
256        # Prepopulate the ZODB...
257        self.getRootFolder()['app'] = app
258        self.app = self.getRootFolder()['app']
259
260        batch = AccessCodeBatch(    # create batch with zero entries
261            datetime.now(), 'testuser', 'FOO', 9.99, 0)
262        self.app['accesscodes'].addBatch(batch)
263
264        self.ac1 = AccessCode(0, '11111111')
265        self.ac2 = AccessCode(1, '22222222')
266        self.ac3 = AccessCode(2, '33333333')
267        batch['FOO-1-11111111'] = self.ac1
268        batch['FOO-1-22222222'] = self.ac2
269        batch['FOO-1-33333333'] = self.ac3
270        self.batch = batch
271
272        setSite(self.app)
273        return
274
275    def tearDown(self):
276        shutil.rmtree(self.dc_root)
277        super(AccessCodeBatchTests, self).tearDown()
278        return
279
280    def test_iface(self):
281        batch = AccessCodeBatch(
282            datetime(2009, 12, 23), 'Fred','APP', 12.12, 3, num=10)
283        assert verifyObject(IAccessCodeBatch, batch)
284        assert verifyClass(IAccessCodeBatch, AccessCodeBatch)
285
286    def test_container_contents(self):
287        assert 'SFE-0' in self.app['accesscodes']
288        assert 'HOS-0' in self.app['accesscodes']
289        assert 'CLR-0' in self.app['accesscodes']
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(ISIRPPluggable, plugin)
415        assert verifyClass(ISIRPPluggable, 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
439
440def test_suite():
441    suite = unittest.TestSuite()
442    for testcase in [
443        AccessCodeHelpersTests,
444        AccessCodeTests,
445        AccessCodeBatchTests,
446        AccessCodeBatchContainerTests,
447        AccessCodePluginTests,
448        ]:
449        suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testcase))
450    for filename in [
451        #'accesscodes.txt',
452        'browser.txt'
453        ]:
454        path = os.path.join(
455            os.path.dirname(os.path.dirname(__file__)), filename)
456        test = doctest.DocFileSuite(
457            path,
458            module_relative=False,
459            setUp=setUp, tearDown=tearDown,
460            globs = dict(getRootFolder = getRootFolder),
461            optionflags = doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE,
462            checker = checker,
463            )
464        test.layer = FunctionalLayer
465        suite.addTest(test)
466    return suite
Note: See TracBrowser for help on using the repository browser.