1
from nose.tools import assert_equal, raises
3
from ivle.webapp.urls import (InsufficientPathSegments, NoPath, NotFound,
4
RouteConflict, Router, ROOT)
10
def add_subject(self, subject):
11
self.subjects[subject.name] = subject
13
class Subject(object):
14
def __init__(self, name, code):
19
def add_offering(self, offering):
20
assert self.name == offering.subject.name
21
self.offerings[(offering.year, offering.semester)] = offering
23
class Offering(object):
24
def __init__(self, subject, year, semester):
25
self.subject = subject
27
self.semester = semester
29
class OfferingFiles(object):
30
def __init__(self, offering):
31
self.offering = offering
34
def __init__(self, context):
35
self.context = context
37
class RootIndex(View):
40
class SubjectIndex(View):
43
class SubjectEdit(View):
46
class OfferingIndex(View):
49
class OfferingEdit(View):
52
class OfferingAPIIndex(View):
55
class OfferingFilesIndex(View):
59
def root_to_subject(root, name):
60
return root.subjects.get(name)
62
def subject_to_offering(subject, year, semester):
63
return subject.offerings.get((int(year), int(semester)))
65
def offering_to_files(offering):
66
return OfferingFiles(offering)
68
def subject_url(subject):
69
return (ROOT, subject.name)
71
def offering_url(offering):
72
return (offering.subject, (str(offering.year), str(offering.semester)))
74
def offering_projects_url(offeringprojects):
75
return (offeringprojects.offering, '+projects')
78
class BaseTest(object):
83
# Give us some subjects...
84
r.add_subject(Subject('info1', '600151'))
85
r.add_subject(Subject('info2', '600152'))
86
r.add_subject(Subject('info3', '600251'))
88
# ... and some offerings.
89
r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
91
r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
93
r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
95
r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
97
r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
99
r.subjects['info3'].add_offering(Offering(self.r.subjects['info3'],
102
class TestResolution(BaseTest):
104
super(TestResolution, self).setUp()
105
self.rtr = Router(root=self.r, viewset='browser')
106
self.rtr.add_set_switch('api', 'api')
107
self.rtr.add_forward(Root, None, root_to_subject, 1)
108
self.rtr.add_forward(Subject, None, subject_to_offering, 2)
109
self.rtr.add_forward(Offering, '+files', offering_to_files, 0)
110
self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
111
self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
112
self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
113
self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
114
self.rtr.add_view(OfferingFiles, '+index', OfferingFilesIndex,
117
def testOneRoute(self):
118
assert_equal(self.rtr.resolve('/info1'),
119
(self.r.subjects['info1'], SubjectIndex, ())
121
assert_equal(self.rtr.resolve('/info3'),
122
(self.r.subjects['info3'], SubjectIndex, ())
125
def testTwoRoutes(self):
126
assert_equal(self.rtr.resolve('/info1/2009/1'),
127
(self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
129
assert_equal(self.rtr.resolve('/info2/2008/2'),
130
(self.r.subjects['info2'].offerings[(2008, 2)], OfferingIndex, ())
133
def testNamedRoute(self):
134
assert_equal(type(self.rtr.resolve('/info1/2009/1/+files')[0]),
137
assert_equal(self.rtr.resolve('/info1/2009/1/+files')[0].offering,
138
self.r.subjects['info1'].offerings[(2009, 1)]
141
def testNonDefaultView(self):
142
assert_equal(self.rtr.resolve('/info1/+edit'),
143
(self.r.subjects['info1'], SubjectEdit, ())
146
def testDefaultView(self):
147
assert_equal(self.rtr.resolve('/info1'),
148
(self.r.subjects['info1'], SubjectIndex, ())
151
def testViewWithSubpath(self):
152
assert_equal(self.rtr.resolve('/info1/+edit/foo/bar'),
153
(self.r.subjects['info1'], SubjectEdit, ('foo', 'bar'))
156
def testNoDefaultView(self):
158
self.rtr.default = 'not+index'
159
self.rtr.resolve('/info1')
161
assert_equal(e.args, (self.r.subjects['info1'], '+index', ()))
165
raise AssertionError('did not raise NotFound')
167
self.rtr.default = '+index'
169
def testMissingView(self):
171
self.rtr.resolve('/info1/+foo')
173
assert_equal(e.args, (self.r.subjects['info1'], '+foo', ()))
177
raise AssertionError('did not raise NotFound')
179
def testViewSetSeparation(self):
181
self.rtr.resolve('/api/info1/+edit')
183
assert_equal(e.args, (self.r.subjects['info1'], '+edit', ()))
187
raise AssertionError('did not raise NotFound')
189
def testRouteReturningNone(self):
191
self.rtr.resolve('/info9/+index')
193
assert_equal(e.args, (self.r, 'info9', ('+index',)))
197
raise AssertionError('did not raise NotFound')
199
def testAlternateViewSetWithDefault(self):
200
assert_equal(self.rtr.resolve('/info1/2009/1'),
201
(self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
204
assert_equal(self.rtr.resolve('/api/info1/2009/1'),
205
(self.r.subjects['info1'].offerings[(2009, 1)], OfferingAPIIndex, ())
208
class TestGeneration(BaseTest):
210
super(TestGeneration, self).setUp()
211
self.rtr = Router(root=self.r, viewset='browser')
212
self.rtr.add_set_switch('api', 'api')
213
self.rtr.add_reverse(Subject, subject_url)
214
self.rtr.add_reverse(Offering, offering_url)
215
self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
216
self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
217
self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
218
self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
220
def testOneLevel(self):
221
assert_equal(self.rtr.generate(self.r.subjects['info1']), '/info1')
223
def testTwoLevel(self):
225
self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)]),
229
self.rtr.generate(self.r.subjects['info2'].offerings[(2008, 2)]),
234
assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit),
238
def testDefaultView(self):
240
self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
246
def testViewWithSubpath(self):
247
assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
249
'/info1/+edit/foo/bar'
252
def testViewWithStringSubpath(self):
253
assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
255
'/info1/+edit/foo/bar'
258
def testAlternateViewSetWithDefault(self):
260
self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
267
class TestErrors(BaseTest):
269
super(TestErrors, self).setUp()
270
self.rtr = Router(root=self.r)
271
self.rtr.add_forward(Root, None, root_to_subject, 1)
272
self.rtr.add_forward(Subject, '+foo', lambda s: s.name + 'foo', 0)
273
self.rtr.add_forward(Subject, None, subject_to_offering, 2)
274
self.rtr.add_reverse(Subject, subject_url)
275
self.rtr.add_reverse(Offering, offering_url)
276
self.rtr.add_view(Offering, '+index', OfferingIndex)
277
self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
278
self.rtr.add_set_switch('rest', 'rest')
280
@raises(RouteConflict)
281
def testForwardConflict(self):
282
self.rtr.add_forward(Subject, '+foo', object(), 2)
284
@raises(RouteConflict)
285
def testReverseConflict(self):
286
self.rtr.add_reverse(Subject, object())
288
@raises(RouteConflict)
289
def testViewConflict(self):
290
self.rtr.add_view(Offering, '+index', object())
292
@raises(RouteConflict)
293
def testSetSwitchForwardConflict(self):
294
self.rtr.add_set_switch('rest', 'foo')
296
@raises(RouteConflict)
297
def testSetSwitchReverseConflict(self):
298
self.rtr.add_set_switch('bar', 'rest')
301
def testNoPath(self):
302
self.rtr.generate(object())
305
def testNoSetSwitch(self):
306
self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
310
def testUnregisteredView(self):
311
self.rtr.generate(self.r.subjects['info1'], SubjectIndex)
314
def testNotFound(self):
315
self.rtr.resolve('/bar')
317
@raises(InsufficientPathSegments)
318
def testInsufficientPathSegments(self):
319
self.rtr.resolve('/info1/foo')