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

« back to all changes in this revision

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

  • Committer: Matt Giuca
  • Date: 2010-07-27 06:53:03 UTC
  • Revision ID: matt.giuca@gmail.com-20100727065303-gs7fn3gc3ccaqfux
Changed database schema 'semester' table. 'year' and 'semester' fields now allow any length, not just 4 or 1 respectively. (LP: #610330).

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
class OfferingWorksheets(View):
 
98
    pass
 
99
 
 
100
class OfferingWorksheetMarks(View):
 
101
    pass
 
102
 
 
103
class OfferingWorksheetCSVMarks(View):
 
104
    pass
 
105
 
 
106
def root_to_subject_or_user(root, name):
 
107
    if name.startswith('~'):
 
108
        return root.users.get(name[1:])
 
109
    return root.subjects.get(name)
 
110
 
 
111
def subject_to_offering(subject, year, semester):
 
112
    return subject.offerings.get((int(year), int(semester)))
 
113
 
 
114
def offering_to_files(offering):
 
115
    return OfferingFiles(offering)
 
116
 
 
117
def offering_files_to_file(offeringfiles, *path):
 
118
    return OfferingFile(offeringfiles, path)
 
119
 
 
120
def offering_to_project(offering, name):
 
121
    return offering.projects.get(name)
 
122
 
 
123
def subject_url(subject):
 
124
    return (ROOT, subject.name)
 
125
 
 
126
def offering_url(offering):
 
127
    return (offering.subject, (str(offering.year), str(offering.semester)))
 
128
 
 
129
def offering_files_url(offeringfiles):
 
130
    return (offeringfiles.offering, '+files')
 
131
 
 
132
def project_url(project):
 
133
    return (project.offering, ('+projects', project.name))
 
134
 
 
135
class BaseTest(object):
 
136
    def setUp(self):
 
137
        r = Root()
 
138
        self.r = r
 
139
 
 
140
        # A user would be nice.
 
141
        r.add_user(User('jsmith'))
 
142
 
 
143
        # Give us some subjects...
 
144
        r.add_subject(Subject('info1', '600151'))
 
145
        r.add_subject(Subject('info2', '600152'))
 
146
        r.add_subject(Subject('info3', '600251'))
 
147
 
 
148
        # ... and some offerings.
 
149
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
150
                                         2008, 1))
 
151
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
152
                                         2008, 2))
 
153
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
154
                                         2009, 1))
 
155
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
156
                                         2008, 2))
 
157
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
158
                                         2009, 1))
 
159
        r.subjects['info3'].add_offering(Offering(self.r.subjects['info3'],
 
160
                                         2009, 1))
 
161
 
 
162
        # A normal project...
 
163
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
164
            Project(r.subjects['info1'].offerings[(2009, 1)], 'p1')
 
165
            )
 
166
 
 
167
        # And one conflicting with a deep view, just to be nasty.
 
168
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
169
            Project(r.subjects['info1'].offerings[(2009, 1)], '+new')
 
170
            )
 
171
 
 
172
class TestResolution(BaseTest):
 
173
    def setUp(self):
 
174
        super(TestResolution, self).setUp()
 
175
        self.rtr = Publisher(root=self.r, viewset='browser')
 
176
        self.rtr.add_set_switch('api', 'api')
 
177
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
178
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
179
        self.rtr.add_forward(Offering, '+files', offering_to_files, 0)
 
180
        self.rtr.add_forward(OfferingFiles, None, offering_files_to_file, INF)
 
181
        self.rtr.add_forward(Offering, '+projects', offering_to_project, 1)
 
182
        self.rtr.add_view(User, None, UserServeView, viewset='browser')
 
183
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
184
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
185
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
186
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
187
        self.rtr.add_view(OfferingFiles, '+index', OfferingFilesIndex,
 
188
                          viewset='browser')
 
189
        self.rtr.add_view(OfferingFile, '+index', OfferingFileIndex,
 
190
                          viewset='browser')
 
191
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
192
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
193
                          viewset='browser')
 
194
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
195
                          viewset='browser')
 
196
        self.rtr.add_view(Offering, ('+worksheets', '+index'),
 
197
                          OfferingWorksheets, viewset='browser')
 
198
        self.rtr.add_view(Offering, ('+worksheets', '+marks', '+index'),
 
199
                          OfferingWorksheetMarks, viewset='browser')
 
200
        self.rtr.add_view(Offering, ('+worksheets', '+marks', 'marks.csv'),
 
201
                          OfferingWorksheetCSVMarks, viewset='browser')
 
