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

Last change on this file was 12944, checked in by Henrik Bettermann, 9 years ago

Remove doctest. accesscode.txt is really outdated and has been replaced by Python tests.

  • Property svn:keywords set to Id
File size: 16.3 KB
RevLine 
[7195]1## $Id: test_accesscode.py 12944 2015-05-15 04:57:20Z henrik $
[6359]2##
[7195]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
[6359]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.
[7195]8##
[6359]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.
[7195]13##
[6359]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##
[6417]18import doctest
19import os
20import re
[6359]21import shutil
22import tempfile
23import unittest
[6417]24
[6414]25from datetime import datetime
[6378]26from hurry.workflow.interfaces import InvalidTransitionError, IWorkflowState
[8182]27from zope.component import getUtility
[6378]28from zope.component.hooks import setSite, clearSite
[6414]29from zope.interface.verify import verifyObject, verifyClass
[6417]30from zope.testing import renormalizing
[7811]31from waeup.kofa.app import University
[8182]32from waeup.kofa.interfaces import IObjectHistory, IKofaPluggable, IKofaUtils
[7811]33from waeup.kofa.testing import (
[6728]34    FunctionalLayer, FunctionalTestCase, setUp, tearDown, getRootFolder)
[7811]35from waeup.kofa.accesscodes.accesscode import (
[6414]36    AccessCodeBatch, get_access_code, invalidate_accesscode, AccessCode,
37    disable_accesscode, reenable_accesscode, fire_transition,
[6554]38    AccessCodeBatchContainer, AccessCodePlugin)
[7811]39from waeup.kofa.accesscodes.interfaces import (
[6414]40    IAccessCode, IAccessCodeBatch,  IAccessCodeBatchContainer,)
[7811]41from waeup.kofa.accesscodes.workflow import INITIALIZED, USED, DISABLED
[6359]42
[6417]43
[6544]44optionflags = (
45    doctest.REPORT_NDIFF + doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE)
[6417]46
[6359]47class AccessCodeHelpersTests(FunctionalTestCase):
[6413]48    # Tests for helpers like get_access_code, disable_accesscode, ...
[6359]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):
[6470]105        assert self.ac1.state != USED
[6374]106        result = invalidate_accesscode('APP-1-11111111')
[6470]107        assert self.ac1.state == USED
[6374]108        assert result is True
[6359]109
110    def test_disable_accesscode_unused(self):
[6378]111        # we can disable initialized acs
[6470]112        assert self.ac1.state != USED
[6359]113        disable_accesscode('APP-1-11111111')
[6470]114        assert self.ac1.state == DISABLED
[6359]115
116    def test_disable_accesscode_used(self):
[6378]117        # we can disable already used acs
[6470]118        assert self.ac1.state != DISABLED
[6359]119        invalidate_accesscode('APP-1-11111111')
120        disable_accesscode('APP-1-11111111')
[6470]121        assert self.ac1.state == DISABLED
[6374]122
123    def test_reenable_accesscode(self):
[6378]124        # we can reenable disabled acs
[6374]125        disable_accesscode('APP-1-11111111')
126        result = reenable_accesscode('APP-1-11111111')
127        assert result is True
[6470]128        assert self.ac1.state != USED
[6374]129
[6378]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
[6413]178
[6422]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]
[6414]185
[6422]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
[7719]191        assert msgs[-1].endswith('used by system')
[6422]192
[6414]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
[6423]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')
[6414]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):
[6423]227        # AccessCodes fullfill their iface promises.
[6414]228        ac = AccessCode('1', '12345678')
229        assert verifyObject(IAccessCode, ac)
230        assert verifyClass(IAccessCode, AccessCode)
231
[6423]232    def test_history(self):
233        # Access codes have a history.
234        match = re.match(
[8234]235            '^....-..-.. ..:..:.. .+ - initialized by system',
[6423]236            self.ac1.history)
237        assert match is not None
238
[6554]239    def test_cost(self):
[8321]240        # The cost of an access code will be stored by handle_batch_added
241        # right after the batch has been added to the ZODB. Thus after
242        # creation of the batch, cost is still 0.0
[6554]243        cost = self.ac1.cost
[8321]244        assert cost == 0.0
[6554]245
[6414]246class AccessCodeBatchTests(FunctionalTestCase):
247    # Tests for AccessCodeBatch class
248
249    layer = FunctionalLayer
250
251    def setUp(self):
252        super(AccessCodeBatchTests, self).setUp()
253
254        # Prepopulate ZODB
255        app = University()
256        self.dc_root = tempfile.mkdtemp()
257        app['datacenter'].setStoragePath(self.dc_root)
258
259        # Prepopulate the ZODB...
260        self.getRootFolder()['app'] = app
261        self.app = self.getRootFolder()['app']
262
[6544]263        batch = AccessCodeBatch(    # create batch with zero entries
[8194]264            datetime.utcnow(), 'testuser', 'FOO', 9.99, 0)
[6544]265        self.app['accesscodes'].addBatch(batch)
266
267        self.ac1 = AccessCode(0, '11111111')
[8321]268        self.ac1.cost = 2345.0
[6544]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
[6414]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
[6932]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
[6544]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 = '''
[9260]303"prefix","serial","ac","state","history","cost","owner"
[6544]304"FOO","9.99","1","0"
[9260]305"FOO","0","FOO-1-11111111","used","<YYYY-MM-DD hh:mm:ss> UTC - ...","2345.0",""
[8321]306"FOO","1","FOO-1-22222222","initialized","<YYYY-MM-DD hh:mm:ss> UTC - ...",""
307"FOO","2","FOO-1-33333333","disabled","<YYYY-MM-DD hh:mm:ss> UTC - ...",""
[6544]308'''[1:]
309        contents = open(result_path, 'rb').read()
310        self.assertMatches(expected, contents)
311
[6414]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
[6544]329        self.import_sample1_src = os.path.join(
330            os.path.dirname(__file__), 'sample_import.csv')
331
[6554]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
[6414]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)
[6417]356
[10448]357    def test_existing_batches(self):
358        self.assertEqual(sorted(self.app['accesscodes'].keys()),
359            [u'BAR-1', u'CLR-0', u'HOS-0', u'SFE-0', u'TSC-0'])
360
[6544]361    def test_csv_import(self):
362        # Make sure we can reimport sample data from local sample_import.csv
363        batchcontainer = self.app['accesscodes']
364        shutil.copyfile(        # Copy sample to import dir
365            os.path.join(os.path.dirname(__file__), 'sample_import.csv'),
366            os.path.join(batchcontainer._getStoragePath(), 'sample_import.csv')
367            )
368        batchcontainer.reimport('sample_import.csv')
369        batch = batchcontainer.get(u'FOO-1', None)
370        self.assertTrue(batch is not None)
371        keys = [x for x in batch.keys()]
372        self.assertEqual(
373            keys,
374            [u'FOO-1-11111111', u'FOO-1-22222222', u'FOO-1-33333333'])
[8321]375        # Also cost has been stored correctly
376        self.assertEqual(batch['FOO-1-11111111'].cost,1000.0)
[6417]377
[6554]378    def test_getAccessCode(self):
379        batchcontainer = self.app['accesscodes']
380        result1 = batchcontainer.getAccessCode('BAR-1-11111111')
381        result2 = batchcontainer.getAccessCode('BAR-1-not-existent')
382        assert isinstance(result1, AccessCode)
383        assert result2 is None
384
385    def test_disableAccessCode(self):
386        batchcontainer = self.app['accesscodes']
387        result1 = batchcontainer.disable('BAR-1-11111111')
388        result2 = batchcontainer.disable('BAR-1-not-existent')
389        assert self.ac1.state is DISABLED
390        assert result2 is None
391
392    def test_enableAccessCode(self):
393        batchcontainer = self.app['accesscodes']
394        batchcontainer.disable('BAR-1-11111111')
395        result1 = batchcontainer.enable('BAR-1-11111111')
396        result2 = batchcontainer.enable('BAR-1-not-existent')
397        assert self.ac1.state is INITIALIZED
398        assert result2 is None
399
400class AccessCodePluginTests(FunctionalTestCase):
401    # Tests for AccessCodeContainer class
402
403    layer = FunctionalLayer
404
405    def setUp(self):
406        super(AccessCodePluginTests, self).setUp()
407
408        # Prepopulate ZODB
409        app = University()
410        self.dc_root = tempfile.mkdtemp()
411        app['datacenter'].setStoragePath(self.dc_root)
412
413        # Prepopulate the ZODB...
414        self.getRootFolder()['app'] = app
415        self.app = self.getRootFolder()['app']
416
417    def tearDown(self):
418        shutil.rmtree(self.dc_root)
419        super(AccessCodePluginTests, self).tearDown()
420        return
421
422    def test_iface(self):
423        plugin = AccessCodePlugin()
[7819]424        assert verifyObject(IKofaPluggable, plugin)
425        assert verifyClass(IKofaPluggable, AccessCodePlugin)
[6554]426
427    def test_update_w_ac_container(self):
428        # The plugin changes nothing, if there is already a container
429        plugin = AccessCodePlugin()
430        site = self.app
431        logger = site.logger
432        accesscodes = site['accesscodes']
433        plugin.update(site, 'app', logger)
434        assert site['accesscodes'] is accesscodes
435
436    def test_update_wo_ac_container(self):
437        # The plugin creates a new accesscodes container if it is missing
438        plugin = AccessCodePlugin()
439        site = self.app
440        logger = site.logger
441        del site['accesscodes']
442        plugin.update(site, 'app', logger)
443        assert 'accesscodes' in site
444
[6417]445checker = renormalizing.RENormalizing([
446        (re.compile('[\d]{10}'), '<10-DIGITS>'),
447        ])
448
449
450def test_suite():
451    suite = unittest.TestSuite()
452    for testcase in [
453        AccessCodeHelpersTests,
454        AccessCodeTests,
455        AccessCodeBatchTests,
456        AccessCodeBatchContainerTests,
[6554]457        AccessCodePluginTests,
[6417]458        ]:
459        suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testcase))
460    for filename in [
[12943]461        '../doctests/accesscode_browser.txt'
[6417]462        ]:
463        path = os.path.join(
464            os.path.dirname(os.path.dirname(__file__)), filename)
465        test = doctest.DocFileSuite(
466            path,
467            module_relative=False,
468            setUp=setUp, tearDown=tearDown,
469            globs = dict(getRootFolder = getRootFolder),
470            optionflags = doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE,
471            checker = checker,
472            )
473        test.layer = FunctionalLayer
474        suite.addTest(test)
475    return suite
Note: See TracBrowser for help on using the repository browser.