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

Last change on this file since 8449 was 8448, checked in by uli, 13 years ago

Distribute student media files over folders with chronological names, max. 1000 studs per folder.

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