202
 
 
203
    def testOneRoute(self):
 
204
        assert_equal(self.rtr.resolve('/info1'),
 
205
                     (self.r.subjects['info1'], SubjectIndex, ())
 
206
                     )
 
207
        assert_equal(self.rtr.resolve('/info3'),
 
208
                     (self.r.subjects['info3'], SubjectIndex, ())
 
209
                     )
 
210
 
 
211
    def testTwoRoutes(self):
 
212
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
213
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
214
             )
 
215
        assert_equal(self.rtr.resolve('/info2/2008/2'),
 
216
             (self.r.subjects['info2'].offerings[(2008, 2)], OfferingIndex, ())
 
217
             )
 
218
 
 
219
    def testNamedRoute(self):
 
220
        assert_equal(type(self.rtr.resolve('/info1/2009/1/+files')[0]),
 
221
                     OfferingFiles
 
222
                    )
 
223
        assert_equal(self.rtr.resolve('/info1/2009/1/+files')[0].offering,
 
224
                     self.r.subjects['info1'].offerings[(2009, 1)]
 
225
                    )
 
226
 
 
227
    def testNonDefaultView(self):
 
228
        assert_equal(self.rtr.resolve('/info1/+edit'),
 
229
                     (self.r.subjects['info1'], SubjectEdit, ())
 
230
                     )
 
231
 
 
232
    def testDefaultView(self):
 
233
        assert_equal(self.rtr.resolve('/info1'),
 
234
                     (self.r.subjects['info1'], SubjectIndex, ())
 
235
                     )
 
236
 
 
237
    def testViewWithSubpath(self):
 
238
        assert_equal(self.rtr.resolve('/info1/+edit/foo/bar'),
 
239
                     (self.r.subjects['info1'], SubjectEdit, ('foo', 'bar'))
 
240
                     )
 
241
 
 
242
    def testNoDefaultView(self):
 
243
        try:
 
244
            self.rtr.default = 'not+index'
 
245
            self.rtr.resolve('/info1')
 
246
        except NotFound, e:
 
247
            assert_equal(e.args, (self.r.subjects['info1'], '+index', ()))
 
248
        except:
 
249
            raise
 
250
        else:
 
251
            raise AssertionError('did not raise NotFound')
 
252
        finally:
 
253
            self.rtr.default = '+index'
 
254
 
 
255
    def testMissingView(self):
 
256
        try:
 
257
            self.rtr.resolve('/info1/+foo')
 
258
        except NotFound, e:
 
259
            assert_equal(e.args, (self.r.subjects['info1'], '+foo', ()))
 
260
        except:
 
261
            raise
 
262
        else:
 
263
            raise AssertionError('did not raise NotFound')
 
264
 
 
265
    def testViewSetSeparation(self):
 
266
        try:
 
267
            self.rtr.resolve('/api/info1/+edit')
 
268
        except NotFound, e:
 
269
            assert_equal(e.args, (self.r.subjects['info1'], '+edit', ()))
 
270
        except:
 
271
            raise
 
272
        else:
 
273
            raise AssertionError('did not raise NotFound')
 
274
 
 
275
    def testRouteReturningNone(self):
 
276
        try:
 
277
            self.rtr.resolve('/info9/+index')
 
278
        except NotFound, e:
 
279
            assert_equal(e.args, (self.r, 'info9', ('+index',)))
 
280
        except:
 
281
            raise
 
282
        else:
 
283
            raise AssertionError('did not raise NotFound')
 
284
 
 
285
    def testRouteWithInfinitelyManyArguments(self):
 
286
        o, v, sp = self.rtr.resolve('/info1/2009/1/+files/foo/bar/baz')
 
287
 
 
288
        assert_equal(type(o), OfferingFile)
 
289
        assert_equal(o.path, ('foo', 'bar', 'baz'))
 
290
        assert_equal(o.offering, self.r.subjects['info1'].offerings[(2009, 1)])
 
291
        assert_equal(v, OfferingFileIndex)
 
292
        assert_equal(sp, ())
 
293
 
 
294
    def testMissingRoute(self):
 
295
        try:
 
296
            self.rtr.resolve('/info1/2009/1/+foo')
 
297
        except NotFound, e:
 
298
            assert_equal(e.args, (
 
299
                self.r.subjects['info1'].offerings[(2009, 1)],
 
300
                '+foo',
 
301
                ()
 
302
                ))
 
303
        except:
 
304
            raise
 
305
        else:
 
306
            raise AssertionError('did not raise NotFound')
 
