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

Last change on this file since 8491 was 8467, checked in by uli, 13 years ago

Use fileformat recognition when uploading student files.

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