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

« back to all changes in this revision

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

Merge from object-publishing.

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