source: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_student.py @ 8422

Last change on this file since 8422 was 8403, checked in by uli, 13 years ago

Backup student data when a student is deleted (blocking of student_id yet missing).

  • Property svn:keywords set to Id
File size: 8.3 KB
Line 
1## $Id: test_student.py 8403 2012-05-09 16:43:10Z uli $
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##
18"""Tests for students and related.
19"""
20import os
21import re
22from cStringIO import StringIO
23from datetime import tzinfo
24from zope.component import getUtility
25from zope.component.interfaces import IFactory
26from zope.interface import verify
27from waeup.kofa.interfaces import IExtFileStore, IFileStoreNameChooser
28from waeup.kofa.students.export import EXPORTER_NAMES
29from waeup.kofa.students.student import (
30    Student, StudentFactory, handle_student_removed)
31from waeup.kofa.students.studycourse import StudentStudyCourse
32from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket
33from waeup.kofa.students.payments import StudentPaymentsContainer
34from waeup.kofa.students.accommodation import StudentAccommodation, BedTicket
35from waeup.kofa.students.interfaces import (
36    IStudent, IStudentStudyCourse, IStudentPaymentsContainer,
37    IStudentAccommodation, IStudentStudyLevel, ICourseTicket, IBedTicket)
38from waeup.kofa.students.tests.test_batching import StudentImportExportSetup
39from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
40from waeup.kofa.university.department import Department
41
42class StudentTest(FunctionalTestCase):
43
44    layer = FunctionalLayer
45
46    def setUp(self):
47        super(StudentTest, self).setUp()
48        self.student = Student()
49        self.student.firstname = u'Anna'
50        self.student.lastname = u'Tester'
51        self.studycourse = StudentStudyCourse()
52        self.studylevel = StudentStudyLevel()
53        self.courseticket = CourseTicket()
54        self.payments = StudentPaymentsContainer()
55        self.accommodation = StudentAccommodation()
56        self.bedticket = BedTicket()
57        return
58
59    def tearDown(self):
60        super(StudentTest, self).tearDown()
61        return
62
63    def test_interfaces(self):
64        verify.verifyClass(IStudent, Student)
65        verify.verifyObject(IStudent, self.student)
66        verify.verifyClass(IStudentStudyCourse, StudentStudyCourse)
67        verify.verifyObject(IStudentStudyCourse, self.studycourse)
68        verify.verifyObject(IStudentStudyLevel, self.studylevel)
69        verify.verifyObject(ICourseTicket, self.courseticket)
70        verify.verifyClass(IStudentPaymentsContainer, StudentPaymentsContainer)
71        verify.verifyObject(IStudentPaymentsContainer, self.payments)
72        verify.verifyClass(IStudentAccommodation, StudentAccommodation)
73        verify.verifyObject(IStudentAccommodation, self.accommodation)
74        verify.verifyClass(IBedTicket, BedTicket)
75        verify.verifyObject(IBedTicket, self.bedticket)
76        return
77
78    def test_base(self):
79        department = Department()
80        studycourse = StudentStudyCourse()
81        self.assertRaises(
82            TypeError, studycourse.addStudentStudyLevel, department)
83        studylevel = StudentStudyLevel()
84        self.assertRaises(
85            TypeError, studylevel.addCourseTicket, department)
86
87    def test_booking_date(self):
88        isinstance(self.bedticket.booking_date.tzinfo, tzinfo)
89        self.assertEqual(self.bedticket.booking_date.tzinfo, None)
90        return
91
92class StudentRemovalTests(StudentImportExportSetup):
93    # Test handle_student_removed
94    #
95    # This is a complex action updating several CSV files and moving
96    # stored files to a backup location.
97    #
98    # These tests make no assumptions about the CSV files except that
99    # they contain a deletion timestamp at end of each data row
100
101    layer = FunctionalLayer
102
103    def setUp(self):
104        super(StudentRemovalTests, self).setUp()
105        self.setup_for_export()
106        return
107
108    def create_passport_img(self, student):
109        # create some faked passport file for `student`
110        storage = getUtility(IExtFileStore)
111        file_id = IFileStoreNameChooser(student).chooseName(
112            attr='passport.jpg')
113        storage.createFile(file_id, StringIO('I am a fake image.'))
114
115    def test_backup_single_student_data(self):
116        # when a single student is removed, the data is backed up.
117        self.setup_student(self.student)
118        # Add a fake image
119        self.create_passport_img(self.student)
120        handle_student_removed(self.student, None)
121        del_dir = self.app['datacenter'].deleted_path
122        del_img_path = os.path.join(
123            del_dir, 'media', 'students', 'A', 'A111111',
124            'passport_A111111.jpg')
125
126        # The image was copied over
127        self.assertTrue(os.path.isfile(del_img_path))
128        self.assertEqual(
129            open(del_img_path, 'rb').read(),
130            'I am a fake image.')
131
132        # The student data were put into CSV files
133        for name in EXPORTER_NAMES:
134            csv_path = os.path.join(del_dir, '%s.csv' % name)
135            self.assertTrue(os.path.isfile(csv_path))
136            contents = open(csv_path, 'rb').read().split('\r\n')
137            # We expect 3 lines output including a linebreak at end of file.
138            self.assertEqual(len(contents), 3)
139        return
140
141    def test_backup_append_csv(self):
142        # when several students are removed, existing CSVs are appended
143        self.setup_student(self.student)
144        # Add a fake image
145        self.create_passport_img(self.student)
146        del_dir = self.app['datacenter'].deleted_path
147        # put fake data into students.csv with trailing linebreak
148        students_csv = os.path.join(del_dir, 'students.csv')
149        open(students_csv, 'wb').write('line1\r\nline2\r\n')
150        handle_student_removed(self.student, None)
151        contents = open(students_csv, 'rb').read().split('\r\n')
152        # there should be 4 lines in result csv (including trailing linebreak)
153        self.assertEqual(len(contents), 4)
154        return
155
156    def test_old_files_removed(self):
157        # make sure old files are not accessible any more
158        self.setup_student(self.student)
159        # Add a fake image
160        self.create_passport_img(self.student)
161        # make sure we can access the image before removal
162        file_store = getUtility(IExtFileStore)
163        image = file_store.getFileByContext(self.student, attr='passport.jpg')
164        self.assertTrue(image is not None)
165
166        # remove image (hopefully)
167        handle_student_removed(self.student, None)
168
169        # the is not accessible anymore
170        image = file_store.getFileByContext(self.student, attr='passport.jpg')
171        self.assertEqual(image, None)
172        return
173
174    def test_csv_file_entries_have_timestamp(self):
175        # each row in written csv files has a ``del_date`` column to
176        # tell when the associated student was deleted
177        self.setup_student(self.student)
178        del_dir = self.app['datacenter'].deleted_path
179        students_csv = os.path.join(del_dir, 'students.csv')
180        handle_student_removed(self.student, None)
181        contents = open(students_csv, 'rb').read().split('\r\n')
182        # the CSV header ends with a ``del_date`` column
183        self.assertTrue(contents[0].endswith(',del_date'))
184        # each line ends with an UTC timestamp
185        timestamp = contents[1][-23:]
186        self.assertTrue(re.match(
187            '^\d\d-\d\d-\d\d \d\d:\d\d:\d\d\+00:00$', timestamp))
188        return
189
190
191class StudentFactoryTest(FunctionalTestCase):
192
193    layer = FunctionalLayer
194
195    def setUp(self):
196        super(StudentFactoryTest, self).setUp()
197        self.factory = StudentFactory()
198
199    def tearDown(self):
200        super(StudentFactoryTest, self).tearDown()
201
202    def test_interfaces(self):
203        verify.verifyClass(IFactory, StudentFactory)
204        verify.verifyObject(IFactory, self.factory)
205
206    def test_factory(self):
207        obj = self.factory()
208        assert isinstance(obj, Student)
209
210    def test_getInterfaces(self):
211        implemented_by = self.factory.getInterfaces()
212        assert implemented_by.isOrExtends(IStudent)
Note: See TracBrowser for help on using the repository browser.