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
RevLine 
[7195]1## $Id: tests.py 9197 2012-09-18 16:24:21Z henrik $
2##
[6951]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.
[7195]8##
[6951]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.
[7195]13##
[6951]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"""
[6988]19Tests for hostels and their UI components.
[6951]20"""
[9197]21import os
[6961]22import shutil
23import tempfile
[7003]24import grok
[8686]25import pytz
26from datetime import datetime, timedelta
[7003]27from zope.event import notify
[6951]28from zope.interface.verify import verifyClass, verifyObject
[6961]29from zope.component.hooks import setSite, clearSite
30from zope.testbrowser.testing import Browser
31from zope.security.interfaces import Unauthorized
[6972]32from zope.catalog.interfaces import ICatalog
33from zope.component import queryUtility
[7811]34from waeup.kofa.app import University
35from waeup.kofa.hostels.interfaces import (
[6963]36    IHostelsContainer, IHostel, IBed)
[7811]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
[6951]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            )
[6963]66        self.assertTrue(
67            verifyClass(
68                IBed, Bed)
69            )
70        self.assertTrue(
71            verifyObject(
72                IBed, Bed())
73            )
[6951]74        return
75
76    def test_base(self):
77        # We cannot call the fundamental methods of a base in that case
78        container = HostelsContainer()
[7077]79        hostel = Hostel()
[6951]80        self.assertRaises(
81            NotImplementedError, container.archive)
82        self.assertRaises(
83            NotImplementedError, container.clear)
[7077]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)
[8686]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)
[6961]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
[7045]119        # Add student with subobjects
120        student = Student()
[7357]121        student.firstname = u'Anna'
122        student.lastname = u'Tester'
[7045]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
[7068]131        # The students_catalog must be informed that the
132        # session attribute has changed
133        notify(grok.ObjectModifiedEvent(self.student))
[7045]134
135        # Set accommodation_session
[8685]136        self.app['hostels'].accommodation_session = 2004
[7045]137
[6972]138        # Create a hostel
139        hostel = Hostel()
[6973]140        hostel.hostel_id = u'hall-x'
[6972]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
[6961]150        self.container_path = 'http://localhost/app/hostels'
[7068]151        self.student_path = 'http://localhost/app/students/%s' % self.student_id
[6961]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
[6972]165class BedCatalogTests(HostelsFullSetup):
166
167    layer = FunctionalLayer
168
169    def test_get_catalog(self):
[7045]170        # We can get a beds catalog if we wish
[6972]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')
[7003]177        results = cat.searchResults(bed_type=(u'abc', u'abc'))
[6972]178        results = [x for x in results] # Turn results generator into list
179        assert len(results) == 1
[6973]180        assert results[0] is self.app['hostels']['hall-x']['xyz']
[6972]181
[7003]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
[6961]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
[6985]203    def test_add_search_edit_delete_manage_hostels(self):
[6961]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)
[6988]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)
[6973]218        hall = self.app['hostels']['hall-1']
[6961]219        hall.blocks_for_female = ['A','B']
[6973]220        self.browser.open(self.container_path + '/hall-1')
[6961]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)
[6973]224        self.browser.open(self.container_path + '/hall-1/manage')
[6985]225        self.browser.getControl(name="form.rooms_per_floor").value = '1'
[6962]226        self.browser.getControl("Save").click()
227        self.assertTrue('Form has been saved' in self.browser.contents)
[6985]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']
[6988]233        hall.beds_for_fresh = ['A']
234        hall.beds_for_returning = ['B']
235        hall.beds_for_final = ['C']
[6985]236        hall.beds_for_all = ['D','E']
237        self.browser.getControl("Update all beds").click()
[6988]238        expected = '...0 empty beds removed, 10 beds added, 0 occupied beds modified ()...'
[6985]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
[6988]245        # Reserve bed
246        self.browser.getControl("Switch reservation", index=0).click()
247        self.assertTrue('No item selected' in self.browser.contents)
[6985]248        ctrl = self.browser.getControl(name='val_id')
[6988]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
[6985]252        ctrl.getControl(value='hall-1_A_101_D').selected = True
253        self.browser.getControl("Switch reservation", index=0).click()
[6988]254        self.assertTrue('Successfully switched beds: hall-1_A_101_A (reserved)'
[6985]255            in self.browser.contents)
[6988]256        assert self.app['hostels']['hall-1'][
[6985]257            'hall-1_A_101_D'].bed_type == 'regular_female_reserved'
[7802]258        self.assertTrue('<div>A_101_A</div>' in self.browser.contents)
259
[6985]260        # Change hostel configuration
261        hall.beds_for_all = ['D']
262        self.browser.getControl("Update all beds").click()
[6988]263        expected = '...10 empty beds removed, 8 beds added, 0 occupied beds modified...'
[6985]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]
[6988]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)
[7045]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')
[7068]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')
[7045]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,
[8234]296          u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> UTC --')
[7070]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)
[9196]303        # Managers can manually allocate students after cancellation
[7068]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)
[7070]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,
[7484]313            self.container_path + '/hall-1/@@manage?tab2')
[7070]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)
[9196]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)
[6985]340        # Remove entire hostel
[6962]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)
[6985]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
[9197]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.