source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_customer.py @ 12342

Last change on this file since 12342 was 12297, checked in by Henrik Bettermann, 10 years ago

Make EXPORTER_NAMES tuples customizable. We have many new subobject classes in custom packages.

  • Property svn:keywords set to Id
File size: 8.2 KB
Line 
1## $Id: test_customer.py 12297 2014-12-22 16:42:50Z henrik $
2##
3## Copyright (C) 2014 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##
18"""Tests for customers and related.
19"""
20import os
21import re
22import unittest
23import grok
24from cStringIO import StringIO
25from datetime import tzinfo
26from zope.component import getUtility, queryUtility, createObject
27from zope.catalog.interfaces import ICatalog
28from zope.component.interfaces import IFactory
29from zope.event import notify
30from zope.interface import verify
31from zope.schema.interfaces import RequiredMissing
32from waeup.ikoba.interfaces import IExtFileStore, IFileStoreNameChooser
33from waeup.ikoba.customers.customer import (
34    Customer, CustomerFactory, handle_customer_removed, path_from_custid)
35from waeup.ikoba.customers.interfaces import (
36    ICustomer, ICustomerNavigation, ICustomersUtils)
37from waeup.ikoba.customers.tests.test_batching import CustomerImportExportSetup
38from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
39
40class HelperTests(unittest.TestCase):
41    # Tests for helper functions in customer module.
42
43    def test_path_from_custid(self):
44        # make sure we get predictable paths from customer ids.
45        self.assertEqual(
46            path_from_custid('K1000000'), u'01000/K1000000')
47        self.assertEqual(
48            path_from_custid('K1234567'), u'01234/K1234567')
49        self.assertEqual(
50            path_from_custid('K12345678'), u'12345/K12345678')
51        # The algorithm works also for overlong numbers, just to be
52        # sure.
53        self.assertEqual(
54            path_from_custid('K123456789'), u'123456/K123456789')
55        # low numbers (< 10**6) are treated special: they get max. of
56        # 10,000 entries. That's mainly because of old customers
57        # migrated into our portal.
58        self.assertEqual(
59            path_from_custid('KM123456'), u'00120/KM123456')
60        return
61
62class CustomerTest(FunctionalTestCase):
63
64    layer = FunctionalLayer
65
66    def setUp(self):
67        super(CustomerTest, self).setUp()
68        self.customer = Customer()
69        self.customer.firstname = u'Anna'
70        self.customer.lastname = u'Tester'
71        return
72
73    def tearDown(self):
74        super(CustomerTest, self).tearDown()
75        return
76
77    def test_interfaces(self):
78        verify.verifyClass(ICustomer, Customer)
79        verify.verifyClass(ICustomerNavigation, Customer)
80        verify.verifyObject(ICustomer, self.customer)
81        verify.verifyObject(ICustomerNavigation, self.customer)
82        return
83
84    #def test_base(self):
85    #    department = Department()
86    #    studycourse = CustomerStudyCourse()
87    #    self.assertRaises(
88    #        TypeError, studycourse.addCustomerStudyLevel, department)
89    #    studylevel = CustomerStudyLevel()
90    #    self.assertRaises(
91    #        TypeError, studylevel.addCourseTicket, department, department)
92
93
94class CustomerRemovalTests(CustomerImportExportSetup):
95    # Test handle_customer_removed
96    #
97    # This is a complex action updating several CSV files and moving
98    # stored files to a backup location.
99    #
100    # These tests make no assumptions about the CSV files except that
101    # they contain a deletion timestamp at end of each data row
102
103    layer = FunctionalLayer
104
105    def setUp(self):
106        super(CustomerRemovalTests, self).setUp()
107        self.setup_for_export()
108        return
109
110    def create_passport_img(self, customer):
111        # create some passport file for `customer`
112        storage = getUtility(IExtFileStore)
113        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
114        self.image_contents = open(image_path, 'rb').read()
115        file_id = IFileStoreNameChooser(customer).chooseName(
116            attr='passport.jpg')
117        storage.createFile(file_id, StringIO(self.image_contents))
118
119    def test_backup_single_customer_data(self):
120        # when a single customer is removed, the data is backed up.
121        self.setup_customer(self.customer)
122        # Add a fake image
123        self.create_passport_img(self.customer)
124        handle_customer_removed(self.customer, None)
125        del_dir = self.app['datacenter'].deleted_path
126        del_img_path = os.path.join(
127            del_dir, 'media', 'customers', '00110', 'A111111',
128            'passport_A111111.jpg')
129
130        # The image was copied over
131        self.assertTrue(os.path.isfile(del_img_path))
132        self.assertEqual(
133            open(del_img_path, 'rb').read(),
134            self.image_contents)
135
136        # The customer data were put into CSV files
137        for name in getUtility(ICustomersUtils).EXPORTER_NAMES:
138            csv_path = os.path.join(del_dir, '%s.csv' % name)
139            self.assertTrue(os.path.isfile(csv_path))
140            contents = open(csv_path, 'rb').read().split('\r\n')
141            # We expect 3 lines output including a linebreak at end of file.
142            self.assertEqual(len(contents), 3)
143        return
144
145    def test_backup_append_csv(self):
146        # when several customers are removed, existing CSVs are appended
147        self.setup_customer(self.customer)
148        # Add a fake image
149        self.create_passport_img(self.customer)
150        del_dir = self.app['datacenter'].deleted_path
151        # put fake data into customers.csv with trailing linebreak
152        customers_csv = os.path.join(del_dir, 'customers.csv')
153        open(customers_csv, 'wb').write('line1\r\nline2\r\n')
154        handle_customer_removed(self.customer, None)
155        contents = open(customers_csv, 'rb').read().split('\r\n')
156        # there should be 4 lines in result csv (including trailing linebreak)
157        self.assertEqual(len(contents), 4)
158        return
159
160    def test_old_files_removed(self):
161        # make sure old files are not accessible any more
162        self.setup_customer(self.customer)
163        # Add a fake image
164        self.create_passport_img(self.customer)
165        # make sure we can access the image before removal
166        file_store = getUtility(IExtFileStore)
167        image = file_store.getFileByContext(self.customer, attr='passport.jpg')
168        self.assertTrue(image is not None)
169
170        # remove image (hopefully)
171        handle_customer_removed(self.customer, None)
172
173        # the is not accessible anymore
174        image = file_store.getFileByContext(self.customer, attr='passport.jpg')
175        self.assertEqual(image, None)
176        return
177
178    def test_csv_file_entries_have_timestamp(self):
179        # each row in written csv files has a ``del_date`` column to
180        # tell when the associated customer was deleted
181        self.setup_customer(self.customer)
182        del_dir = self.app['datacenter'].deleted_path
183        customers_csv = os.path.join(del_dir, 'customers.csv')
184        handle_customer_removed(self.customer, None)
185        contents = open(customers_csv, 'rb').read().split('\r\n')
186        # the CSV header ends with a ``del_date`` column
187        self.assertTrue(contents[0].endswith(',del_date'))
188        # each line ends with an UTC timestamp
189        timestamp = contents[1][-23:]
190        self.assertTrue(re.match(
191            '^\d\d-\d\d-\d\d \d\d:\d\d:\d\d\+00:00$', timestamp))
192        return
193
194class CustomerFactoryTest(FunctionalTestCase):
195
196    layer = FunctionalLayer
197
198    def setUp(self):
199        super(CustomerFactoryTest, self).setUp()
200        self.factory = CustomerFactory()
201
202    def tearDown(self):
203        super(CustomerFactoryTest, self).tearDown()
204
205    def test_interfaces(self):
206        verify.verifyClass(IFactory, CustomerFactory)
207        verify.verifyObject(IFactory, self.factory)
208
209    def test_factory(self):
210        obj = self.factory()
211        assert isinstance(obj, Customer)
212
213    def test_getInterfaces(self):
214        implemented_by = self.factory.getInterfaces()
215        assert implemented_by.isOrExtends(ICustomer)
Note: See TracBrowser for help on using the repository browser.