source: main/waeup.sirp/trunk/src/waeup/sirp/hostels/tests.py @ 7091

Last change on this file since 7091 was 7077, checked in by Henrik Bettermann, 13 years ago

Further increase overall test coverage.

  • Property svn:keywords set to Id
File size: 15.3 KB
Line 
1## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
2## This program is free software; you can redistribute it and/or modify
3## it under the terms of the GNU General Public License as published by
4## the Free Software Foundation; either version 2 of the License, or
5## (at your option) any later version.
6##
7## This program is distributed in the hope that it will be useful,
8## but WITHOUT ANY WARRANTY; without even the implied warranty of
9## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10## GNU General Public License for more details.
11##
12## You should have received a copy of the GNU General Public License
13## along with this program; if not, write to the Free Software
14## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15##
16"""
17Tests for hostels and their UI components.
18"""
19import shutil
20import tempfile
21import grok
22from zope.event import notify
23
24from zope.interface.verify import verifyClass, verifyObject
25from zope.component.hooks import setSite, clearSite
26from zope.testbrowser.testing import Browser
27from zope.security.interfaces import Unauthorized
28from zope.catalog.interfaces import ICatalog
29from zope.component import queryUtility
30from waeup.sirp.app import University
31from waeup.sirp.hostels.interfaces import (
32    IHostelsContainer, IHostel, IBed)
33from waeup.sirp.hostels.container import HostelsContainer
34from waeup.sirp.hostels.hostel import Hostel, Bed
35from waeup.sirp.testing import (FunctionalLayer, FunctionalTestCase)
36from waeup.sirp.students.student import Student
37from waeup.sirp.students.accommodation import BedTicket
38from waeup.sirp.university.department import Department
39
40class HostelsContainerTestCase(FunctionalTestCase):
41
42    layer = FunctionalLayer
43
44    def test_interfaces(self):
45        # Make sure the correct interfaces are implemented.
46        self.assertTrue(
47            verifyClass(
48                IHostelsContainer, HostelsContainer)
49            )
50        self.assertTrue(
51            verifyObject(
52                IHostelsContainer, HostelsContainer())
53            )
54        self.assertTrue(
55            verifyClass(
56                IHostel, Hostel)
57            )
58        self.assertTrue(
59            verifyObject(
60                IHostel, Hostel())
61            )
62        self.assertTrue(
63            verifyClass(
64                IBed, Bed)
65            )
66        self.assertTrue(
67            verifyObject(
68                IBed, Bed())
69            )
70        return
71
72    def test_base(self):
73        # We cannot call the fundamental methods of a base in that case
74        container = HostelsContainer()
75        hostel = Hostel()
76        self.assertRaises(
77            NotImplementedError, container.archive)
78        self.assertRaises(
79            NotImplementedError, container.clear)
80        # We cannot add arbitrary objects
81        department = Department()
82        self.assertRaises(
83            TypeError, container.addHostel, department)
84        self.assertRaises(
85            TypeError, hostel.addBed, department)
86
87class HostelsFullSetup(FunctionalTestCase):
88
89    def setUp(self):
90        super(HostelsFullSetup, self).setUp()
91
92        # Setup a sample site for each test
93        app = University()
94        self.dc_root = tempfile.mkdtemp()
95        app['datacenter'].setStoragePath(self.dc_root)
96
97        # Prepopulate the ZODB...
98        self.getRootFolder()['app'] = app
99        # we add the site immediately after creation to the
100        # ZODB. Catalogs and other local utilities are not setup
101        # before that step.
102        self.app = self.getRootFolder()['app']
103        # Set site here. Some of the following setup code might need
104        # to access grok.getSite() and should get our new app then
105        setSite(app)
106
107        # Add student with subobjects
108        student = Student()
109        student.fullname = u'Anna Tester'
110        student.reg_number = u'123'
111        student.matric_number = u'234'
112        student.sex = u'f'
113        self.app['students'].addStudent(student)
114        self.student_id = student.student_id
115        self.student = self.app['students'][self.student_id]
116        self.student['studycourse'].current_session = 2004
117        self.student['studycourse'].entry_session = 2004
118        # The students_catalog must be informed that the
119        # session attribute has changed
120        notify(grok.ObjectModifiedEvent(self.student))
121
122        # Set accommodation_session
123        self.app['configuration'].accommodation_session = 2004
124
125        # Create a hostel
126        hostel = Hostel()
127        hostel.hostel_id = u'hall-x'
128        self.app['hostels'][hostel.hostel_id] = hostel
129
130        # Create a bed
131        bed = Bed()
132        bed.bed_id = u'xyz'
133        bed.bed_number = 1
134        bed.bed_type = u'abc'
135        self.app['hostels'][hostel.hostel_id][bed.bed_id] = bed
136
137        self.container_path = 'http://localhost/app/hostels'
138        self.student_path = 'http://localhost/app/students/%s' % self.student_id
139        self.manage_container_path = self.container_path + '/@@manage'
140        self.add_hostel_path = self.container_path + '/addhostel'
141
142        # Put the prepopulated site into test ZODB and prepare test
143        # browser
144        self.browser = Browser()
145        self.browser.handleErrors = False
146
147    def tearDown(self):
148        super(HostelsFullSetup, self).tearDown()
149        clearSite()
150        shutil.rmtree(self.dc_root)
151
152class BedCatalogTests(HostelsFullSetup):
153
154    layer = FunctionalLayer
155
156    def test_get_catalog(self):
157        # We can get a beds catalog if we wish
158        cat = queryUtility(ICatalog, name='beds_catalog')
159        assert cat is not None
160
161    def test_search_by_type(self):
162        # We can find a certain bed
163        cat = queryUtility(ICatalog, name='beds_catalog')
164        results = cat.searchResults(bed_type=(u'abc', u'abc'))
165        results = [x for x in results] # Turn results generator into list
166        assert len(results) == 1
167        assert results[0] is self.app['hostels']['hall-x']['xyz']
168
169    def test_search_by_owner(self):
170        # We can find a certain bed
171        myobj = self.app['hostels']['hall-x']['xyz']
172        myobj.owner = u'abc'
173        notify(grok.ObjectModifiedEvent(myobj))
174        cat = queryUtility(ICatalog, name='beds_catalog')
175        results = cat.searchResults(owner=(u'abc', u'abc'))
176        results = [x for x in results] # Turn results generator into list
177        assert len(results) == 1
178        assert results[0] is self.app['hostels']['hall-x']['xyz']
179
180class HostelsUITests(HostelsFullSetup):
181
182    layer = FunctionalLayer
183
184    def test_anonymous_access(self):
185        # Anonymous users can't access hostels containers
186        self.assertRaises(
187            Unauthorized, self.browser.open, self.manage_container_path)
188        return
189
190    def test_add_search_edit_delete_manage_hostels(self):
191        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
192        self.browser.open(self.container_path)
193        self.browser.getLink("Manage accommodation").click()
194        self.assertEqual(self.browser.headers['Status'], '200 Ok')
195        self.assertEqual(self.browser.url, self.manage_container_path)
196        self.browser.getControl("Add hostel").click()
197        self.assertEqual(self.browser.headers['Status'], '200 Ok')
198        self.assertEqual(self.browser.url, self.add_hostel_path)
199        self.browser.getControl("Create hostel").click()
200        self.assertEqual(self.browser.headers['Status'], '200 Ok')
201        self.assertTrue('Hostel created' in self.browser.contents)
202        self.browser.open(self.container_path + '/addhostel')
203        self.browser.getControl("Create hostel").click()
204        self.assertTrue('The hostel already exists' in self.browser.contents)
205        hall = self.app['hostels']['hall-1']
206        hall.blocks_for_female = ['A','B']
207        self.browser.open(self.container_path + '/hall-1')
208        expected = '''...<ul id="form.blocks_for_female" ><li>Block A</li>
209<li>Block B</li></ul>...'''
210        self.assertMatches(expected,self.browser.contents)
211        self.browser.open(self.container_path + '/hall-1/manage')
212        self.browser.getControl(name="form.rooms_per_floor").value = '1'
213        self.browser.getControl("Save").click()
214        self.assertTrue('Form has been saved' in self.browser.contents)
215        # Since the testbrowser does not support Javascrip the
216        # save action cleared the settings above and we have to set them
217        # again
218        self.assertTrue(len(hall.blocks_for_female) == 0)
219        hall.blocks_for_female = ['A','B']
220        hall.beds_for_fresh = ['A']
221        hall.beds_for_returning = ['B']
222        hall.beds_for_final = ['C']
223        hall.beds_for_all = ['D','E']
224        self.browser.getControl("Update all beds").click()
225        expected = '...0 empty beds removed, 10 beds added, 0 occupied beds modified ()...'
226        self.assertMatches(expected,self.browser.contents)
227        cat = queryUtility(ICatalog, name='beds_catalog')
228        results = cat.searchResults(
229            bed_type=('regular_female_all', 'regular_female_all'))
230        results = [x for x in results]
231        assert len(results) == 4
232        # Reserve bed
233        self.browser.getControl("Switch reservation", index=0).click()
234        self.assertTrue('No item selected' in self.browser.contents)
235        ctrl = self.browser.getControl(name='val_id')
236        ctrl.getControl(value='hall-1_A_101_A').selected = True
237        ctrl.getControl(value='hall-1_A_101_B').selected = True
238        ctrl.getControl(value='hall-1_A_101_C').selected = True
239        ctrl.getControl(value='hall-1_A_101_D').selected = True
240        self.browser.getControl("Switch reservation", index=0).click()
241        self.assertTrue('Successfully switched beds: hall-1_A_101_A (reserved)'
242            in self.browser.contents)
243        assert self.app['hostels']['hall-1'][
244            'hall-1_A_101_D'].bed_type == 'regular_female_reserved'
245        expected = 'name="form.beds_reserved.0." size="20" type="text" value="A_101_A"  />'
246        self.assertTrue(expected in self.browser.contents)
247        # Provoke exception
248        self.app['hostels']['hall-1']['hall-1_A_101_D'].bed_type = u'nonsense'
249        ctrl = self.browser.getControl(name='val_id')
250        ctrl.getControl(value='hall-1_A_101_D').selected = True
251        self.browser.getControl("Switch reservation", index=0).click()
252        self.assertMatches(
253            '...need more than 1 value to unpack...', self.browser.contents)
254        self.app['hostels']['hall-1'][
255            'hall-1_A_101_D'].bed_type = u'regular_female_reserved'
256        # Change hostel configuration
257        hall.beds_for_all = ['D']
258        self.browser.getControl("Update all beds").click()
259        expected = '...10 empty beds removed, 8 beds added, 0 occupied beds modified...'
260        self.assertMatches(expected,self.browser.contents)
261        results = cat.searchResults(
262            bed_type=('regular_female_all', 'regular_female_all'))
263        results = [x for x in results]
264        assert len(results) == 1
265        # Unreserve bed
266        ctrl = self.browser.getControl(name='val_id')
267        ctrl.getControl(value='hall-1_A_101_A').selected = True
268        ctrl.getControl(value='hall-1_A_101_B').selected = True
269        ctrl.getControl(value='hall-1_A_101_C').selected = True
270        ctrl.getControl(value='hall-1_A_101_D').selected = True
271        self.browser.getControl("Switch reservation", index=0).click()
272        assert self.app['hostels']['hall-1'][
273            'hall-1_A_101_D'].bed_type == 'regular_female_all'
274        self.assertFalse(expected in self.browser.contents)
275        # Release bed which has previously been booked
276        bedticket = BedTicket()
277        bedticket.ticket_id = u'2004'
278        bedticket.bed_coordinates = u'anything'
279        self.student['accommodation'].addBedTicket(bedticket)
280        self.app['hostels']['hall-1']['hall-1_A_101_D'].owner = self.student_id
281        self.browser.open(self.container_path + '/hall-1/manage')
282        ctrl = self.browser.getControl(name='val_id')
283        self.browser.getControl("Release selected beds", index=0).click()
284        self.assertMatches("...No item selected...", self.browser.contents)
285        ctrl = self.browser.getControl(name='val_id')
286        ctrl.getControl(value='hall-1_A_101_D').selected = True
287        self.browser.getControl("Release selected beds", index=0).click()
288        self.assertMatches(
289          '...Successfully released beds: hall-1_A_101_D (%s)...' % self.student_id,
290          self.browser.contents)
291        self.assertMatches(bedticket.bed_coordinates,
292          u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> --')
293        # If we release a free be, nothing will happen
294        ctrl = self.browser.getControl(name='val_id')
295        ctrl.getControl(value='hall-1_A_101_D').selected = True
296        self.browser.getControl("Release selected beds", index=0).click()
297        self.assertMatches(
298          '...No allocated bed selected...', self.browser.contents)
299        # Managers can manually allocate studenst after cancellation
300        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
301        self.browser.getControl(name="form.owner").value = [self.student_id]
302        self.browser.getControl("Save").click()
303        self.assertMatches("...Form has been saved...", self.browser.contents)
304        # If we open the same form again, we will be redirected to hostel
305        # manage page. Beds must be released first before they can be
306        # allocated to other students.
307        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
308        self.assertEqual(self.browser.url,
309            self.container_path + '/hall-1/@@manage#tab-2')
310        # Updating the beds again will not affect the allocation and also
311        # the bed numbering remains the same
312        old_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
313        old_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
314        self.browser.getControl("Update all beds").click()
315        # 7 beds have been removed and re-added, 1 bed remains untouched
316        expected = '...7 empty beds removed, 7 beds added, 0 occupied beds modified...'
317        self.assertMatches(expected,self.browser.contents)
318        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
319        new_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
320        self.assertEqual(new_number, old_number)
321        self.assertEqual(new_owner, old_owner)
322        # If we change the bed type of an allocated bed, the modification will
323        # be indicated
324        hall.blocks_for_female = ['B']
325        hall.blocks_for_male = ['A']
326        self.browser.getControl("Update all beds").click()
327        expected = '...7 empty beds removed, 7 beds added, ' + \
328            '1 occupied beds modified (hall-1_A_101_A )...'
329        self.assertMatches(expected,self.browser.contents)
330        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
331        # Also the number of the bed has changed.
332        self.assertFalse(new_number == old_number)
333        # Remove entire hostel
334        self.browser.open(self.manage_container_path)
335        ctrl = self.browser.getControl(name='val_id')
336        value = ctrl.options[0]
337        ctrl.getControl(value=value).selected = True
338        self.browser.getControl("Remove selected", index=0).click()
339        self.assertTrue('Successfully removed' in self.browser.contents)
340        # Catalog is empty
341        results = cat.searchResults(
342            bed_type=('regular_female_all', 'regular_female_all'))
343        results = [x for x in results]
344        assert len(results) == 0
Note: See TracBrowser for help on using the repository browser.