~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/publisher/test_publisher.py

  • Committer: William Grant
  • Date: 2009-12-08 10:05:52 UTC
  • Revision ID: grantw@unimelb.edu.au-20091208100552-51qd6m3pl3h2owkk
Improve the diff/log breadcrumbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from nose.tools import assert_equal, raises
 
2
 
 
3
from ivle.webapp.publisher import (INF, InsufficientPathSegments, NoPath,
 
4
                                   NotFound, RouteConflict, Publisher, ROOT)
 
5
 
 
6
class Root(object):
 
7
    def __init__(self):
 
8
        self.subjects = {}
 
9
        self.users = {}
 
10
 
 
11
    def add_subject(self, subject):
 
12
        self.subjects[subject.name] = subject
 
13
 
 
14
    def add_user(self, user):
 
15
        self.users[user.login] = user
 
16
 
 
17
class User(object):
 
18
    def __init__(self, login):
 
19
        self.login = login
 
20
 
 
21
class Subject(object):
 
22
    def __init__(self, name, code):
 
23
        self.name = name
 
24
        self.code = code
 
25
        self.offerings = {}
 
26
 
 
27
    def add_offering(self, offering):
 
28
        assert self.name == offering.subject.name
 
29
        self.offerings[(offering.year, offering.semester)] = offering
 
30
 
 
31
class Offering(object):
 
32
    def __init__(self, subject, year, semester):
 
33
        self.subject = subject
 
34
        self.year = year
 
35
        self.semester = semester
 
36
        self.projects = {}
 
37
 
 
38
    def add_project(self, project):
 
39
        assert project.offering is self
 
40
        self.projects[project.name] = project
 
41
 
 
42
class OfferingFiles(object):
 
43
    def __init__(self, offering):
 
44
        self.offering = offering
 
45
 
 
46
class OfferingFile(object):
 
47
    def __init__(self, offeringfiles, path):
 
48
        self.offering = offeringfiles.offering
 
49
        self.path = path
 
50
 
 
51
class Project(object):
 
52
    def __init__(self, offering, name):
 
53
        self.offering = offering
 
54
        self.name = name
 
55
 
 
56
 
 
57
class View(object):
 
58
    def __init__(self, context):
 
59
        self.context = context
 
60
 
 
61
class RootIndex(View):
 
62
    pass
 
63
 
 
64
class UserServeView(View):
 
65
    pass
 
66
 
 
67
class SubjectIndex(View):
 
68
    pass
 
69
 
 
70
class SubjectEdit(View):
 
71
    pass
 
72
 
 
73
class OfferingIndex(View):
 
74
    pass
 
75
 
 
76
class OfferingEdit(View):
 
77
    pass
 
78
 
 
79
class OfferingAPIIndex(View):
 
80
    pass
 
81
 
 
82
class OfferingFilesIndex(View):
 
83
    pass
 
84
 
 
85
class OfferingFileIndex(View):
 
86
    pass
 
87
 
 
88
class ProjectIndex(View):
 
89
    pass
 
90
 
 
91
class OfferingProjects(View):
 
92
    pass
 
93
 
 
94
class OfferingAddProject(View):
 
95
    pass
 
96
 
 
97
def root_to_subject_or_user(root, name):
 
98
    if name.startswith('~'):
 
99
        return root.users.get(name[1:])
 
100
    return root.subjects.get(name)
 
101
 
 
102
def subject_to_offering(subject, year, semester):
 
103
    return subject.offerings.get((int(year), int(semester)))
 
104
 
 
105
def offering_to_files(offering):
 
106
    return OfferingFiles(offering)
 
107
 
 
108
def offering_files_to_file(offeringfiles, *path):
 
109
    return OfferingFile(offeringfiles, path)
 
110
 
 
111
def offering_to_project(offering, name):
 
112
    return offering.projects.get(name)
 
113
 
 
114
def subject_url(subject):
 
115
    return (ROOT, subject.name)
 
116
 
 
117
def offering_url(offering):
 
118
    return (offering.subject, (str(offering.year), str(offering.semester)))
 
119
 
 
120
def offering_files_url(offeringfiles):
 
121
    return (offeringfiles.offering, '+files')
 
122
 
 
123
def project_url(project):
 
124
    return (project.offering, ('+projects', project.name))
 
125
 
 
126
class BaseTest(object):
 
127
    def setUp(self):
 
128
        r = Root()
 
129
        self.r = r
 
130
 
 
131
        # A user would be nice.
 
132
        r.add_user(User('jsmith'))
 
133
 
 
134
        # Give us some subjects...
 
135
        r.add_subject(Subject('info1', '600151'))
 
