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

Last change on this file since 8452 was 8452, checked in by uli, 12 years ago

Finetune media file distributions for students a bit: make sure that for lower numbers 10,000 numbers share a path.

  • Property svn:keywords set to Id
File size: 9.2 KB
Line 
1## $Id: test_student.py 8452 2012-05-15 15:24:11Z 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 faked passport file for `student`
133        storage = getUtility(IExtFileStore)
134        file_id = IFileStoreNameChooser(student).chooseName(
135            attr='passport.jpg')
136        storage.createFile(file_id, StringIO('I am a fake image.'))
137
138    def test_backup_single_student_data(self):
139        # when a single student is removed, the data is backed up.
140        self.setup_student(self.student)
141        # Add a fake image
142        self.create_passport_img(self.student)
143        handle_student_removed(self.student, None)
144        del_dir = self.app['datacenter'].deleted_path
145        del_img_path = os.path.join(
146            del_dir, 'media', 'students', '00111', 'A111111',
147            'passport_A111111.jpg')
148
149        # The image was copied over
150        self.assertTrue(os.path.isfile(del_img_path))
151        self.assertEqual(
152            open(del_img_path, 'rb').read(),
153            'I am a fake image.')
154
155        # The student data were put into CSV files
156        for name in EXPORTER_NAMES:
157            csv_path = os.path.join(del_dir, '%s.csv' % name)
158            self.assertTrue(os.path.isfile(csv_path))
159            contents = open(csv_path, 'rb').read().split('\r\n')
160            # We expect 3 lines output including a linebreak at end of file.
161            self.assertEqual(len(contents), 3)
162        return
163
164    def test_backup_append_csv(self):
165        # when several students are removed, existing CSVs are appended
166        self.setup_student(self.student)
167        # Add a fake image
168        self.create_passport_img(self.student)
169        del_dir = self.app['datacenter'].deleted_path
170        # put fake data into students.csv with trailing linebreak
171        students_csv = os.path.join(del_dir, 'students.csv')
172        open(students_csv, 'wb').write('line1\r\nline2\r\n')
173        handle_student_removed(self.student, None)
174        contents = open(students_csv, 'rb').read().split('\r\n')
175        # there should be 4 lines in result csv (including trailing linebreak)
176        self.assertEqual(len(contents), 4)
177        return
178
179    def test_old_files_removed(self):
180        # make sure old files are not accessible any more
181        self.setup_student(self.student)
182        # Add a fake image
183        self.create_passport_img(self.student)
184        # make sure we can access the image before removal
185        file_store = getUtility(IExtFileStore)
186        image = file_store.getFileByContext(self.student, attr='passport.jpg')
187        self.assertTrue(image is not None)
188
189        # remove image (hopefully)
190        handle_student_removed(self.student, None)
191
192        # the is not accessible anymore
193        image = file_store.getFileByContext(self.student, attr='passport.jpg')
194        self.assertEqual(image, None)
195        return
196
197    def test_csv_file_entries_have_timestamp(self):
198        # each row in written csv files has a ``del_date`` column to
199        # tell when the associated student was deleted
200        self.setup_student(self.student)
201        del_dir = self.app['datacenter'].deleted_path
202        students_csv = os.path.join(del_dir, 'students.csv')
203        handle_student_removed(self.student, None)
204        contents = open(students_csv, 'rb').read().split('\r\n')
205        # the CSV header ends with a ``del_date`` column
206        self.assertTrue(contents[0].endswith(',del_date'))
207        # each line ends with an UTC timestamp
208        timestamp = contents[1][-23:]
209        self.assertTrue(re.match(
210            '^\d\d-\d\d-\d\d \d\d:\d\d:\d\d\+00:00$', timestamp))
211        return
212
213
214class StudentFactoryTest(FunctionalTestCase):
215
216    layer = FunctionalLayer
217
218    def setUp(self):
219        super(StudentFactoryTest, self).setUp()
220        self.factory = StudentFactory()
221
222    def tearDown(self):
223        super(StudentFactoryTest, self).tearDown()
224
225    def test_interfaces(self):
226        verify.verifyClass(IFactory, StudentFactory)
227        verify.verifyObject(IFactory, self.factory)
228
229    def test_factory(self):
230        obj = self.factory()
231        assert isinstance(obj, Student)
232
233    def test_getInterfaces(self):
234        implemented_by = self.factory.getInterfaces()
235        assert implemented_by.isOrExtends(IStudent)
Note: See TracBrowser for help on using the repository browser.