307
 
 
308
    def testAlternateViewSetWithDefault(self):
 
309
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
310
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
311
             )
 
312
 
 
313
        assert_equal(self.rtr.resolve('/api/info1/2009/1'),
 
314
          (self.r.subjects['info1'].offerings[(2009, 1)], OfferingAPIIndex, ())
 
315
          )
 
316
 
 
317
    def testDeepView(self):
 
318
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/+new'),
 
319
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
320
              OfferingAddProject, ())
 
321
             )
 
322
 
 
323
    def testDefaultDeepView(self):
 
324
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects'),
 
325
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
326
              OfferingProjects, ())
 
327
             )
 
328
 
 
329
    def testAnotherDefaultDeepView(self):
 
330
        assert_equal(self.rtr.resolve('/info1/2009/1/+worksheets'),
 
331
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
332
              OfferingWorksheets, ())
 
333
             )
 
334
 
 
335
    def testReallyDeepView(self):
 
336
        assert_equal(
 
337
             self.rtr.resolve('/info1/2009/1/+worksheets/+marks/marks.csv'),
 
338
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
339
              OfferingWorksheetCSVMarks, ())
 
340
             )
 
341
 
 
342
    def testDefaultReallyDeepView(self):
 
343
        assert_equal(self.rtr.resolve('/info1/2009/1/+worksheets/+marks'),
 
344
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
345
              OfferingWorksheetMarks, ())
 
346
             )
 
347
 
 
348
    def testNamedRouteWithDeepView(self):
 
349
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/p1'),
 
350
             (self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
351
              ProjectIndex, ())
 
352
             )
 
353
 
 
354
    def testNullPathView(self):
 
355
        """Verify that views can be placed immediately under an object.
 
356
 
 
357
        There are some cases in which it is useful for a view with a
 
358
        subpath to exist immediately under an object, with no name.
 
359
        """
 
360
        assert_equal(self.rtr.resolve('/~jsmith/foo/bar'),
 
361
             (self.r.users['jsmith'], UserServeView, ('foo', 'bar')))
 
362
 
 
363
    def testTrailingSlashResolvesToDefaultView(self):
 
364
        assert_equal(
 
365
             self.rtr.resolve('/info1/2009/1/'),
 
366
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
367
              OfferingIndex, ())
 
368
             )
 
369
 
 
370
    def testTrailingSlashResolvesToDeepDefaultView(self):
 
371
        assert_equal(
 
372
             self.rtr.resolve('/info1/2009/1/+worksheets/+marks/'),
 
373
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
374
              OfferingWorksheetMarks, ())
 
375
             )
 
376
 
 
377
    def testSubpathIndicatesTrailingSlash(self):
 
378
        assert_equal(
 
379
             self.rtr.resolve('/info1/2009/1/+index/'),
 
380
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
381
              OfferingIndex, ('',))
 
382
             )
 
383
 
 
384
class TestGeneration(BaseTest):
 
385
    def setUp(self):
 
386
        super(TestGeneration, self).setUp()
 
387
        self.rtr = Publisher(root=self.r, viewset='browser')
 
388
        self.rtr.add_set_switch('api', 'api')
 
389
        self.rtr.add_reverse(Subject, subject_url)
 
390
        self.rtr.add_reverse(Offering, offering_url)
 
391
        self.rtr.add_reverse(OfferingFiles, offering_files_url)
 
392
        self.rtr.add_reverse(Project, project_url)
 
393
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
394
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
395
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
396
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
397
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
398
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
399
                          viewset='browser')
 
400
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
401
                          viewset='browser')
 
402
 
 
403
    def testOneLevel(self):
 
404
        assert_equal(self.rtr.generate(self.r.subjects['info1']), '/info1')
 
405
 
 
406
    def testTwoLevel(self):
 
407
        assert_equal(
 
408
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)]),
 
409
            '/info1/2009/1'
 
410
            )
 
411
        assert_equal(
 
412
            self.rtr.generate(self.r.subjects['info2'].offerings[(2008, 2)]),
 
413
            '/info2/2008/2'
 
414
            )
 
415
 
 
416
    def testNamedRoute(self):
 
417
        assert_equal(self.rtr.generate(
 
418
                OfferingFiles(self.r.subjects['info1'].offerings[(2009, 1)])),
 
419
                '/info1/2009/1/+files'
 
420
            )
 
421
 
 
422
    def testView(self):
 
423
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit),
 
424
                     '/info1/+edit'
 
425
                     )
 
426
 
 
427
    def testDefaultView(self):
 