136
        r.add_subject(Subject('info2', '600152'))
 
137
        r.add_subject(Subject('info3', '600251'))
 
138
 
 
139
        # ... and some offerings.
 
140
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
141
                                         2008, 1))
 
142
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
143
                                         2008, 2))
 
144
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
145
                                         2009, 1))
 
146
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
147
                                         2008, 2))
 
148
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
149
                                         2009, 1))
 
150
        r.subjects['info3'].add_offering(Offering(self.r.subjects['info3'],
 
151
                                         2009, 1))
 
152
 
 
153
        # A normal project...
 
154
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
155
            Project(r.subjects['info1'].offerings[(2009, 1)], 'p1')
 
156
            )
 
157
 
 
158
        # And one conflicting with a deep view, just to be nasty.
 
159
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
160
            Project(r.subjects['info1'].offerings[(2009, 1)], '+new')
 
161
            )
 
162
 
 
163
class TestResolution(BaseTest):
 
164
    def setUp(self):
 
165
        super(TestResolution, self).setUp()
 
166
        self.rtr = Publisher(root=self.r, viewset='browser')
 
167
        self.rtr.add_set_switch('api', 'api')
 
168
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
169
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
170
        self.rtr.add_forward(Offering, '+files', offering_to_files, 0)
 
171
        self.rtr.add_forward(OfferingFiles, None, offering_files_to_file, INF)
 
172
        self.rtr.add_forward(Offering, '+projects', offering_to_project, 1)
 
173
        self.rtr.add_view(User, None, UserServeView, viewset='browser')
 
174
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
175
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
176
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
177
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
178
        self.rtr.add_view(OfferingFiles, '+index', OfferingFilesIndex,
 
179
                          viewset='browser')
 
180
        self.rtr.add_view(OfferingFile, '+index', OfferingFileIndex,
 
181
                          viewset='browser')
 
182
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
183
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
184
                          viewset='browser')
 
185
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
186
                          viewset='browser')
 
187
 
 
188
    def testOneRoute(self):
 
189
        assert_equal(self.rtr.resolve('/info1'),
 
190
                     (self.r.subjects['info1'], SubjectIndex, ())
 
191
                     )
 
192
        assert_equal(self.rtr.resolve('/info3'),
 
193
                     (self.r.subjects['info3'], SubjectIndex, ())
 
194
                     )
 
195
 
 
196
    def testTwoRoutes(self):
 
197
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
198
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
199
             )
 
200
        assert_equal(self.rtr.resolve('/info2/2008/2'),
 
201
             (self.r.subjects['info2'].offerings[(2008, 2)], OfferingIndex, ())
 
202
             )
 
203
 
 
204
    def testNamedRoute(self):
 
205
        assert_equal(type(self.rtr.resolve('/info1/2009/1/+files')[0]),
 
206
                     OfferingFiles
 
207
                    )
 
208
        assert_equal(self.rtr.resolve('/info1/2009/1/+files')[0].offering,
 
209
                     self.r.subjects['info1'].offerings[(2009, 1)]
 
210
                    )
 
211
 
 
212
    def testNonDefaultView(self):
 
213
        assert_equal(self.rtr.resolve('/info1/+edit'),
 
214
                     (self.r.subjects['info1'], SubjectEdit, ())
 
215
                     )
 
216
 
 
217
    def testDefaultView(self):
 
218
        assert_equal(self.rtr.resolve('/info1'),
 
219
                     (self.r.subjects['info1'], SubjectIndex, ())
 
220
                     )
 
221
 
 
222
    def testViewWithSubpath(self):
 
223
        assert_equal(self.rtr.resolve('/info1/+edit/foo/bar'),
 
224
                     (self.r.subjects['info1'], SubjectEdit, ('foo', 'bar'))
 
225
                     )
 
226
 
 
227
    def testNoDefaultView(self):
 
228
        try:
 
229
            self.rtr.default = 'not+index'
 
230
            self.rtr.resolve('/info1')
 
231
        except NotFound, e:
 
232
            assert_equal(e.args, (self.r.subjects['info1'], '+index', ()))
 
233
        except:
 
234
            raise
 
235
        else:
 
236
            raise AssertionError('did not raise NotFound')
 
237
        finally:
 
238
            self.rtr.default = '+index'
 
239
 
 
240
    def testMissingView(self):
 
241
        try:
 
242
            self.rtr.resolve('/info1/+foo')
 
243
        except NotFound, e:
 
244
            assert_equal(e.args, (self.r.subjects['info1'], '+foo', ()))
 
245
        except:
 
246
            raise
 
247
        else:
 
248
            raise AssertionError('did not raise NotFound')
 
