~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-04-07 03:48:23 UTC
  • mfrom: (1165.1.46 submissions)
  • Revision ID: grantw@unimelb.edu.au-20090407034823-snd6wa5p6otzq073
Allow students to submit projects from personal or group repositories.

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')