source: main/waeup.kofa/trunk/src/waeup/kofa/hostels/tests.py @ 9197

Last change on this file since 9197 was 9197, checked in by Henrik Bettermann, 12 years ago

Add view and content component methods to clear hostels.

  • Property svn:keywords set to Id
File size: 17.2 KB
Line 
1## $Id: tests.py 9197 2012-09-18 16:24:21Z henrik $
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"""
19Tests for hostels and their UI components.
20"""
21import os
22import shutil
23import tempfile
24import grok
25import pytz
26from datetime import datetime, timedelta
27from zope.event import notify
28from zope.interface.verify import verifyClass, verifyObject
29from zope.component.hooks import setSite, clearSite
30from zope.testbrowser.testing import Browser
31from zope.security.interfaces import Unauthorized
32from zope.catalog.interfaces import ICatalog
33from zope.component import queryUtility
34from waeup.kofa.app import University
35from waeup.kofa.hostels.interfaces import (
36    IHostelsContainer, IHostel, IBed)
37from waeup.kofa.hostels.container import HostelsContainer
38from waeup.kofa.hostels.hostel import Hostel, Bed
39from waeup.kofa.testing import (FunctionalLayer, FunctionalTestCase)
40from waeup.kofa.students.student import Student
41from waeup.kofa.students.accommodation import BedTicket
42from waeup.kofa.university.department import Department
43
44class HostelsContainerTestCase(FunctionalTestCase):
45
46    layer = FunctionalLayer
47
48    def test_interfaces(self):
49        # Make sure the correct interfaces are implemented.
50        self.assertTrue(
51            verifyClass(
52                IHostelsContainer, HostelsContainer)
53            )
54        self.assertTrue(
55            verifyObject(
56                IHostelsContainer, HostelsContainer())
57            )
58        self.assertTrue(
59            verifyClass(
60                IHostel, Hostel)
61            )
62        self.assertTrue(
63            verifyObject(
64                IHostel, Hostel())
65            )
66        self.assertTrue(
67            verifyClass(
68                IBed, Bed)
69            )
70        self.assertTrue(
71            verifyObject(
72                IBed, Bed())
73            )
74        return
75
76    def test_base(self):
77        # We cannot call the fundamental methods of a base in that case
78        container = HostelsContainer()
79        hostel = Hostel()
80        self.assertRaises(
81            NotImplementedError, container.archive)
82        self.assertRaises(
83            NotImplementedError, container.clear)
84        # We cannot add arbitrary objects
85        department = Department()
86        self.assertRaises(
87            TypeError, container.addHostel, department)
88        self.assertRaises(
89            TypeError, hostel.addBed, department)
90        # Application is expired if startdate or enddate are not set
91        # or current datetime is outside application period.
92        self.assertTrue(container.expired)
93        delta = timedelta(days=10)
94        container.startdate = datetime.now(pytz.utc) - delta
95        self.assertTrue(container.expired)
96        container.enddate = datetime.now(pytz.utc) + delta
97        self.assertFalse(container.expired)
98
99class HostelsFullSetup(FunctionalTestCase):
100
101    def setUp(self):
102        super(HostelsFullSetup, self).setUp()
103
104        # Setup a sample site for each test
105        app = University()
106        self.dc_root = tempfile.mkdtemp()
107        app['datacenter'].setStoragePath(self.dc_root)
108
109        # Prepopulate the ZODB...
110        self.getRootFolder()['app'] = app
111        # we add the site immediately after creation to the
112        # ZODB. Catalogs and other local utilities are not setup
113        # before that step.
114        self.app = self.getRootFolder()['app']
115        # Set site here. Some of the following setup code might need
116        # to access grok.getSite() and should get our new app then
117        setSite(app)
118
119        # Add student with subobjects
120        student = Student()
121        student.firstname = u'Anna'
122        student.lastname = u'Tester'
123        student.reg_number = u'123'
124        student.matric_number = u'234'
125        student.sex = u'f'
126        self.app['students'].addStudent(student)
127        self.student_id = student.student_id
128        self.student = self.app['students'][self.student_id]
129        self.student['studycourse'].current_session = 2004
130        self.student['studycourse'].entry_session = 2004
131        # The students_catalog must be informed that the
132        # session attribute has changed
133        notify(grok.ObjectModifiedEvent(self.student))
134
135        # Set accommodation_session
136        self.app['hostels'].accommodation_session = 2004
137
138        # Create a hostel
139        hostel = Hostel()
140        hostel.hostel_id = u'hall-x'
141        self.app['hostels'][hostel.hostel_id] = hostel
142
143        # Create a bed
144        bed = Bed()
145        bed.bed_id = u'xyz'
146        bed.bed_number = 1
147        bed.bed_type = u'abc'
148        self.app['hostels'][hostel.hostel_id][bed.bed_id] = bed
149
150        self.container_path = 'http://localhost/app/hostels'
151        self.student_path = 'http://localhost/app/students/%s' % self.student_id
152        self.manage_container_path = self.container_path + '/@@manage'
153        self.add_hostel_path = self.container_path + '/addhostel'
154
155        # Put the prepopulated site into test ZODB and prepare test
156        # browser
157        self.browser = Browser()
158        self.browser.handleErrors = False
159
160    def tearDown(self):
161        super(HostelsFullSetup, self).tearDown()
162        clearSite()
163        shutil.rmtree(self.dc_root)
164
165class BedCatalogTests(HostelsFullSetup):
166
167    layer = FunctionalLayer
168
169    def test_get_catalog(self):
170        # We can get a beds catalog if we wish
171        cat = queryUtility(ICatalog, name='beds_catalog')
172        assert cat is not None
173
174    def test_search_by_type(self):
175        # We can find a certain bed
176        cat = queryUtility(ICatalog, name='beds_catalog')
177        results = cat.searchResults(bed_type=(u'abc', u'abc'))
178        results = [x for x in results] # Turn results generator into list
179        assert len(results) == 1
180        assert results[0] is self.app['hostels']['hall-x']['xyz']
181
182    def test_search_by_owner(self):
183        # We can find a certain bed
184        myobj = self.app['hostels']['hall-x']['xyz']
185        myobj.owner = u'abc'
186        notify(grok.ObjectModifiedEvent(myobj))
187        cat = queryUtility(ICatalog, name='beds_catalog')
188        results = cat.searchResults(owner=(u'abc', u'abc'))
189        results = [x for x in results] # Turn results generator into list
190        assert len(results) == 1
191        assert results[0] is self.app['hostels']['hall-x']['xyz']
192
193class HostelsUITests(HostelsFullSetup):
194
195    layer = FunctionalLayer
196
197    def test_anonymous_access(self):
198        # Anonymous users can't access hostels containers
199        self.assertRaises(
200            Unauthorized, self.browser.open, self.manage_container_path)
201        return
202
203    def test_add_search_edit_delete_manage_hostels(self):
204        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
205        self.browser.open(self.container_path)
206        self.browser.getLink("Manage accommodation").click()
207        self.assertEqual(self.browser.headers['Status'], '200 Ok')
208        self.assertEqual(self.browser.url, self.manage_container_path)
209        self.browser.getControl("Add hostel").click()
210        self.assertEqual(self.browser.headers['Status'], '200 Ok')
211        self.assertEqual(self.browser.url, self.add_hostel_path)
212        self.browser.getControl("Create hostel").click()
213        self.assertEqual(self.browser.headers['Status'], '200 Ok')
214        self.assertTrue('Hostel created' in self.browser.contents)
215        self.browser.open(self.container_path + '/addhostel')
216        self.browser.getControl("Create hostel").click()
217        self.assertTrue('The hostel already exists' in self.browser.contents)
218        hall = self.app['hostels']['hall-1']
219        hall.blocks_for_female = ['A','B']
220        self.browser.open(self.container_path + '/hall-1')
221        expected = '''...<ul id="form.blocks_for_female" ><li>Block A</li>
222<li>Block B</li></ul>...'''
223        self.assertMatches(expected,self.browser.contents)
224        self.browser.open(self.container_path + '/hall-1/manage')
225        self.browser.getControl(name="form.rooms_per_floor").value = '1'
226        self.browser.getControl("Save").click()
227        self.assertTrue('Form has been saved' in self.browser.contents)
228        # Since the testbrowser does not support Javascrip the
229        # save action cleared the settings above and we have to set them
230        # again
231        self.assertTrue(len(hall.blocks_for_female) == 0)
232        hall.blocks_for_female = ['A','B']
233        hall.beds_for_fresh = ['A']
234        hall.beds_for_returning = ['B']
235        hall.beds_for_final = ['C']
236        hall.beds_for_all = ['D','E']
237        self.browser.getControl("Update all beds").click()
238        expected = '...0 empty beds removed, 10 beds added, 0 occupied beds modified ()...'
239        self.assertMatches(expected,self.browser.contents)
240        cat = queryUtility(ICatalog, name='beds_catalog')
241        results = cat.searchResults(
242            bed_type=('regular_female_all', 'regular_female_all'))
243        results = [x for x in results]
244        assert len(results) == 4
245        # Reserve bed
246        self.browser.getControl("Switch reservation", index=0).click()
247        self.assertTrue('No item selected' in self.browser.contents)
248        ctrl = self.browser.getControl(name='val_id')
249        ctrl.getControl(value='hall-1_A_101_A').selected = True
250        ctrl.getControl(value='hall-1_A_101_B').selected = True
251        ctrl.getControl(value='hall-1_A_101_C').selected = True
252        ctrl.getControl(value='hall-1_A_101_D').selected = True
253        self.browser.getControl("Switch reservation", index=0).click()
254        self.assertTrue('Successfully switched beds: hall-1_A_101_A (reserved)'
255            in self.browser.contents)
256        assert self.app['hostels']['hall-1'][
257            'hall-1_A_101_D'].bed_type == 'regular_female_reserved'
258        self.assertTrue('<div>A_101_A</div>' in self.browser.contents)
259
260        # Change hostel configuration
261        hall.beds_for_all = ['D']
262        self.browser.getControl("Update all beds").click()
263        expected = '...10 empty beds removed, 8 beds added, 0 occupied beds modified...'
264        self.assertMatches(expected,self.browser.contents)
265        results = cat.searchResults(
266            bed_type=('regular_female_all', 'regular_female_all'))
267        results = [x for x in results]
268        assert len(results) == 1
269        # Unreserve bed
270        ctrl = self.browser.getControl(name='val_id')
271        ctrl.getControl(value='hall-1_A_101_A').selected = True
272        ctrl.getControl(value='hall-1_A_101_B').selected = True
273        ctrl.getControl(value='hall-1_A_101_C').selected = True
274        ctrl.getControl(value='hall-1_A_101_D').selected = True
275        self.browser.getControl("Switch reservation", index=0).click()
276        assert self.app['hostels']['hall-1'][
277            'hall-1_A_101_D'].bed_type == 'regular_female_all'
278        self.assertFalse(expected in self.browser.contents)
279        # Release bed which has previously been booked
280        bedticket = BedTicket()
281        bedticket.ticket_id = u'2004'
282        bedticket.bed_coordinates = u'anything'
283        self.student['accommodation'].addBedTicket(bedticket)
284        self.app['hostels']['hall-1']['hall-1_A_101_D'].owner = self.student_id
285        self.browser.open(self.container_path + '/hall-1/manage')
286        ctrl = self.browser.getControl(name='val_id')
287        self.browser.getControl("Release selected beds", index=0).click()
288        self.assertMatches("...No item selected...", self.browser.contents)
289        ctrl = self.browser.getControl(name='val_id')
290        ctrl.getControl(value='hall-1_A_101_D').selected = True
291        self.browser.getControl("Release selected beds", index=0).click()
292        self.assertMatches(
293          '...Successfully released beds: hall-1_A_101_D (%s)...' % self.student_id,
294          self.browser.contents)
295        self.assertMatches(bedticket.bed_coordinates,
296          u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> UTC --')
297        # If we release a free be, nothing will happen
298        ctrl = self.browser.getControl(name='val_id')
299        ctrl.getControl(value='hall-1_A_101_D').selected = True
300        self.browser.getControl("Release selected beds", index=0).click()
301        self.assertMatches(
302          '...No allocated bed selected...', self.browser.contents)
303        # Managers can manually allocate students after cancellation
304        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
305        self.browser.getControl(name="form.owner").value = [self.student_id]
306        self.browser.getControl("Save").click()
307        self.assertMatches("...Form has been saved...", self.browser.contents)
308        # If we open the same form again, we will be redirected to hostel
309        # manage page. Beds must be released first before they can be
310        # allocated to other students.
311        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
312        self.assertEqual(self.browser.url,
313            self.container_path + '/hall-1/@@manage?tab2')
314        # Updating the beds again will not affect the allocation and also
315        # the bed numbering remains the same
316        old_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
317        old_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
318        self.browser.getControl("Update all beds").click()
319        # 7 beds have been removed and re-added, 1 bed remains untouched
320        expected = '...7 empty beds removed, 7 beds added, 0 occupied beds modified...'
321        self.assertMatches(expected,self.browser.contents)
322        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
323        new_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
324        self.assertEqual(new_number, old_number)
325        self.assertEqual(new_owner, old_owner)
326        # If we change the bed type of an allocated bed, the modification will
327        # be indicated
328        hall.blocks_for_female = ['B']
329        hall.blocks_for_male = ['A']
330        self.browser.getControl("Update all beds").click()
331        expected = '...7 empty beds removed, 7 beds added, ' + \
332            '1 occupied beds modified (hall-1_A_101_A )...'
333        self.assertMatches(expected,self.browser.contents)
334        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
335        # Also the number of the bed has changed.
336        self.assertFalse(new_number == old_number)
337        # The number of occupied bed are displayed on container page.
338        self.browser.open(self.container_path)
339        self.assertTrue('1 of 8' in self.browser.contents)
340        # Remove entire hostel
341        self.browser.open(self.manage_container_path)
342        ctrl = self.browser.getControl(name='val_id')
343        value = ctrl.options[0]
344        ctrl.getControl(value=value).selected = True
345        self.browser.getControl("Remove selected", index=0).click()
346        self.assertTrue('Successfully removed' in self.browser.contents)
347        # Catalog is empty
348        results = cat.searchResults(
349            bed_type=('regular_female_all', 'regular_female_all'))
350        results = [x for x in results]
351        assert len(results) == 0
352
353    def test_clear_hostels(self):
354        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
355        self.browser.open(self.container_path)
356        self.browser.getLink("Manage accommodation").click()
357        self.browser.getControl("Add hostel").click()
358        self.browser.getControl("Create hostel").click()
359        hall = self.app['hostels']['hall-1']
360        hall.blocks_for_female = ['A','B']
361        hall.rooms_per_floor = 1
362        hall.beds_for_fresh = ['A']
363        hall.beds_for_returning = ['B']
364        hall.beds_for_final = ['C']
365        hall.beds_for_all = ['D','E']
366        self.browser.open(self.container_path + '/hall-1/manage')
367        self.browser.getControl("Update all beds").click()
368        cat = queryUtility(ICatalog, name='beds_catalog')
369        results = cat.searchResults(bed_type=(None, None))
370        self.assertEqual(len(results), 11)
371        self.browser.getControl("Clear hostel").click()
372        self.assertEqual(len(self.app['hostels']['hall-1']), 0)
373        # Only the bed in hall-x remains in the catalog.
374        results = cat.searchResults(bed_type=(None, None))
375        self.assertEqual(len(results), 1)
376        # We can clear all hostels at the same time.
377        self.browser.open(self.manage_container_path)
378        self.browser.getControl("Clear all hostels").click()
379        results = cat.searchResults(bed_type=(None, None))
380        self.assertEqual(len(results), 0)
381        # Both actions have been logged.
382        logfile = os.path.join(
383            self.app['datacenter'].storage, 'logs', 'hostels.log')
384        logcontent = open(logfile).read()
385        self.assertTrue('INFO - zope.mgr - hostels.browser.HostelManageFormPage'
386                        ' - hall-1 - cleared' in logcontent)
387        self.assertTrue('zope.mgr - hostels.browser.HostelsContainerManagePage'
388                        ' - hostels - all hostels cleared' in logcontent)
Note: See TracBrowser for help on using the repository browser.