249
 
 
250
    def testViewSetSeparation(self):
 
251
        try:
 
252
            self.rtr.resolve('/api/info1/+edit')
 
253
        except NotFound, e:
 
254
            assert_equal(e.args, (self.r.subjects['info1'], '+edit', ()))
 
255
        except:
 
256
            raise
 
257
        else:
 
258
            raise AssertionError('did not raise NotFound')
 
259
 
 
260
    def testRouteReturningNone(self):
 
261
        try:
 
262
            self.rtr.resolve('/info9/+index')
 
263
        except NotFound, e:
 
264
            assert_equal(e.args, (self.r, 'info9', ('+index',)))
 
265
        except:
 
266
            raise
 
267
        else:
 
268
            raise AssertionError('did not raise NotFound')
 
269
 
 
270
    def testRouteWithInfinitelyManyArguments(self):
 
271
        o, v, sp = self.rtr.resolve('/info1/2009/1/+files/foo/bar/baz')
 
272
 
 
273
        assert_equal(type(o), OfferingFile)
 
274
        assert_equal(o.path, ('foo', 'bar', 'baz'))
 
275
        assert_equal(o.offering, self.r.subjects['info1'].offerings[(2009, 1)])
 
276
        assert_equal(v, OfferingFileIndex)
 
277
        assert_equal(sp, ())
 
278
 
 
279
    def testMissingRoute(self):
 
280
        try:
 
281
            self.rtr.resolve('/info1/2009/1/+foo')
 
282
        except NotFound, e:
 
283
            assert_equal(e.args, (
 
284
                self.r.subjects['info1'].offerings[(2009, 1)],
 
285
                '+foo',
 
286
                ()
 
287
                ))
 
288
        except:
 
289
            raise
 
290
        else:
 
291
            raise AssertionError('did not raise NotFound')
 
292
 
 
293
    def testAlternateViewSetWithDefault(self):
 
294
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
295
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
296
             )
 
297
 
 
298
        assert_equal(self.rtr.resolve('/api/info1/2009/1'),
 
299
          (self.r.subjects['info1'].offerings[(2009, 1)], OfferingAPIIndex, ())
 
300
          )
 
301
 
 
302
    def testDeepView(self):
 
303
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/+new'),
 
304
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
305
              OfferingAddProject, ())
 
306
             )
 
307
 
 
308
    def testDefaultDeepView(self):
 
309
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects'),
 
310
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
311
              OfferingProjects, ())
 
312
             )
 
313
 
 
314
    def testNamedRouteWithDeepView(self):
 
315
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/p1'),
 
316
             (self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
317
              ProjectIndex, ())
 
318
             )
 
319
 
 
320
    def testNullPathView(self):
 
321
        """Verify that views can be placed immediately under an object.
 
322
 
 
323
        There are some cases in which it is useful for a view with a
 
324
        subpath to exist immediately under an object, with no name.
 
325
        """
 
326
        assert_equal(self.rtr.resolve('/~jsmith/foo/bar'),
 
327
             (self.r.users['jsmith'], UserServeView, ('foo', 'bar')))
 
328
 
 
329
 
 
330
class TestGeneration(BaseTest):
 
331
    def setUp(self):
 
332
        super(TestGeneration, self).setUp()
 
333
        self.rtr = Publisher(root=self.r, viewset='browser')
 
334
        self.rtr.add_set_switch('api', 'api')
 
335
        self.rtr.add_reverse(Subject, subject_url)
 
336
        self.rtr.add_reverse(Offering, offering_url)
 
337
        self.rtr.add_reverse(OfferingFiles, offering_files_url)
 
338
        self.rtr.add_reverse(Project, project_url)
 
339
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
340
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
341
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
342
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
343
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
344
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
345
                          viewset='browser')
 
346
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
347
                          viewset='browser')
 
348
 
 
349
    def testOneLevel(self):
 
350
        assert_equal(self.rtr.generate(self.r.subjects['info1']), '/info1')
 
351
 
 
352
    def testTwoLevel(self):
 
353
        assert_equal(
 
354
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)]),
 
355
            '/info1/2009/1'
 
356
            )
 
357
        assert_equal(
 
358
            self.rtr.generate(self.r.subjects['info2'].offerings[(2008, 2)]),
 
359
            '/info2/2008/2'
 
360
            )
 
361
 
 
362
    def testNamedRoute(self):
 
363
        assert_equal(self.rtr.generate(
 
364
                OfferingFiles(self.r.subjects['info1'].offerings[(2009, 1)])),
 
365
                '/info1/2009/1/+files'
 
366
            )
 
367
 
 
368
    def testView(self):
 
369
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit),
 
