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

Last change on this file since 6776 was 6728, checked in by uli, 13 years ago

Use logger-aware components from w.s.testing to remove loggers
properly.

File size: 15.7 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_csv_export(self):
291        # Make sure CSV export of accesscodes works
292        batch = self.batch
293        invalidate_accesscode('FOO-1-11111111', comment='comment with "quotes"')
294        disable_accesscode('FOO-1-33333333')
295        basename = batch.archive()
296        result_path = os.path.join(batch._getStoragePath(), basename)
297        expected = '''
298"prefix","serial","ac","state","history"
299"FOO","9.99","1","0"
300"FOO","0","FOO-1-11111111","used","<YYYY-MM-DD hh:mm:ss> - ..."
301"FOO","1","FOO-1-22222222","initialized","<YYYY-MM-DD hh:mm:ss> - ..."
302"FOO","2","FOO-1-33333333","disabled","<YYYY-MM-DD hh:mm:ss> - ..."
303'''[1:]
304        contents = open(result_path, 'rb').read()
305        self.assertMatches(expected, contents)
306
307class AccessCodeBatchContainerTests(FunctionalTestCase):
308    # Tests for AccessCodeContainer class
309
310    layer = FunctionalLayer
311
312    def setUp(self):
313        super(AccessCodeBatchContainerTests, self).setUp()
314
315        # Prepopulate ZODB
316        app = University()
317        self.dc_root = tempfile.mkdtemp()
318        app['datacenter'].setStoragePath(self.dc_root)
319
320        # Prepopulate the ZODB...
321        self.getRootFolder()['app'] = app
322        self.app = self.getRootFolder()['app']
323
324        self.import_sample1_src = os.path.join(
325            os.path.dirname(__file__), 'sample_import.csv')
326
327        batch = AccessCodeBatch(    # create batch with zero entries
328            datetime.now(), 'testuser', 'BAR', 9.99, 0)
329        self.app['accesscodes'].addBatch(batch)
330
331        self.ac1 = AccessCode(0, '11111111')
332        self.ac2 = AccessCode(1, '22222222')
333        self.ac3 = AccessCode(2, '33333333')
334        batch['BAR-1-11111111'] = self.ac1
335        batch['BAR-1-22222222'] = self.ac2
336        batch['BAR-1-33333333'] = self.ac3
337        self.batch = batch
338
339        setSite(self.app)
340        return
341
342    def tearDown(self):
343        shutil.rmtree(self.dc_root)
344        super(AccessCodeBatchContainerTests, self).tearDown()
345        return
346
347    def test_iface(self):
348        accesscodes = AccessCodeBatchContainer()
349        assert verifyObject(IAccessCodeBatchContainer, accesscodes)
350        assert verifyClass(IAccessCodeBatchContainer, AccessCodeBatchContainer)
351
352    def test_csv_import(self):
353        # Make sure we can reimport sample data from local sample_import.csv
354        batchcontainer = self.app['accesscodes']
355        shutil.copyfile(        # Copy sample to import dir
356            os.path.join(os.path.dirname(__file__), 'sample_import.csv'),
357            os.path.join(batchcontainer._getStoragePath(), 'sample_import.csv')
358            )
359        batchcontainer.reimport('sample_import.csv')
360        batch = batchcontainer.get(u'FOO-1', None)
361        self.assertTrue(batch is not None)
362        keys = [x for x in batch.keys()]
363        self.assertEqual(
364            keys,
365            [u'FOO-1-11111111', u'FOO-1-22222222', u'FOO-1-33333333'])
366
367    def test_getAccessCode(self):
368        batchcontainer = self.app['accesscodes']
369        result1 = batchcontainer.getAccessCode('BAR-1-11111111')
370        result2 = batchcontainer.getAccessCode('BAR-1-not-existent')
371        assert isinstance(result1, AccessCode)
372        assert result2 is None
373
374    def test_disableAccessCode(self):
375        batchcontainer = self.app['accesscodes']
376        result1 = batchcontainer.disable('BAR-1-11111111')
377        result2 = batchcontainer.disable('BAR-1-not-existent')
378        assert self.ac1.state is DISABLED
379        assert result2 is None
380
381    def test_enableAccessCode(self):
382        batchcontainer = self.app['accesscodes']
383        batchcontainer.disable('BAR-1-11111111')
384        result1 = batchcontainer.enable('BAR-1-11111111')
385        result2 = batchcontainer.enable('BAR-1-not-existent')
386        assert self.ac1.state is INITIALIZED
387        assert result2 is None
388
389class AccessCodePluginTests(FunctionalTestCase):
390    # Tests for AccessCodeContainer class
391
392    layer = FunctionalLayer
393
394    def setUp(self):
395        super(AccessCodePluginTests, self).setUp()
396
397        # Prepopulate ZODB
398        app = University()
399        self.dc_root = tempfile.mkdtemp()
400        app['datacenter'].setStoragePath(self.dc_root)
401
402        # Prepopulate the ZODB...
403        self.getRootFolder()['app'] = app
404        self.app = self.getRootFolder()['app']
405
406    def tearDown(self):
407        shutil.rmtree(self.dc_root)
408        super(AccessCodePluginTests, self).tearDown()
409        return
410
411    def test_iface(self):
412        plugin = AccessCodePlugin()
413        assert verifyObject(IWAeUPSIRPPluggable, plugin)
414        assert verifyClass(IWAeUPSIRPPluggable, AccessCodePlugin)
415
416    def test_update_w_ac_container(self):
417        # The plugin changes nothing, if there is already a container
418        plugin = AccessCodePlugin()
419        site = self.app
420        logger = site.logger
421        accesscodes = site['accesscodes']
422        plugin.update(site, 'app', logger)
423        assert site['accesscodes'] is accesscodes
424
425    def test_update_wo_ac_container(self):
426        # The plugin creates a new accesscodes container if it is missing
427        plugin = AccessCodePlugin()
428        site = self.app
429        logger = site.logger
430        del site['accesscodes']
431        plugin.update(site, 'app', logger)
432        assert 'accesscodes' in site
433
434checker = renormalizing.RENormalizing([
435        (re.compile('[\d]{10}'), '<10-DIGITS>'),
436        ])
437
438
439def test_suite():
440    suite = unittest.TestSuite()
441    for testcase in [
442        AccessCodeHelpersTests,
443        AccessCodeTests,
444        AccessCodeBatchTests,
445        AccessCodeBatchContainerTests,
446        AccessCodePluginTests,
447        ]:
448        suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testcase))
449    for filename in [
450        #'accesscodes.txt',
451        'browser.txt'
452        ]:
453        path = os.path.join(
454            os.path.dirname(os.path.dirname(__file__)), filename)
455        test = doctest.DocFileSuite(
456            path,
457            module_relative=False,
458            setUp=setUp, tearDown=tearDown,
459            globs = dict(getRootFolder = getRootFolder),
460            optionflags = doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE,
461            checker = checker,
462            )
463        test.layer = FunctionalLayer
464        suite.addTest(test)
465    return suite
Note: See TracBrowser for help on using the repository browser.