428
        assert_equal(
 
429
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
430
                              OfferingIndex
 
431
                              ),
 
432
            '/info1/2009/1'
 
433
            )
 
434
 
 
435
    def testViewWithSubpath(self):
 
436
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
437
                                       ('foo', 'bar')),
 
438
                     '/info1/+edit/foo/bar'
 
439
                     )
 
440
 
 
441
    def testViewWithStringSubpath(self):
 
442
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
443
                                       'foo/bar'),
 
444
                     '/info1/+edit/foo/bar'
 
445
                     )
 
446
 
 
447
    def testAlternateViewSetWithDefault(self):
 
448
        assert_equal(
 
449
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
450
                              OfferingAPIIndex
 
451
                              ),
 
452
            '/api/info1/2009/1'
 
453
            )
 
454
 
 
455
    def testDeepView(self):
 
456
        assert_equal(
 
457
            self.rtr.generate(
 
458
                self.r.subjects['info1'].offerings[(2009, 1)],
 
459
                OfferingAddProject
 
460
                ),
 
461
        '/info1/2009/1/+projects/+new'
 
462
        )
 
463
 
 
464
    def testDefaultDeepView(self):
 
465
        assert_equal(
 
466
            self.rtr.generate(
 
467
                self.r.subjects['info1'].offerings[(2009, 1)],
 
468
                OfferingProjects
 
469
                ),
 
470
        '/info1/2009/1/+projects'
 
471
        )
 
472
 
 
473
    def testDefaultDeepViewWithSubpath(self):
 
474
        assert_equal(
 
475
            self.rtr.generate(
 
476
                self.r.subjects['info1'].offerings[(2009, 1)],
 
477
                OfferingProjects, ('foo', 'bar')
 
478
                ),
 
479
        '/info1/2009/1/+projects/+index/foo/bar'
 
480
        )
 
481
 
 
482
    def testNamedRouteWithDeepView(self):
 
483
        assert_equal(
 
484
            self.rtr.generate(
 
485
                self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
486
                ProjectIndex
 
487
                ),
 
488
        '/info1/2009/1/+projects/p1'
 
489
        )
 
490
 
 
491
    def testRoot(self):
 
492
        assert_equal(self.rtr.generate(self.r), '/')
 
493
 
 
494
 
 
495
class TestErrors(BaseTest):
 
496
    def setUp(self):
 
497
        super(TestErrors, self).setUp()
 
498
        self.rtr = Publisher(root=self.r)
 
499
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
500
        self.rtr.add_forward(Subject, '+foo', lambda s: s.name + 'foo', 0)
 
501
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
502
        self.rtr.add_reverse(Subject, subject_url)
 
503
        self.rtr.add_reverse(Offering, offering_url)
 
504
        self.rtr.add_view(Offering, '+index', OfferingIndex)
 
505
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
506
        self.rtr.add_set_switch('rest', 'rest')
 
507
 
 
508
    @raises(RouteConflict)
 
509
    def testForwardConflict(self):
 
510
        self.rtr.add_forward(Subject, '+foo', object(), 2)
 
511
 
 
512
    @raises(RouteConflict)
 
513
    def testReverseConflict(self):
 
514
        self.rtr.add_reverse(Subject, object())
 
515
 
 
516
    @raises(RouteConflict)
 
517
    def testViewConflict(self):
 
518
        self.rtr.add_view(Offering, '+index', object())
 
519
 
 
520
    @raises(RouteConflict)
 
521
    def testSetSwitchForwardConflict(self):
 
522
        self.rtr.add_set_switch('rest', 'foo')
 
523
 
 
524
    @raises(RouteConflict)
 
525
    def testSetSwitchReverseConflict(self):
 
526
        self.rtr.add_set_switch('bar', 'rest')
 
527
 
 
528
    @raises(NoPath)
 
529
    def testNoPath(self):
 
530
        self.rtr.generate(object())
 
531
 
 
532
    @raises(NoPath)
 
533
    def testNoSetSwitch(self):
 
534
        self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
535
                          OfferingAPIIndex)
 
536
 
 
537
    @raises(NoPath)
 
538
    def testUnregisteredView(self):
 
539
        self.rtr.generate(self.r.subjects['info1'], SubjectIndex)
 
540
 
 
541
    @raises(NotFound)
 
542
    def testNotFound(self):
 
543
        self.rtr.resolve('/bar')
 
544
 
 
545
    @raises(InsufficientPathSegments)
 
546
    def testInsufficientPathSegments(self):
 
547
        self.rtr.resolve('/info1/foo')