370
                     '/info1/+edit'
 
371
                     )
 
372
 
 
373
    def testDefaultView(self):
 
374
        assert_equal(
 
375
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
376
                              OfferingIndex
 
377
                              ),
 
378
            '/info1/2009/1'
 
379
            )
 
380
 
 
381
    def testViewWithSubpath(self):
 
382
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
383
                                       ('foo', 'bar')),
 
384
                     '/info1/+edit/foo/bar'
 
385
                     )
 
386
 
 
387
    def testViewWithStringSubpath(self):
 
388
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
389
                                       'foo/bar'),
 
390
                     '/info1/+edit/foo/bar'
 
391
                     )
 
392
 
 
393
    def testAlternateViewSetWithDefault(self):
 
394
        assert_equal(
 
395
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
396
                              OfferingAPIIndex
 
397
                              ),
 
398
            '/api/info1/2009/1'
 
399
            )
 
400
 
 
401
    def testDeepView(self):
 
402
        assert_equal(
 
403
            self.rtr.generate(
 
404
                self.r.subjects['info1'].offerings[(2009, 1)],
 
405
                OfferingAddProject
 
406
                ),
 
407
        '/info1/2009/1/+projects/+new'
 
408
        )
 
409
 
 
410
    def testDefaultDeepView(self):
 
411
        assert_equal(
 
412
            self.rtr.generate(
 
413
                self.r.subjects['info1'].offerings[(2009, 1)],
 
414
                OfferingProjects
 
415
                ),
 
416
        '/info1/2009/1/+projects'
 
417
        )
 
418
 
 
419
    def testDefaultDeepViewWithSubpath(self):
 
420
        assert_equal(
 
421
            self.rtr.generate(
 
422
                self.r.subjects['info1'].offerings[(2009, 1)],
 
423
                OfferingProjects, ('foo', 'bar')
 
424
                ),
 
425
        '/info1/2009/1/+projects/+index/foo/bar'
 
426
        )
 
427
 
 
428
    def testNamedRouteWithDeepView(self):
 
429
        assert_equal(
 
430
            self.rtr.generate(
 
431
                self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
432
                ProjectIndex
 
433
                ),
 
434
        '/info1/2009/1/+projects/p1'
 
435
        )
 
436
 
 
437
    def testRoot(self):
 
438
        assert_equal(self.rtr.generate(self.r), '/')
 
439
 
 
440
 
 
441
class TestErrors(BaseTest):
 
442
    def setUp(self):
 
443
        super(TestErrors, self).setUp()
 
444
        self.rtr = Publisher(root=self.r)
 
445
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
446
        self.rtr.add_forward(Subject, '+foo', lambda s: s.name + 'foo', 0)
 
447
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
448
        self.rtr.add_reverse(Subject, subject_url)
 
449
        self.rtr.add_reverse(Offering, offering_url)
 
450
        self.rtr.add_view(Offering, '+index', OfferingIndex)
 
451
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
452
        self.rtr.add_set_switch('rest', 'rest')
 
453
 
 
454
    @raises(RouteConflict)
 
455
    def testForwardConflict(self):
 
456
        self.rtr.add_forward(Subject, '+foo', object(), 2)
 
457
 
 
458
    @raises(RouteConflict)
 
459
    def testReverseConflict(self):
 
460
        self.rtr.add_reverse(Subject, object())
 
461
 
 
462
    @raises(RouteConflict)
 
463
    def testViewConflict(self):
 
464
        self.rtr.add_view(Offering, '+index', object())
 
465
 
 
466
    @raises(RouteConflict)
 
467
    def testSetSwitchForwardConflict(self):
 
468
        self.rtr.add_set_switch('rest', 'foo')
 
469
 
 
470
    @raises(RouteConflict)
 
471
    def testSetSwitchReverseConflict(self):
 
472
        self.rtr.add_set_switch('bar', 'rest')
 
473
 
 
474
    @raises(NoPath)
 
475
    def testNoPath(self):
 
476
        self.rtr.generate(object())
 
477
 
 
478
    @raises(NoPath)
 
479
    def testNoSetSwitch(self):
 
480
        self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
481
                          OfferingAPIIndex)
 
482
 
 
483
    @raises(NoPath)
 
484
    def testUnregisteredView(self):
 
485
        self.rtr.generate(self.r.subjects['info1'], SubjectIndex)
 
486
 
 
487
    @raises(NotFound)
 
488
    def testNotFound(self):
 
489
        self.rtr.resolve('/bar')
 
490
 
 
491
    @raises(InsufficientPathSegments)
 
492
    def testInsufficientPathSegments(self):
 
493
        self.rtr.resolve('/info1/foo')