~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-03-05 06:40:59 UTC
  • Revision ID: matt.giuca@gmail.com-20100305064059-wc6jsup5v66lo1o4
Added an entry on the user settings page to display the user's Subversion password. This was previously only possible through an arcane console command. Updated documentation. This fixes Launchpad bug #528450.

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
 
 
364
class TestGeneration(BaseTest):
 
365
    def setUp(self):
 
366
        super(TestGeneration, self).setUp()
 
367
        self.rtr = Publisher(root=self.r, viewset='browser')
 
368
        self.rtr.add_set_switch('api', 'api')
 
369
        self.rtr.add_reverse(Subject, subject_url)
 
370
        self.rtr.add_reverse(Offering, offering_url)
 
371
        self.rtr.add_reverse(OfferingFiles, offering_files_url)
 
372
        self.rtr.add_reverse(Project, project_url)
 
373
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
374
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
375
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
376
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
377
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
378
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
379
                          viewset='browser')
 
380
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
381
                          viewset='browser')
 
382
 
 
383
    def testOneLevel(self):
 
384
        assert_equal(self.rtr.generate(self.r.subjects['info1']), '/info1')
 
385
 
 
386
    def testTwoLevel(self):
 
387
        assert_equal(
 
388
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)]),
 
389
            '/info1/2009/1'
 
390
            )
 
391
        assert_equal(
 
392
            self.rtr.generate(self.r.subjects['info2'].offerings[(2008, 2)]),
 
393
            '/info2/2008/2'
 
394
            )
 
395
 
 
396
    def testNamedRoute(self):
 
397
        assert_equal(self.rtr.generate(
 
398
                OfferingFiles(self.r.subjects['info1'].offerings[(2009, 1)])),
 
399
                '/info1/2009/1/+files'
 
400
            )
 
401
 
 
402
    def testView(self):
 
403
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit),
 
404
                     '/info1/+edit'
 
405
                     )
 
406
 
 
407
    def testDefaultView(self):
 
408
        assert_equal(
 
409
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
410
                              OfferingIndex
 
411
                              ),
 
412
            '/info1/2009/1'
 
413
            )
 
414
 
 
415
    def testViewWithSubpath(self):
 
416
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
417
                                       ('foo', 'bar')),
 
418
                     '/info1/+edit/foo/bar'
 
419
                     )
 
420
 
 
421
    def testViewWithStringSubpath(self):
 
422
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
423
                                       'foo/bar'),
 
424
                     '/info1/+edit/foo/bar'
 
425
                     )
 
426
 
 
427
    def testAlternateViewSetWithDefault(self):
 
428
        assert_equal(
 
429
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
430
                              OfferingAPIIndex
 
431
                              ),
 
432
            '/api/info1/2009/1'
 
433
            )
 
434
 
 
435
    def testDeepView(self):
 
436
        assert_equal(
 
437
            self.rtr.generate(
 
438
                self.r.subjects['info1'].offerings[(2009, 1)],
 
439
                OfferingAddProject
 
440
                ),
 
441
        '/info1/2009/1/+projects/+new'
 
442
        )
 
443
 
 
444
    def testDefaultDeepView(self):
 
445
        assert_equal(
 
446
            self.rtr.generate(
 
447
                self.r.subjects['info1'].offerings[(2009, 1)],
 
448
                OfferingProjects
 
449
                ),
 
450
        '/info1/2009/1/+projects'
 
451
        )
 
452
 
 
453
    def testDefaultDeepViewWithSubpath(self):
 
454
        assert_equal(
 
455
            self.rtr.generate(
 
456
                self.r.subjects['info1'].offerings[(2009, 1)],
 
457
                OfferingProjects, ('foo', 'bar')
 
458
                ),
 
459
        '/info1/2009/1/+projects/+index/foo/bar'
 
460
        )
 
461
 
 
462
    def testNamedRouteWithDeepView(self):
 
463
        assert_equal(
 
464
            self.rtr.generate(
 
465
                self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
466
                ProjectIndex
 
467
                ),
 
468
        '/info1/2009/1/+projects/p1'
 
469
        )
 
470
 
 
471
    def testRoot(self):
 
472
        assert_equal(self.rtr.generate(self.r), '/')
 
473
 
 
474
 
 
475
class TestErrors(BaseTest):
 
476
    def setUp(self):
 
477
        super(TestErrors, self).setUp()
 
478
        self.rtr = Publisher(root=self.r)
 
479
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
480
        self.rtr.add_forward(Subject, '+foo', lambda s: s.name + 'foo', 0)
 
481
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
482
        self.rtr.add_reverse(Subject, subject_url)
 
483
        self.rtr.add_reverse(Offering, offering_url)
 
484
        self.rtr.add_view(Offering, '+index', OfferingIndex)
 
485
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
486
        self.rtr.add_set_switch('rest', 'rest')
 
487
 
 
488
    @raises(RouteConflict)
 
489
    def testForwardConflict(self):
 
490
        self.rtr.add_forward(Subject, '+foo', object(), 2)
 
491
 
 
492
    @raises(RouteConflict)
 
493
    def testReverseConflict(self):
 
494
        self.rtr.add_reverse(Subject, object())
 
495
 
 
496
    @raises(RouteConflict)
 
497
    def testViewConflict(self):
 
498
        self.rtr.add_view(Offering, '+index', object())
 
499
 
 
500
    @raises(RouteConflict)
 
501
    def testSetSwitchForwardConflict(self):
 
502
        self.rtr.add_set_switch('rest', 'foo')
 
503
 
 
504
    @raises(RouteConflict)
 
505
    def testSetSwitchReverseConflict(self):
 
506
        self.rtr.add_set_switch('bar', 'rest')
 
507
 
 
508
    @raises(NoPath)
 
509
    def testNoPath(self):
 
510
        self.rtr.generate(object())
 
511
 
 
512
    @raises(NoPath)
 
513
    def testNoSetSwitch(self):
 
514
        self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
515
                          OfferingAPIIndex)
 
516
 
 
517
    @raises(NoPath)
 
518
    def testUnregisteredView(self):
 
519
        self.rtr.generate(self.r.subjects['info1'], SubjectIndex)
 
520
 
 
521
    @raises(NotFound)
 
522
    def testNotFound(self):
 
523
        self.rtr.resolve('/bar')
 
524
 
 
525
    @raises(InsufficientPathSegments)
 
526
    def testInsufficientPathSegments(self):
 
527
        self.rtr.resolve('/info1/foo')