Michael Merickel
2018-10-19 d579f2104de139e0b0fc5d6c81aabb2f826e5e54
commit | author | age
464973 1 import unittest
fdd1f8 2 from zope.interface import implementer
464973 3
CD 4 from pyramid import testing
488fdd 5 from pyramid.exceptions import ConfigurationError
0c29cf 6 from pyramid.interfaces import IResponse, IRequest
MM 7
464973 8
CD 9 class TestDeriveView(unittest.TestCase):
10     def setUp(self):
11         self.config = testing.setUp()
313c25 12         self.config.set_default_csrf_options(require_csrf=False)
464973 13
CD 14     def tearDown(self):
15         self.config = None
174bb5 16         testing.tearDown()
464973 17
CD 18     def _makeRequest(self):
19         request = DummyRequest()
20         request.registry = self.config.registry
21         return request
22
23     def _registerLogger(self):
24         from pyramid.interfaces import IDebugLogger
0c29cf 25
464973 26         logger = DummyLogger()
CD 27         self.config.registry.registerUtility(logger, IDebugLogger)
28         return logger
29
30     def _registerSecurityPolicy(self, permissive):
31         from pyramid.interfaces import IAuthenticationPolicy
32         from pyramid.interfaces import IAuthorizationPolicy
0c29cf 33
464973 34         policy = DummySecurityPolicy(permissive)
CD 35         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
36         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
37
38     def test_function_returns_non_adaptable(self):
39         def view(request):
40             return None
0c29cf 41
464973 42         result = self.config.derive_view(view)
CD 43         self.assertFalse(result is view)
44         try:
45             result(None, None)
46         except ValueError as e:
47             self.assertEqual(
48                 e.args[0],
49                 'Could not convert return value of the view callable function '
dd3cc8 50                 'tests.test_viewderivers.view into a response '
464973 51                 'object. The value returned was None. You may have forgotten '
0c29cf 52                 'to return a value from the view callable.',
MM 53             )
54         else:  # pragma: no cover
464973 55             raise AssertionError
CD 56
57     def test_function_returns_non_adaptable_dict(self):
58         def view(request):
0c29cf 59             return {'a': 1}
MM 60
464973 61         result = self.config.derive_view(view)
CD 62         self.assertFalse(result is view)
63         try:
64             result(None, None)
65         except ValueError as e:
66             self.assertEqual(
67                 e.args[0],
68                 "Could not convert return value of the view callable function "
dd3cc8 69                 "tests.test_viewderivers.view into a response "
464973 70                 "object. The value returned was {'a': 1}. You may have "
0c29cf 71                 "forgotten to define a renderer in the view configuration.",
MM 72             )
73         else:  # pragma: no cover
464973 74             raise AssertionError
CD 75
76     def test_instance_returns_non_adaptable(self):
77         class AView(object):
78             def __call__(self, request):
79                 return None
0c29cf 80
464973 81         view = AView()
CD 82         result = self.config.derive_view(view)
83         self.assertFalse(result is view)
84         try:
85             result(None, None)
86         except ValueError as e:
87             msg = e.args[0]
0c29cf 88             self.assertTrue(
MM 89                 msg.startswith(
10ddb6 90                     'Could not convert return value of the view callable '
MM 91                     'object <tests.test_viewderivers.'
0c29cf 92                 )
MM 93             )
94             self.assertTrue(
95                 msg.endswith(
10ddb6 96                     '> into a response object. The value returned was None. '
MM 97                     'You may have forgotten to return a value from the view '
98                     'callable.'
0c29cf 99                 )
MM 100             )
101         else:  # pragma: no cover
464973 102             raise AssertionError
CD 103
104     def test_function_returns_true_Response_no_renderer(self):
105         from pyramid.response import Response
0c29cf 106
464973 107         r = Response('Hello')
0c29cf 108
464973 109         def view(request):
CD 110             return r
0c29cf 111
464973 112         result = self.config.derive_view(view)
CD 113         self.assertFalse(result is view)
114         response = result(None, None)
115         self.assertEqual(response, r)
116
117     def test_function_returns_true_Response_with_renderer(self):
118         from pyramid.response import Response
0c29cf 119
464973 120         r = Response('Hello')
0c29cf 121
464973 122         def view(request):
CD 123             return r
0c29cf 124
464973 125         result = self.config.derive_view(view)
CD 126         self.assertFalse(result is view)
127         response = result(None, None)
128         self.assertEqual(response, r)
129
130     def test_requestonly_default_method_returns_non_adaptable(self):
131         request = DummyRequest()
0c29cf 132
464973 133         class AView(object):
CD 134             def __init__(self, request):
135                 pass
0c29cf 136
464973 137             def __call__(self):
CD 138                 return None
0c29cf 139
464973 140         result = self.config.derive_view(AView)
CD 141         self.assertFalse(result is AView)
142         try:
143             result(None, request)
144         except ValueError as e:
145             self.assertEqual(
146                 e.args[0],
147                 'Could not convert return value of the view callable '
148                 'method __call__ of '
dd3cc8 149                 'class tests.test_viewderivers.AView into a '
464973 150                 'response object. The value returned was None. You may have '
0c29cf 151                 'forgotten to return a value from the view callable.',
MM 152             )
153         else:  # pragma: no cover
464973 154             raise AssertionError
CD 155
156     def test_requestonly_nondefault_method_returns_non_adaptable(self):
157         request = DummyRequest()
0c29cf 158
464973 159         class AView(object):
CD 160             def __init__(self, request):
161                 pass
0c29cf 162
464973 163             def theviewmethod(self):
CD 164                 return None
0c29cf 165
96c87b 166         result = self.config.derive_view(AView, attr='theviewmethod')
464973 167         self.assertFalse(result is AView)
CD 168         try:
169             result(None, request)
170         except ValueError as e:
171             self.assertEqual(
172                 e.args[0],
173                 'Could not convert return value of the view callable '
174                 'method theviewmethod of '
dd3cc8 175                 'class tests.test_viewderivers.AView into a '
464973 176                 'response object. The value returned was None. You may have '
0c29cf 177                 'forgotten to return a value from the view callable.',
MM 178             )
179         else:  # pragma: no cover
464973 180             raise AssertionError
CD 181
182     def test_requestonly_function(self):
183         response = DummyResponse()
0c29cf 184
464973 185         def view(request):
CD 186             return response
0c29cf 187
464973 188         result = self.config.derive_view(view)
CD 189         self.assertFalse(result is view)
190         self.assertEqual(result(None, None), response)
191
192     def test_requestonly_function_with_renderer(self):
193         response = DummyResponse()
0c29cf 194
464973 195         class moo(object):
CD 196             def render_view(inself, req, resp, view_inst, ctx):
197                 self.assertEqual(req, request)
198                 self.assertEqual(resp, 'OK')
199                 self.assertEqual(view_inst, view)
200                 self.assertEqual(ctx, context)
201                 return response
0c29cf 202
464973 203             def clone(self):
CD 204                 return self
0c29cf 205
464973 206         def view(request):
CD 207             return 'OK'
0c29cf 208
96c87b 209         result = self.config.derive_view(view, renderer=moo())
464973 210         self.assertFalse(result.__wraps__ is view)
CD 211         request = self._makeRequest()
212         context = testing.DummyResource()
213         self.assertEqual(result(context, request), response)
214
215     def test_requestonly_function_with_renderer_request_override(self):
216         def moo(info):
217             def inner(value, system):
218                 self.assertEqual(value, 'OK')
219                 self.assertEqual(system['request'], request)
220                 self.assertEqual(system['context'], context)
221                 return b'moo'
0c29cf 222
464973 223             return inner
0c29cf 224
464973 225         def view(request):
CD 226             return 'OK'
0c29cf 227
464973 228         self.config.add_renderer('moo', moo)
CD 229         result = self.config.derive_view(view, renderer='string')
230         self.assertFalse(result is view)
231         request = self._makeRequest()
232         request.override_renderer = 'moo'
233         context = testing.DummyResource()
234         self.assertEqual(result(context, request).body, b'moo')
235
236     def test_requestonly_function_with_renderer_request_has_view(self):
237         response = DummyResponse()
0c29cf 238
464973 239         class moo(object):
CD 240             def render_view(inself, req, resp, view_inst, ctx):
241                 self.assertEqual(req, request)
242                 self.assertEqual(resp, 'OK')
243                 self.assertEqual(view_inst, 'view')
244                 self.assertEqual(ctx, context)
245                 return response
0c29cf 246
464973 247             def clone(self):
CD 248                 return self
0c29cf 249
464973 250         def view(request):
CD 251             return 'OK'
0c29cf 252
464973 253         result = self.config.derive_view(view, renderer=moo())
CD 254         self.assertFalse(result.__wraps__ is view)
255         request = self._makeRequest()
256         request.__view__ = 'view'
257         context = testing.DummyResource()
258         r = result(context, request)
259         self.assertEqual(r, response)
260         self.assertFalse(hasattr(request, '__view__'))
261
262     def test_class_without_attr(self):
263         response = DummyResponse()
0c29cf 264
464973 265         class View(object):
CD 266             def __init__(self, request):
267                 pass
0c29cf 268
464973 269             def __call__(self):
CD 270                 return response
0c29cf 271
464973 272         result = self.config.derive_view(View)
CD 273         request = self._makeRequest()
274         self.assertEqual(result(None, request), response)
275         self.assertEqual(request.__view__.__class__, View)
276
277     def test_class_with_attr(self):
278         response = DummyResponse()
0c29cf 279
464973 280         class View(object):
CD 281             def __init__(self, request):
282                 pass
0c29cf 283
464973 284             def another(self):
CD 285                 return response
0c29cf 286
464973 287         result = self.config.derive_view(View, attr='another')
CD 288         request = self._makeRequest()
289         self.assertEqual(result(None, request), response)
290         self.assertEqual(request.__view__.__class__, View)
291
292     def test_as_function_context_and_request(self):
293         def view(context, request):
294             return 'OK'
0c29cf 295
464973 296         result = self.config.derive_view(view)
CD 297         self.assertTrue(result.__wraps__ is view)
298         self.assertFalse(hasattr(result, '__call_permissive__'))
299         self.assertEqual(view(None, None), 'OK')
300
301     def test_as_function_requestonly(self):
302         response = DummyResponse()
0c29cf 303
464973 304         def view(request):
CD 305             return response
0c29cf 306
464973 307         result = self.config.derive_view(view)
CD 308         self.assertFalse(result is view)
309         self.assertEqual(view.__module__, result.__module__)
310         self.assertEqual(view.__doc__, result.__doc__)
311         self.assertEqual(view.__name__, result.__name__)
312         self.assertFalse(hasattr(result, '__call_permissive__'))
313         self.assertEqual(result(None, None), response)
314
315     def test_as_newstyle_class_context_and_request(self):
316         response = DummyResponse()
0c29cf 317
464973 318         class view(object):
CD 319             def __init__(self, context, request):
320                 pass
0c29cf 321
464973 322             def __call__(self):
CD 323                 return response
0c29cf 324
464973 325         result = self.config.derive_view(view)
CD 326         self.assertFalse(result is view)
327         self.assertEqual(view.__module__, result.__module__)
328         self.assertEqual(view.__doc__, result.__doc__)
329         self.assertEqual(view.__name__, result.__name__)
330         self.assertFalse(hasattr(result, '__call_permissive__'))
331         request = self._makeRequest()
332         self.assertEqual(result(None, request), response)
333         self.assertEqual(request.__view__.__class__, view)
334
335     def test_as_newstyle_class_requestonly(self):
336         response = DummyResponse()
0c29cf 337
464973 338         class view(object):
CD 339             def __init__(self, context, request):
340                 pass
0c29cf 341
464973 342             def __call__(self):
CD 343                 return response
0c29cf 344
464973 345         result = self.config.derive_view(view)
CD 346         self.assertFalse(result is view)
347         self.assertEqual(view.__module__, result.__module__)
348         self.assertEqual(view.__doc__, result.__doc__)
349         self.assertEqual(view.__name__, result.__name__)
350         self.assertFalse(hasattr(result, '__call_permissive__'))
351         request = self._makeRequest()
352         self.assertEqual(result(None, request), response)
353         self.assertEqual(request.__view__.__class__, view)
354
355     def test_as_oldstyle_class_context_and_request(self):
356         response = DummyResponse()
0c29cf 357
464973 358         class view:
CD 359             def __init__(self, context, request):
360                 pass
0c29cf 361
464973 362             def __call__(self):
CD 363                 return response
0c29cf 364
464973 365         result = self.config.derive_view(view)
CD 366         self.assertFalse(result is view)
367         self.assertEqual(view.__module__, result.__module__)
368         self.assertEqual(view.__doc__, result.__doc__)
369         self.assertEqual(view.__name__, result.__name__)
370         self.assertFalse(hasattr(result, '__call_permissive__'))
371         request = self._makeRequest()
372         self.assertEqual(result(None, request), response)
373         self.assertEqual(request.__view__.__class__, view)
374
375     def test_as_oldstyle_class_requestonly(self):
376         response = DummyResponse()
0c29cf 377
464973 378         class view:
CD 379             def __init__(self, context, request):
380                 pass
0c29cf 381
464973 382             def __call__(self):
CD 383                 return response
0c29cf 384
464973 385         result = self.config.derive_view(view)
CD 386         self.assertFalse(result is view)
387         self.assertEqual(view.__module__, result.__module__)
388         self.assertEqual(view.__doc__, result.__doc__)
389         self.assertEqual(view.__name__, result.__name__)
390         self.assertFalse(hasattr(result, '__call_permissive__'))
391         request = self._makeRequest()
392         self.assertEqual(result(None, request), response)
393         self.assertEqual(request.__view__.__class__, view)
394
395     def test_as_instance_context_and_request(self):
396         response = DummyResponse()
0c29cf 397
464973 398         class View:
CD 399             def __call__(self, context, request):
400                 return response
0c29cf 401
464973 402         view = View()
CD 403         result = self.config.derive_view(view)
404         self.assertTrue(result.__wraps__ is view)
405         self.assertFalse(hasattr(result, '__call_permissive__'))
406         self.assertEqual(result(None, None), response)
407
408     def test_as_instance_requestonly(self):
409         response = DummyResponse()
0c29cf 410
464973 411         class View:
CD 412             def __call__(self, request):
413                 return response
0c29cf 414
464973 415         view = View()
CD 416         result = self.config.derive_view(view)
417         self.assertFalse(result is view)
418         self.assertEqual(view.__module__, result.__module__)
419         self.assertEqual(view.__doc__, result.__doc__)
ecc9d8 420         self.assertTrue('test_viewderivers' in result.__name__)
464973 421         self.assertFalse(hasattr(result, '__call_permissive__'))
CD 422         self.assertEqual(result(None, None), response)
423
424     def test_with_debug_authorization_no_authpol(self):
425         response = DummyResponse()
426         view = lambda *arg: response
427         self.config.registry.settings = dict(
0c29cf 428             debug_authorization=True, reload_templates=True
MM 429         )
464973 430         logger = self._registerLogger()
CD 431         result = self.config._derive_view(view, permission='view')
432         self.assertEqual(view.__module__, result.__module__)
433         self.assertEqual(view.__doc__, result.__doc__)
434         self.assertEqual(view.__name__, result.__name__)
435         self.assertFalse(hasattr(result, '__call_permissive__'))
436         request = self._makeRequest()
437         request.view_name = 'view_name'
438         request.url = 'url'
439         self.assertEqual(result(None, request), response)
440         self.assertEqual(len(logger.messages), 1)
0c29cf 441         self.assertEqual(
MM 442             logger.messages[0],
443             "debug_authorization of url url (view name "
444             "'view_name' against context None): Allowed "
445             "(no authorization policy in use)",
446         )
464973 447
CD 448     def test_with_debug_authorization_authn_policy_no_authz_policy(self):
449         response = DummyResponse()
450         view = lambda *arg: response
451         self.config.registry.settings = dict(debug_authorization=True)
452         from pyramid.interfaces import IAuthenticationPolicy
0c29cf 453
464973 454         policy = DummySecurityPolicy(False)
CD 455         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
456         logger = self._registerLogger()
457         result = self.config._derive_view(view, permission='view')
458         self.assertEqual(view.__module__, result.__module__)
459         self.assertEqual(view.__doc__, result.__doc__)
460         self.assertEqual(view.__name__, result.__name__)
461         self.assertFalse(hasattr(result, '__call_permissive__'))
462         request = self._makeRequest()
463         request.view_name = 'view_name'
464         request.url = 'url'
465         self.assertEqual(result(None, request), response)
466         self.assertEqual(len(logger.messages), 1)
0c29cf 467         self.assertEqual(
MM 468             logger.messages[0],
469             "debug_authorization of url url (view name "
470             "'view_name' against context None): Allowed "
471             "(no authorization policy in use)",
472         )
464973 473
CD 474     def test_with_debug_authorization_authz_policy_no_authn_policy(self):
475         response = DummyResponse()
476         view = lambda *arg: response
477         self.config.registry.settings = dict(debug_authorization=True)
478         from pyramid.interfaces import IAuthorizationPolicy
0c29cf 479
464973 480         policy = DummySecurityPolicy(False)
CD 481         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
482         logger = self._registerLogger()
483         result = self.config._derive_view(view, permission='view')
484         self.assertEqual(view.__module__, result.__module__)
485         self.assertEqual(view.__doc__, result.__doc__)
486         self.assertEqual(view.__name__, result.__name__)
487         self.assertFalse(hasattr(result, '__call_permissive__'))
488         request = self._makeRequest()
489         request.view_name = 'view_name'
490         request.url = 'url'
491         self.assertEqual(result(None, request), response)
492         self.assertEqual(len(logger.messages), 1)
0c29cf 493         self.assertEqual(
MM 494             logger.messages[0],
495             "debug_authorization of url url (view name "
496             "'view_name' against context None): Allowed "
497             "(no authorization policy in use)",
498         )
464973 499
CD 500     def test_with_debug_authorization_no_permission(self):
501         response = DummyResponse()
502         view = lambda *arg: response
503         self.config.registry.settings = dict(
0c29cf 504             debug_authorization=True, reload_templates=True
MM 505         )
464973 506         self._registerSecurityPolicy(True)
CD 507         logger = self._registerLogger()
508         result = self.config._derive_view(view)
509         self.assertEqual(view.__module__, result.__module__)
510         self.assertEqual(view.__doc__, result.__doc__)
511         self.assertEqual(view.__name__, result.__name__)
512         self.assertFalse(hasattr(result, '__call_permissive__'))
513         request = self._makeRequest()
514         request.view_name = 'view_name'
515         request.url = 'url'
516         self.assertEqual(result(None, request), response)
517         self.assertEqual(len(logger.messages), 1)
0c29cf 518         self.assertEqual(
MM 519             logger.messages[0],
520             "debug_authorization of url url (view name "
521             "'view_name' against context None): Allowed ("
522             "no permission registered)",
523         )
464973 524
CD 525     def test_debug_auth_permission_authpol_permitted(self):
526         response = DummyResponse()
527         view = lambda *arg: response
528         self.config.registry.settings = dict(
0c29cf 529             debug_authorization=True, reload_templates=True
MM 530         )
464973 531         logger = self._registerLogger()
CD 532         self._registerSecurityPolicy(True)
533         result = self.config._derive_view(view, permission='view')
534         self.assertEqual(view.__module__, result.__module__)
535         self.assertEqual(view.__doc__, result.__doc__)
536         self.assertEqual(view.__name__, result.__name__)
537         self.assertEqual(result.__call_permissive__.__wraps__, view)
538         request = self._makeRequest()
539         request.view_name = 'view_name'
540         request.url = 'url'
541         self.assertEqual(result(None, request), response)
542         self.assertEqual(len(logger.messages), 1)
0c29cf 543         self.assertEqual(
MM 544             logger.messages[0],
545             "debug_authorization of url url (view name "
546             "'view_name' against context None): True",
547         )
464973 548
CD 549     def test_debug_auth_permission_authpol_permitted_no_request(self):
550         response = DummyResponse()
551         view = lambda *arg: response
552         self.config.registry.settings = dict(
0c29cf 553             debug_authorization=True, reload_templates=True
MM 554         )
464973 555         logger = self._registerLogger()
CD 556         self._registerSecurityPolicy(True)
557         result = self.config._derive_view(view, permission='view')
558         self.assertEqual(view.__module__, result.__module__)
559         self.assertEqual(view.__doc__, result.__doc__)
560         self.assertEqual(view.__name__, result.__name__)
561         self.assertEqual(result.__call_permissive__.__wraps__, view)
562         self.assertEqual(result(None, None), response)
563         self.assertEqual(len(logger.messages), 1)
0c29cf 564         self.assertEqual(
MM 565             logger.messages[0],
566             "debug_authorization of url None (view name "
567             "None against context None): True",
568         )
464973 569
CD 570     def test_debug_auth_permission_authpol_denied(self):
571         from pyramid.httpexceptions import HTTPForbidden
0c29cf 572
464973 573         response = DummyResponse()
CD 574         view = lambda *arg: response
575         self.config.registry.settings = dict(
0c29cf 576             debug_authorization=True, reload_templates=True
MM 577         )
464973 578         logger = self._registerLogger()
CD 579         self._registerSecurityPolicy(False)
580         result = self.config._derive_view(view, permission='view')
581         self.assertEqual(view.__module__, result.__module__)
582         self.assertEqual(view.__doc__, result.__doc__)
583         self.assertEqual(view.__name__, result.__name__)
584         self.assertEqual(result.__call_permissive__.__wraps__, view)
585         request = self._makeRequest()
586         request.view_name = 'view_name'
587         request.url = 'url'
588         self.assertRaises(HTTPForbidden, result, None, request)
589         self.assertEqual(len(logger.messages), 1)
0c29cf 590         self.assertEqual(
MM 591             logger.messages[0],
592             "debug_authorization of url url (view name "
593             "'view_name' against context None): False",
594         )
464973 595
CD 596     def test_debug_auth_permission_authpol_denied2(self):
597         view = lambda *arg: 'OK'
598         self.config.registry.settings = dict(
0c29cf 599             debug_authorization=True, reload_templates=True
MM 600         )
464973 601         self._registerLogger()
CD 602         self._registerSecurityPolicy(False)
603         result = self.config._derive_view(view, permission='view')
604         self.assertEqual(view.__module__, result.__module__)
605         self.assertEqual(view.__doc__, result.__doc__)
606         self.assertEqual(view.__name__, result.__name__)
607         request = self._makeRequest()
608         request.view_name = 'view_name'
609         request.url = 'url'
610         permitted = result.__permitted__(None, None)
611         self.assertEqual(permitted, False)
612
613     def test_debug_auth_permission_authpol_overridden(self):
614         from pyramid.security import NO_PERMISSION_REQUIRED
0c29cf 615
464973 616         response = DummyResponse()
CD 617         view = lambda *arg: response
618         self.config.registry.settings = dict(
0c29cf 619             debug_authorization=True, reload_templates=True
MM 620         )
464973 621         logger = self._registerLogger()
CD 622         self._registerSecurityPolicy(False)
0c29cf 623         result = self.config._derive_view(
MM 624             view, permission=NO_PERMISSION_REQUIRED
625         )
464973 626         self.assertEqual(view.__module__, result.__module__)
CD 627         self.assertEqual(view.__doc__, result.__doc__)
628         self.assertEqual(view.__name__, result.__name__)
629         self.assertFalse(hasattr(result, '__call_permissive__'))
630         request = self._makeRequest()
631         request.view_name = 'view_name'
632         request.url = 'url'
633         self.assertEqual(result(None, request), response)
634         self.assertEqual(len(logger.messages), 1)
0c29cf 635         self.assertEqual(
MM 636             logger.messages[0],
637             "debug_authorization of url url (view name "
638             "'view_name' against context None): "
639             "Allowed (NO_PERMISSION_REQUIRED)",
640         )
464973 641
e8c66a 642     def test_debug_auth_permission_authpol_permitted_excview(self):
MM 643         response = DummyResponse()
644         view = lambda *arg: response
645         self.config.registry.settings = dict(
0c29cf 646             debug_authorization=True, reload_templates=True
MM 647         )
e8c66a 648         logger = self._registerLogger()
MM 649         self._registerSecurityPolicy(True)
650         result = self.config._derive_view(
0c29cf 651             view, context=Exception, permission='view'
MM 652         )
e8c66a 653         self.assertEqual(view.__module__, result.__module__)
MM 654         self.assertEqual(view.__doc__, result.__doc__)
655         self.assertEqual(view.__name__, result.__name__)
656         self.assertEqual(result.__call_permissive__.__wraps__, view)
657         request = self._makeRequest()
658         request.view_name = 'view_name'
659         request.url = 'url'
660         self.assertEqual(result(Exception(), request), response)
661         self.assertEqual(len(logger.messages), 1)
0c29cf 662         self.assertEqual(
MM 663             logger.messages[0],
664             "debug_authorization of url url (view name "
665             "'view_name' against context Exception()): True",
666         )
e8c66a 667
464973 668     def test_secured_view_authn_policy_no_authz_policy(self):
CD 669         response = DummyResponse()
670         view = lambda *arg: response
671         self.config.registry.settings = {}
672         from pyramid.interfaces import IAuthenticationPolicy
0c29cf 673
464973 674         policy = DummySecurityPolicy(False)
CD 675         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
676         result = self.config._derive_view(view, permission='view')
677         self.assertEqual(view.__module__, result.__module__)
678         self.assertEqual(view.__doc__, result.__doc__)
679         self.assertEqual(view.__name__, result.__name__)
680         self.assertFalse(hasattr(result, '__call_permissive__'))
681         request = self._makeRequest()
682         request.view_name = 'view_name'
683         request.url = 'url'
684         self.assertEqual(result(None, request), response)
685
686     def test_secured_view_authz_policy_no_authn_policy(self):
687         response = DummyResponse()
688         view = lambda *arg: response
689         self.config.registry.settings = {}
690         from pyramid.interfaces import IAuthorizationPolicy
0c29cf 691
464973 692         policy = DummySecurityPolicy(False)
CD 693         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
694         result = self.config._derive_view(view, permission='view')
695         self.assertEqual(view.__module__, result.__module__)
696         self.assertEqual(view.__doc__, result.__doc__)
697         self.assertEqual(view.__name__, result.__name__)
698         self.assertFalse(hasattr(result, '__call_permissive__'))
699         request = self._makeRequest()
700         request.view_name = 'view_name'
701         request.url = 'url'
702         self.assertEqual(result(None, request), response)
703
704     def test_secured_view_raises_forbidden_no_name(self):
705         from pyramid.interfaces import IAuthenticationPolicy
706         from pyramid.interfaces import IAuthorizationPolicy
707         from pyramid.httpexceptions import HTTPForbidden
0c29cf 708
464973 709         response = DummyResponse()
CD 710         view = lambda *arg: response
711         self.config.registry.settings = {}
712         policy = DummySecurityPolicy(False)
713         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
714         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
715         result = self.config._derive_view(view, permission='view')
716         request = self._makeRequest()
717         request.view_name = 'view_name'
718         request.url = 'url'
719         try:
720             result(None, request)
721         except HTTPForbidden as e:
0c29cf 722             self.assertEqual(
MM 723                 e.message, 'Unauthorized: <lambda> failed permission check'
724             )
725         else:  # pragma: no cover
464973 726             raise AssertionError
CD 727
728     def test_secured_view_raises_forbidden_with_name(self):
729         from pyramid.interfaces import IAuthenticationPolicy
730         from pyramid.interfaces import IAuthorizationPolicy
731         from pyramid.httpexceptions import HTTPForbidden
0c29cf 732
10ddb6 733         def myview(request):  # pragma: no cover
0c29cf 734             pass
MM 735
464973 736         self.config.registry.settings = {}
CD 737         policy = DummySecurityPolicy(False)
738         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
739         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
740         result = self.config._derive_view(myview, permission='view')
741         request = self._makeRequest()
742         request.view_name = 'view_name'
743         request.url = 'url'
744         try:
745             result(None, request)
746         except HTTPForbidden as e:
0c29cf 747             self.assertEqual(
MM 748                 e.message, 'Unauthorized: myview failed permission check'
749             )
750         else:  # pragma: no cover
464973 751             raise AssertionError
CD 752
2160ce 753     def test_secured_view_skipped_by_default_on_exception_view(self):
MM 754         from pyramid.request import Request
755         from pyramid.security import NO_PERMISSION_REQUIRED
0c29cf 756
2160ce 757         def view(request):
MM 758             raise ValueError
0c29cf 759
2160ce 760         def excview(request):
MM 761             return 'hello'
0c29cf 762
2160ce 763         self._registerSecurityPolicy(False)
MM 764         self.config.add_settings({'debug_authorization': True})
765         self.config.set_default_permission('view')
0c29cf 766         self.config.add_view(
MM 767             view, name='foo', permission=NO_PERMISSION_REQUIRED
768         )
2160ce 769         self.config.add_view(excview, context=ValueError, renderer='string')
MM 770         app = self.config.make_wsgi_app()
771         request = Request.blank('/foo', base_url='http://example.com')
772         request.method = 'POST'
773         response = request.get_response(app)
774         self.assertTrue(b'hello' in response.body)
775
776     def test_secured_view_failed_on_explicit_exception_view(self):
777         from pyramid.httpexceptions import HTTPForbidden
778         from pyramid.request import Request
779         from pyramid.security import NO_PERMISSION_REQUIRED
0c29cf 780
2160ce 781         def view(request):
MM 782             raise ValueError
0c29cf 783
10ddb6 784         def excview(request):  # pragma: no cover
0c29cf 785             pass
MM 786
2160ce 787         self._registerSecurityPolicy(False)
0c29cf 788         self.config.add_view(
MM 789             view, name='foo', permission=NO_PERMISSION_REQUIRED
790         )
791         self.config.add_view(
792             excview, context=ValueError, renderer='string', permission='view'
793         )
2160ce 794         app = self.config.make_wsgi_app()
MM 795         request = Request.blank('/foo', base_url='http://example.com')
796         request.method = 'POST'
797         try:
798             request.get_response(app)
799         except HTTPForbidden:
800             pass
0c29cf 801         else:  # pragma: no cover
2160ce 802             raise AssertionError
MM 803
804     def test_secured_view_passed_on_explicit_exception_view(self):
805         from pyramid.request import Request
806         from pyramid.security import NO_PERMISSION_REQUIRED
0c29cf 807
2160ce 808         def view(request):
MM 809             raise ValueError
0c29cf 810
2160ce 811         def excview(request):
MM 812             return 'hello'
0c29cf 813
2160ce 814         self._registerSecurityPolicy(True)
0c29cf 815         self.config.add_view(
MM 816             view, name='foo', permission=NO_PERMISSION_REQUIRED
817         )
818         self.config.add_view(
819             excview, context=ValueError, renderer='string', permission='view'
820         )
2160ce 821         app = self.config.make_wsgi_app()
MM 822         request = Request.blank('/foo', base_url='http://example.com')
823         request.method = 'POST'
824         request.headers['X-CSRF-Token'] = 'foo'
825         response = request.get_response(app)
826         self.assertTrue(b'hello' in response.body)
827
464973 828     def test_predicate_mismatch_view_has_no_name(self):
CD 829         from pyramid.exceptions import PredicateMismatch
0c29cf 830
464973 831         response = DummyResponse()
CD 832         view = lambda *arg: response
0c29cf 833
464973 834         def predicate1(context, request):
CD 835             return False
0c29cf 836
464973 837         predicate1.text = lambda *arg: 'text'
CD 838         result = self.config._derive_view(view, predicates=[predicate1])
839         request = self._makeRequest()
840         request.method = 'POST'
841         try:
842             result(None, None)
843         except PredicateMismatch as e:
0c29cf 844             self.assertEqual(
MM 845                 e.detail, 'predicate mismatch for view <lambda> (text)'
846             )
847         else:  # pragma: no cover
464973 848             raise AssertionError
CD 849
850     def test_predicate_mismatch_view_has_name(self):
851         from pyramid.exceptions import PredicateMismatch
0c29cf 852
10ddb6 853         def myview(request):  # pragma: no cover
0c29cf 854             pass
MM 855
464973 856         def predicate1(context, request):
CD 857             return False
0c29cf 858
464973 859         predicate1.text = lambda *arg: 'text'
CD 860         result = self.config._derive_view(myview, predicates=[predicate1])
861         request = self._makeRequest()
862         request.method = 'POST'
863         try:
864             result(None, None)
865         except PredicateMismatch as e:
0c29cf 866             self.assertEqual(
MM 867                 e.detail, 'predicate mismatch for view myview (text)'
868             )
869         else:  # pragma: no cover
464973 870             raise AssertionError
CD 871
872     def test_predicate_mismatch_exception_has_text_in_detail(self):
873         from pyramid.exceptions import PredicateMismatch
0c29cf 874
10ddb6 875         def myview(request):  # pragma: no cover
0c29cf 876             pass
MM 877
464973 878         def predicate1(context, request):
CD 879             return True
0c29cf 880
464973 881         predicate1.text = lambda *arg: 'pred1'
0c29cf 882
464973 883         def predicate2(context, request):
CD 884             return False
0c29cf 885
464973 886         predicate2.text = lambda *arg: 'pred2'
0c29cf 887         result = self.config._derive_view(
MM 888             myview, predicates=[predicate1, predicate2]
889         )
464973 890         request = self._makeRequest()
CD 891         request.method = 'POST'
892         try:
893             result(None, None)
894         except PredicateMismatch as e:
0c29cf 895             self.assertEqual(
MM 896                 e.detail, 'predicate mismatch for view myview (pred2)'
897             )
898         else:  # pragma: no cover
464973 899             raise AssertionError
CD 900
901     def test_with_predicates_all(self):
902         response = DummyResponse()
903         view = lambda *arg: response
904         predicates = []
0c29cf 905
464973 906         def predicate1(context, request):
CD 907             predicates.append(True)
908             return True
0c29cf 909
464973 910         def predicate2(context, request):
CD 911             predicates.append(True)
912             return True
0c29cf 913
MM 914         result = self.config._derive_view(
915             view, predicates=[predicate1, predicate2]
916         )
464973 917         request = self._makeRequest()
CD 918         request.method = 'POST'
919         next = result(None, None)
920         self.assertEqual(next, response)
921         self.assertEqual(predicates, [True, True])
922
923     def test_with_predicates_checker(self):
924         view = lambda *arg: 'OK'
925         predicates = []
0c29cf 926
464973 927         def predicate1(context, request):
CD 928             predicates.append(True)
929             return True
0c29cf 930
464973 931         def predicate2(context, request):
CD 932             predicates.append(True)
933             return True
0c29cf 934
MM 935         result = self.config._derive_view(
936             view, predicates=[predicate1, predicate2]
937         )
464973 938         request = self._makeRequest()
CD 939         request.method = 'POST'
940         next = result.__predicated__(None, None)
941         self.assertEqual(next, True)
942         self.assertEqual(predicates, [True, True])
943
944     def test_with_predicates_notall(self):
945         from pyramid.httpexceptions import HTTPNotFound
0c29cf 946
464973 947         view = lambda *arg: 'OK'
CD 948         predicates = []
0c29cf 949
464973 950         def predicate1(context, request):
CD 951             predicates.append(True)
952             return True
0c29cf 953
464973 954         predicate1.text = lambda *arg: 'text'
0c29cf 955
464973 956         def predicate2(context, request):
CD 957             predicates.append(True)
958             return False
0c29cf 959
464973 960         predicate2.text = lambda *arg: 'text'
0c29cf 961         result = self.config._derive_view(
MM 962             view, predicates=[predicate1, predicate2]
963         )
464973 964         request = self._makeRequest()
CD 965         request.method = 'POST'
966         self.assertRaises(HTTPNotFound, result, None, None)
967         self.assertEqual(predicates, [True, True])
968
969     def test_with_wrapper_viewname(self):
970         from pyramid.response import Response
971         from pyramid.interfaces import IView
972         from pyramid.interfaces import IViewClassifier
0c29cf 973
464973 974         inner_response = Response('OK')
0c29cf 975
464973 976         def inner_view(context, request):
CD 977             return inner_response
0c29cf 978
464973 979         def outer_view(context, request):
CD 980             self.assertEqual(request.wrapped_response, inner_response)
981             self.assertEqual(request.wrapped_body, inner_response.body)
0c29cf 982             self.assertEqual(
MM 983                 request.wrapped_view.__original_view__, inner_view
984             )
464973 985             return Response(b'outer ' + request.wrapped_body)
0c29cf 986
464973 987         self.config.registry.registerAdapter(
0c29cf 988             outer_view, (IViewClassifier, None, None), IView, 'owrap'
MM 989         )
990         result = self.config._derive_view(
991             inner_view, viewname='inner', wrapper_viewname='owrap'
992         )
464973 993         self.assertFalse(result is inner_view)
CD 994         self.assertEqual(inner_view.__module__, result.__module__)
995         self.assertEqual(inner_view.__doc__, result.__doc__)
996         request = self._makeRequest()
997         response = result(None, request)
998         self.assertEqual(response.body, b'outer OK')
999
1000     def test_with_wrapper_viewname_notfound(self):
1001         from pyramid.response import Response
0c29cf 1002
464973 1003         inner_response = Response('OK')
0c29cf 1004
464973 1005         def inner_view(context, request):
CD 1006             return inner_response
0c29cf 1007
MM 1008         wrapped = self.config._derive_view(
1009             inner_view, viewname='inner', wrapper_viewname='owrap'
1010         )
464973 1011         request = self._makeRequest()
CD 1012         self.assertRaises(ValueError, wrapped, None, request)
1013
1014     def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
1015         response = DummyResponse()
0c29cf 1016
464973 1017         class renderer(object):
CD 1018             def render_view(inself, req, resp, view_inst, ctx):
1019                 self.assertEqual(req, request)
0c29cf 1020                 self.assertEqual(resp, {'a': '1'})
464973 1021                 self.assertEqual(view_inst.__class__, View)
CD 1022                 self.assertEqual(ctx, context)
1023                 return response
0c29cf 1024
464973 1025             def clone(self):
CD 1026                 return self
0c29cf 1027
464973 1028         class View(object):
CD 1029             def __init__(self, context, request):
1030                 pass
0c29cf 1031
464973 1032             def index(self):
0c29cf 1033                 return {'a': '1'}
MM 1034
1035         result = self.config._derive_view(
1036             View, renderer=renderer(), attr='index'
1037         )
464973 1038         self.assertFalse(result is View)
CD 1039         self.assertEqual(result.__module__, View.__module__)
1040         self.assertEqual(result.__doc__, View.__doc__)
1041         self.assertEqual(result.__name__, View.__name__)
1042         request = self._makeRequest()
1043         context = testing.DummyResource()
1044         self.assertEqual(result(context, request), response)
1045
1046     def test_as_newstyle_class_requestonly_attr_and_renderer(self):
1047         response = DummyResponse()
0c29cf 1048
464973 1049         class renderer(object):
CD 1050             def render_view(inself, req, resp, view_inst, ctx):
1051                 self.assertEqual(req, request)
0c29cf 1052                 self.assertEqual(resp, {'a': '1'})
464973 1053                 self.assertEqual(view_inst.__class__, View)
CD 1054                 self.assertEqual(ctx, context)
1055                 return response
0c29cf 1056
464973 1057             def clone(self):
CD 1058                 return self
0c29cf 1059
464973 1060         class View(object):
CD 1061             def __init__(self, request):
1062                 pass
0c29cf 1063
464973 1064             def index(self):
0c29cf 1065                 return {'a': '1'}
MM 1066
1067         result = self.config.derive_view(
1068             View, renderer=renderer(), attr='index'
1069         )
464973 1070         self.assertFalse(result is View)
CD 1071         self.assertEqual(result.__module__, View.__module__)
1072         self.assertEqual(result.__doc__, View.__doc__)
1073         self.assertEqual(result.__name__, View.__name__)
1074         request = self._makeRequest()
1075         context = testing.DummyResource()
1076         self.assertEqual(result(context, request), response)
1077
1078     def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
1079         response = DummyResponse()
0c29cf 1080
464973 1081         class renderer(object):
CD 1082             def render_view(inself, req, resp, view_inst, ctx):
1083                 self.assertEqual(req, request)
0c29cf 1084                 self.assertEqual(resp, {'a': '1'})
464973 1085                 self.assertEqual(view_inst.__class__, View)
CD 1086                 self.assertEqual(ctx, context)
1087                 return response
0c29cf 1088
464973 1089             def clone(self):
CD 1090                 return self
0c29cf 1091
464973 1092         class View:
CD 1093             def __init__(self, context, request):
1094                 pass
0c29cf 1095
464973 1096             def index(self):
0c29cf 1097                 return {'a': '1'}
MM 1098
1099         result = self.config.derive_view(
1100             View, renderer=renderer(), attr='index'
1101         )
464973 1102         self.assertFalse(result is View)
CD 1103         self.assertEqual(result.__module__, View.__module__)
1104         self.assertEqual(result.__doc__, View.__doc__)
1105         self.assertEqual(result.__name__, View.__name__)
1106         request = self._makeRequest()
1107         context = testing.DummyResource()
1108         self.assertEqual(result(context, request), response)
1109
1110     def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
1111         response = DummyResponse()
0c29cf 1112
464973 1113         class renderer(object):
CD 1114             def render_view(inself, req, resp, view_inst, ctx):
1115                 self.assertEqual(req, request)
0c29cf 1116                 self.assertEqual(resp, {'a': '1'})
464973 1117                 self.assertEqual(view_inst.__class__, View)
CD 1118                 self.assertEqual(ctx, context)
1119                 return response
0c29cf 1120
464973 1121             def clone(self):
CD 1122                 return self
0c29cf 1123
464973 1124         class View:
CD 1125             def __init__(self, request):
1126                 pass
0c29cf 1127
464973 1128             def index(self):
0c29cf 1129                 return {'a': '1'}
MM 1130
1131         result = self.config.derive_view(
1132             View, renderer=renderer(), attr='index'
1133         )
464973 1134         self.assertFalse(result is View)
CD 1135         self.assertEqual(result.__module__, View.__module__)
1136         self.assertEqual(result.__doc__, View.__doc__)
1137         self.assertEqual(result.__name__, View.__name__)
1138         request = self._makeRequest()
1139         context = testing.DummyResource()
1140         self.assertEqual(result(context, request), response)
1141
1142     def test_as_instance_context_and_request_attr_and_renderer(self):
1143         response = DummyResponse()
0c29cf 1144
464973 1145         class renderer(object):
CD 1146             def render_view(inself, req, resp, view_inst, ctx):
1147                 self.assertEqual(req, request)
0c29cf 1148                 self.assertEqual(resp, {'a': '1'})
464973 1149                 self.assertEqual(view_inst, view)
CD 1150                 self.assertEqual(ctx, context)
1151                 return response
0c29cf 1152
464973 1153             def clone(self):
CD 1154                 return self
0c29cf 1155
464973 1156         class View:
CD 1157             def index(self, context, request):
0c29cf 1158                 return {'a': '1'}
MM 1159
464973 1160         view = View()
0c29cf 1161         result = self.config.derive_view(
MM 1162             view, renderer=renderer(), attr='index'
1163         )
464973 1164         self.assertFalse(result is view)
CD 1165         self.assertEqual(result.__module__, view.__module__)
1166         self.assertEqual(result.__doc__, view.__doc__)
1167         request = self._makeRequest()
1168         context = testing.DummyResource()
1169         self.assertEqual(result(context, request), response)
1170
1171     def test_as_instance_requestonly_attr_and_renderer(self):
1172         response = DummyResponse()
0c29cf 1173
464973 1174         class renderer(object):
CD 1175             def render_view(inself, req, resp, view_inst, ctx):
1176                 self.assertEqual(req, request)
0c29cf 1177                 self.assertEqual(resp, {'a': '1'})
464973 1178                 self.assertEqual(view_inst, view)
CD 1179                 self.assertEqual(ctx, context)
1180                 return response
0c29cf 1181
464973 1182             def clone(self):
CD 1183                 return self
0c29cf 1184
464973 1185         class View:
CD 1186             def index(self, request):
0c29cf 1187                 return {'a': '1'}
MM 1188
464973 1189         view = View()
0c29cf 1190         result = self.config.derive_view(
MM 1191             view, renderer=renderer(), attr='index'
1192         )
464973 1193         self.assertFalse(result is view)
CD 1194         self.assertEqual(result.__module__, view.__module__)
1195         self.assertEqual(result.__doc__, view.__doc__)
1196         request = self._makeRequest()
1197         context = testing.DummyResource()
1198         self.assertEqual(result(context, request), response)
1199
1200     def test_with_view_mapper_config_specified(self):
1201         response = DummyResponse()
0c29cf 1202
464973 1203         class mapper(object):
CD 1204             def __init__(self, **kw):
1205                 self.kw = kw
0c29cf 1206
464973 1207             def __call__(self, view):
CD 1208                 def wrapped(context, request):
1209                     return response
0c29cf 1210
464973 1211                 return wrapped
0c29cf 1212
10ddb6 1213         def view(context, request):  # pragma: no cover
0c29cf 1214             return 'NOTOK'
MM 1215
464973 1216         result = self.config._derive_view(view, mapper=mapper)
CD 1217         self.assertFalse(result.__wraps__ is view)
1218         self.assertEqual(result(None, None), response)
1219
1220     def test_with_view_mapper_view_specified(self):
1221         from pyramid.response import Response
0c29cf 1222
464973 1223         response = Response()
0c29cf 1224
464973 1225         def mapper(**kw):
CD 1226             def inner(view):
1227                 def superinner(context, request):
1228                     self.assertEqual(request, None)
1229                     return response
0c29cf 1230
464973 1231                 return superinner
0c29cf 1232
464973 1233             return inner
0c29cf 1234
10ddb6 1235         def view(context, request):  # pragma: no cover
0c29cf 1236             return 'NOTOK'
MM 1237
464973 1238         view.__view_mapper__ = mapper
CD 1239         result = self.config.derive_view(view)
1240         self.assertFalse(result.__wraps__ is view)
1241         self.assertEqual(result(None, None), response)
1242
1243     def test_with_view_mapper_default_mapper_specified(self):
1244         from pyramid.response import Response
0c29cf 1245
464973 1246         response = Response()
0c29cf 1247
464973 1248         def mapper(**kw):
CD 1249             def inner(view):
1250                 def superinner(context, request):
1251                     self.assertEqual(request, None)
0c29cf 1252                     return response
MM 1253
464973 1254                 return superinner
0c29cf 1255
464973 1256             return inner
0c29cf 1257
464973 1258         self.config.set_view_mapper(mapper)
0c29cf 1259
10ddb6 1260         def view(context, request):  # pragma: no cover
0c29cf 1261             return 'NOTOK'
MM 1262
464973 1263         result = self.config.derive_view(view)
CD 1264         self.assertFalse(result.__wraps__ is view)
1265         self.assertEqual(result(None, None), response)
1266
1267     def test_attr_wrapped_view_branching_default_phash(self):
d579f2 1268         from pyramid.config.predicates import DEFAULT_PHASH
0c29cf 1269
10ddb6 1270         def view(context, request):  # pragma: no cover
0c29cf 1271             pass
MM 1272
611aa3 1273         result = self.config._derive_view(view, phash=DEFAULT_PHASH)
464973 1274         self.assertEqual(result.__wraps__, view)
CD 1275
1276     def test_attr_wrapped_view_branching_nondefault_phash(self):
10ddb6 1277         def view(context, request):  # pragma: no cover
0c29cf 1278             pass
MM 1279
611aa3 1280         result = self.config._derive_view(view, phash='nondefault')
464973 1281         self.assertNotEqual(result, view)
CD 1282
1283     def test_http_cached_view_integer(self):
1284         import datetime
1285         from pyramid.response import Response
0c29cf 1286
464973 1287         response = Response('OK')
0c29cf 1288
464973 1289         def inner_view(context, request):
CD 1290             return response
0c29cf 1291
611aa3 1292         result = self.config._derive_view(inner_view, http_cache=3600)
464973 1293         self.assertFalse(result is inner_view)
CD 1294         self.assertEqual(inner_view.__module__, result.__module__)
1295         self.assertEqual(inner_view.__doc__, result.__doc__)
1296         request = self._makeRequest()
1297         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1298         result = result(None, request)
1299         self.assertEqual(result, response)
1300         headers = dict(result.headerlist)
1301         expires = parse_httpdate(headers['Expires'])
1302         assert_similar_datetime(expires, when)
1303         self.assertEqual(headers['Cache-Control'], 'max-age=3600')
1304
1305     def test_http_cached_view_timedelta(self):
1306         import datetime
1307         from pyramid.response import Response
0c29cf 1308
464973 1309         response = Response('OK')
0c29cf 1310
464973 1311         def inner_view(context, request):
CD 1312             return response
0c29cf 1313
MM 1314         result = self.config._derive_view(
1315             inner_view, http_cache=datetime.timedelta(hours=1)
1316         )
464973 1317         self.assertFalse(result is inner_view)
CD 1318         self.assertEqual(inner_view.__module__, result.__module__)
1319         self.assertEqual(inner_view.__doc__, result.__doc__)
1320         request = self._makeRequest()
1321         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1322         result = result(None, request)
1323         self.assertEqual(result, response)
1324         headers = dict(result.headerlist)
1325         expires = parse_httpdate(headers['Expires'])
1326         assert_similar_datetime(expires, when)
1327         self.assertEqual(headers['Cache-Control'], 'max-age=3600')
1328
1329     def test_http_cached_view_tuple(self):
1330         import datetime
1331         from pyramid.response import Response
0c29cf 1332
464973 1333         response = Response('OK')
0c29cf 1334
464973 1335         def inner_view(context, request):
CD 1336             return response
0c29cf 1337
MM 1338         result = self.config._derive_view(
1339             inner_view, http_cache=(3600, {'public': True})
1340         )
464973 1341         self.assertFalse(result is inner_view)
CD 1342         self.assertEqual(inner_view.__module__, result.__module__)
1343         self.assertEqual(inner_view.__doc__, result.__doc__)
1344         request = self._makeRequest()
1345         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1346         result = result(None, request)
1347         self.assertEqual(result, response)
1348         headers = dict(result.headerlist)
1349         expires = parse_httpdate(headers['Expires'])
1350         assert_similar_datetime(expires, when)
1351         self.assertEqual(headers['Cache-Control'], 'max-age=3600, public')
1352
1353     def test_http_cached_view_tuple_seconds_None(self):
1354         from pyramid.response import Response
0c29cf 1355
464973 1356         response = Response('OK')
0c29cf 1357
464973 1358         def inner_view(context, request):
CD 1359             return response
0c29cf 1360
MM 1361         result = self.config._derive_view(
1362             inner_view, http_cache=(None, {'public': True})
1363         )
464973 1364         self.assertFalse(result is inner_view)
CD 1365         self.assertEqual(inner_view.__module__, result.__module__)
1366         self.assertEqual(inner_view.__doc__, result.__doc__)
1367         request = self._makeRequest()
1368         result = result(None, request)
1369         self.assertEqual(result, response)
1370         headers = dict(result.headerlist)
1371         self.assertFalse('Expires' in headers)
1372         self.assertEqual(headers['Cache-Control'], 'public')
1373
1374     def test_http_cached_view_prevent_auto_set(self):
1375         from pyramid.response import Response
0c29cf 1376
464973 1377         response = Response()
CD 1378         response.cache_control.prevent_auto = True
0c29cf 1379
464973 1380         def inner_view(context, request):
CD 1381             return response
0c29cf 1382
464973 1383         result = self.config._derive_view(inner_view, http_cache=3600)
CD 1384         request = self._makeRequest()
1385         result = result(None, request)
0c29cf 1386         self.assertEqual(result, response)  # doesn't blow up
464973 1387         headers = dict(result.headerlist)
CD 1388         self.assertFalse('Expires' in headers)
1389         self.assertFalse('Cache-Control' in headers)
1390
1391     def test_http_cached_prevent_http_cache_in_settings(self):
1392         self.config.registry.settings['prevent_http_cache'] = True
1393         from pyramid.response import Response
0c29cf 1394
464973 1395         response = Response()
0c29cf 1396
464973 1397         def inner_view(context, request):
CD 1398             return response
0c29cf 1399
464973 1400         result = self.config._derive_view(inner_view, http_cache=3600)
CD 1401         request = self._makeRequest()
1402         result = result(None, request)
1403         self.assertEqual(result, response)
1404         headers = dict(result.headerlist)
1405         self.assertFalse('Expires' in headers)
1406         self.assertFalse('Cache-Control' in headers)
1407
1408     def test_http_cached_view_bad_tuple(self):
10ddb6 1409         def view(request):  # pragma: no cover
0c29cf 1410             pass
MM 1411
1412         self.assertRaises(
1413             ConfigurationError,
1414             self.config._derive_view,
1415             view,
1416             http_cache=(None,),
1417         )
464973 1418
de3d0c 1419     def test_csrf_view_ignores_GET(self):
MM 1420         response = DummyResponse()
0c29cf 1421
de3d0c 1422         def inner_view(request):
MM 1423             return response
0c29cf 1424
de3d0c 1425         request = self._makeRequest()
MM 1426         request.method = 'GET'
1427         view = self.config._derive_view(inner_view, require_csrf=True)
1428         result = view(None, request)
1429         self.assertTrue(result is response)
6b35eb 1430
de3d0c 1431     def test_csrf_view_fails_with_bad_POST_header(self):
MM 1432         from pyramid.exceptions import BadCSRFToken
0c29cf 1433
10ddb6 1434         def inner_view(request):  # pragma: no cover
0c29cf 1435             pass
MM 1436
de3d0c 1437         request = self._makeRequest()
MM 1438         request.scheme = "http"
1439         request.method = 'POST'
1440         request.session = DummySession({'csrf_token': 'foo'})
1441         request.headers = {'X-CSRF-Token': 'bar'}
1442         view = self.config._derive_view(inner_view, require_csrf=True)
1443         self.assertRaises(BadCSRFToken, lambda: view(None, request))
6b35eb 1444
de3d0c 1445     def test_csrf_view_passes_with_good_POST_header(self):
9e9fa9 1446         response = DummyResponse()
0c29cf 1447
9e9fa9 1448         def inner_view(request):
MM 1449             return response
0c29cf 1450
9e9fa9 1451         request = self._makeRequest()
65dee6 1452         request.scheme = "http"
6b35eb 1453         request.method = 'POST'
9e9fa9 1454         request.session = DummySession({'csrf_token': 'foo'})
MM 1455         request.headers = {'X-CSRF-Token': 'foo'}
1456         view = self.config._derive_view(inner_view, require_csrf=True)
1457         result = view(None, request)
1458         self.assertTrue(result is response)
1459
de3d0c 1460     def test_csrf_view_fails_with_bad_POST_token(self):
MM 1461         from pyramid.exceptions import BadCSRFToken
0c29cf 1462
10ddb6 1463         def inner_view(request):  # pragma: no cover
0c29cf 1464             pass
MM 1465
de3d0c 1466         request = self._makeRequest()
MM 1467         request.scheme = "http"
1468         request.method = 'POST'
1469         request.session = DummySession({'csrf_token': 'foo'})
1470         request.POST = {'csrf_token': 'bar'}
1471         view = self.config._derive_view(inner_view, require_csrf=True)
1472         self.assertRaises(BadCSRFToken, lambda: view(None, request))
1473
1474     def test_csrf_view_passes_with_good_POST_token(self):
9e9fa9 1475         response = DummyResponse()
0c29cf 1476
9e9fa9 1477         def inner_view(request):
MM 1478             return response
0c29cf 1479
9e9fa9 1480         request = self._makeRequest()
65dee6 1481         request.scheme = "http"
DS 1482         request.method = 'POST'
1483         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1484         request.POST = {'csrf_token': 'foo'}
MM 1485         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1486         result = view(None, request)
DS 1487         self.assertTrue(result is response)
1488
1489     def test_csrf_view_https_domain(self):
1490         response = DummyResponse()
0c29cf 1491
65dee6 1492         def inner_view(request):
DS 1493             return response
0c29cf 1494
65dee6 1495         request = self._makeRequest()
DS 1496         request.scheme = "https"
1497         request.domain = "example.com"
884043 1498         request.host_port = "443"
65dee6 1499         request.referrer = "https://example.com/login/"
6b35eb 1500         request.method = 'POST'
9e9fa9 1501         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1502         request.POST = {'csrf_token': 'foo'}
6b35eb 1503         view = self.config._derive_view(inner_view, require_csrf=True)
MM 1504         result = view(None, request)
1505         self.assertTrue(result is response)
1506
21d5be 1507     def test_csrf_view_fails_on_bad_PUT_header(self):
DS 1508         from pyramid.exceptions import BadCSRFToken
0c29cf 1509
10ddb6 1510         def inner_view(request):  # pragma: no cover
0c29cf 1511             pass
MM 1512
21d5be 1513         request = self._makeRequest()
65dee6 1514         request.scheme = "http"
21d5be 1515         request.method = 'PUT'
DS 1516         request.session = DummySession({'csrf_token': 'foo'})
1517         request.headers = {'X-CSRF-Token': 'bar'}
de3d0c 1518         view = self.config._derive_view(inner_view, require_csrf=True)
21d5be 1519         self.assertRaises(BadCSRFToken, lambda: view(None, request))
DS 1520
65dee6 1521     def test_csrf_view_fails_on_bad_referrer(self):
DS 1522         from pyramid.exceptions import BadCSRFOrigin
0c29cf 1523
10ddb6 1524         def inner_view(request):  # pragma: no cover
0c29cf 1525             pass
MM 1526
65dee6 1527         request = self._makeRequest()
DS 1528         request.method = "POST"
1529         request.scheme = "https"
884043 1530         request.host_port = "443"
65dee6 1531         request.domain = "example.com"
DS 1532         request.referrer = "https://not-example.com/evil/"
1533         request.registry.settings = {}
de3d0c 1534         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1535         self.assertRaises(BadCSRFOrigin, lambda: view(None, request))
DS 1536
1537     def test_csrf_view_fails_on_bad_origin(self):
1538         from pyramid.exceptions import BadCSRFOrigin
0c29cf 1539
10ddb6 1540         def inner_view(request):  # pragma: no cover
0c29cf 1541             pass
MM 1542
65dee6 1543         request = self._makeRequest()
DS 1544         request.method = "POST"
1545         request.scheme = "https"
884043 1546         request.host_port = "443"
65dee6 1547         request.domain = "example.com"
DS 1548         request.headers = {"Origin": "https://not-example.com/evil/"}
1549         request.registry.settings = {}
de3d0c 1550         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1551         self.assertRaises(BadCSRFOrigin, lambda: view(None, request))
DS 1552
de3d0c 1553     def test_csrf_view_enabled_by_default(self):
MM 1554         from pyramid.exceptions import BadCSRFToken
0c29cf 1555
10ddb6 1556         def inner_view(request):  # pragma: no cover
0c29cf 1557             pass
MM 1558
6b35eb 1559         request = self._makeRequest()
65dee6 1560         request.scheme = "http"
6b35eb 1561         request.method = 'POST'
MM 1562         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1563         self.config.set_default_csrf_options(require_csrf=True)
6b35eb 1564         view = self.config._derive_view(inner_view)
de3d0c 1565         self.assertRaises(BadCSRFToken, lambda: view(None, request))
6b35eb 1566
17fa5e 1567     def test_csrf_view_enabled_via_callback(self):
MM 1568         def callback(request):
1569             return True
0c29cf 1570
17fa5e 1571         from pyramid.exceptions import BadCSRFToken
0c29cf 1572
10ddb6 1573         def inner_view(request):  # pragma: no cover
0c29cf 1574             pass
MM 1575
17fa5e 1576         request = self._makeRequest()
MM 1577         request.scheme = "http"
1578         request.method = 'POST'
1579         request.session = DummySession({'csrf_token': 'foo'})
0c29cf 1580         self.config.set_default_csrf_options(
MM 1581             require_csrf=True, callback=callback
1582         )
17fa5e 1583         view = self.config._derive_view(inner_view)
MM 1584         self.assertRaises(BadCSRFToken, lambda: view(None, request))
1585
1586     def test_csrf_view_disabled_via_callback(self):
1587         def callback(request):
1588             return False
0c29cf 1589
17fa5e 1590         response = DummyResponse()
0c29cf 1591
17fa5e 1592         def inner_view(request):
MM 1593             return response
0c29cf 1594
17fa5e 1595         request = self._makeRequest()
MM 1596         request.scheme = "http"
1597         request.method = 'POST'
1598         request.session = DummySession({'csrf_token': 'foo'})
0c29cf 1599         self.config.set_default_csrf_options(
MM 1600             require_csrf=True, callback=callback
1601         )
17fa5e 1602         view = self.config._derive_view(inner_view)
MM 1603         result = view(None, request)
1604         self.assertTrue(result is response)
1605
de3d0c 1606     def test_csrf_view_uses_custom_csrf_token(self):
6b35eb 1607         response = DummyResponse()
0c29cf 1608
6b35eb 1609         def inner_view(request):
MM 1610             return response
0c29cf 1611
6b35eb 1612         request = self._makeRequest()
65dee6 1613         request.scheme = "http"
6b35eb 1614         request.method = 'POST'
MM 1615         request.session = DummySession({'csrf_token': 'foo'})
f12005 1616         request.POST = {'DUMMY': 'foo'}
de3d0c 1617         self.config.set_default_csrf_options(require_csrf=True, token='DUMMY')
6b35eb 1618         view = self.config._derive_view(inner_view)
MM 1619         result = view(None, request)
1620         self.assertTrue(result is response)
1621
de3d0c 1622     def test_csrf_view_uses_custom_csrf_header(self):
6b35eb 1623         response = DummyResponse()
0c29cf 1624
6b35eb 1625         def inner_view(request):
MM 1626             return response
0c29cf 1627
6b35eb 1628         request = self._makeRequest()
de3d0c 1629         request.scheme = "http"
6b35eb 1630         request.method = 'POST'
MM 1631         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1632         request.headers = {'DUMMY': 'foo'}
MM 1633         self.config.set_default_csrf_options(require_csrf=True, header='DUMMY')
1634         view = self.config._derive_view(inner_view)
1635         result = view(None, request)
1636         self.assertTrue(result is response)
1637
1638     def test_csrf_view_uses_custom_methods(self):
1639         response = DummyResponse()
0c29cf 1640
de3d0c 1641         def inner_view(request):
MM 1642             return response
0c29cf 1643
de3d0c 1644         request = self._makeRequest()
MM 1645         request.scheme = "http"
1646         request.method = 'PUT'
1647         request.session = DummySession({'csrf_token': 'foo'})
1648         self.config.set_default_csrf_options(
0c29cf 1649             require_csrf=True, safe_methods=['PUT']
MM 1650         )
6b35eb 1651         view = self.config._derive_view(inner_view)
MM 1652         result = view(None, request)
1653         self.assertTrue(result is response)
1654
1655     def test_csrf_view_uses_view_option_override(self):
1656         response = DummyResponse()
0c29cf 1657
6b35eb 1658         def inner_view(request):
MM 1659             return response
0c29cf 1660
6b35eb 1661         request = self._makeRequest()
65dee6 1662         request.scheme = "http"
6b35eb 1663         request.method = 'POST'
MM 1664         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1665         request.POST = {'csrf_token': 'bar'}
MM 1666         self.config.set_default_csrf_options(require_csrf=True)
1667         view = self.config._derive_view(inner_view, require_csrf=False)
9e9fa9 1668         result = view(None, request)
MM 1669         self.assertTrue(result is response)
1670
6f524a 1671     def test_csrf_view_skipped_by_default_on_exception_view(self):
MM 1672         from pyramid.request import Request
0c29cf 1673
6f524a 1674         def view(request):
MM 1675             raise ValueError
0c29cf 1676
6f524a 1677         def excview(request):
MM 1678             return 'hello'
0c29cf 1679
de3d0c 1680         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1681         self.config.set_session_factory(
0c29cf 1682             lambda request: DummySession({'csrf_token': 'foo'})
MM 1683         )
6f524a 1684         self.config.add_view(view, name='foo', require_csrf=False)
MM 1685         self.config.add_view(excview, context=ValueError, renderer='string')
1686         app = self.config.make_wsgi_app()
1687         request = Request.blank('/foo', base_url='http://example.com')
1688         request.method = 'POST'
1689         response = request.get_response(app)
1690         self.assertTrue(b'hello' in response.body)
1691
1692     def test_csrf_view_failed_on_explicit_exception_view(self):
1693         from pyramid.exceptions import BadCSRFToken
1694         from pyramid.request import Request
0c29cf 1695
6f524a 1696         def view(request):
MM 1697             raise ValueError
0c29cf 1698
10ddb6 1699         def excview(request):  # pragma: no cover
0c29cf 1700             pass
MM 1701
de3d0c 1702         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1703         self.config.set_session_factory(
0c29cf 1704             lambda request: DummySession({'csrf_token': 'foo'})
MM 1705         )
6f524a 1706         self.config.add_view(view, name='foo', require_csrf=False)
0c29cf 1707         self.config.add_view(
MM 1708             excview, context=ValueError, renderer='string', require_csrf=True
1709         )
6f524a 1710         app = self.config.make_wsgi_app()
MM 1711         request = Request.blank('/foo', base_url='http://example.com')
1712         request.method = 'POST'
1713         try:
1714             request.get_response(app)
1715         except BadCSRFToken:
1716             pass
0c29cf 1717         else:  # pragma: no cover
6f524a 1718             raise AssertionError
MM 1719
1720     def test_csrf_view_passed_on_explicit_exception_view(self):
1721         from pyramid.request import Request
0c29cf 1722
6f524a 1723         def view(request):
MM 1724             raise ValueError
0c29cf 1725
6f524a 1726         def excview(request):
MM 1727             return 'hello'
0c29cf 1728
de3d0c 1729         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1730         self.config.set_session_factory(
0c29cf 1731             lambda request: DummySession({'csrf_token': 'foo'})
MM 1732         )
6f524a 1733         self.config.add_view(view, name='foo', require_csrf=False)
0c29cf 1734         self.config.add_view(
MM 1735             excview, context=ValueError, renderer='string', require_csrf=True
1736         )
6f524a 1737         app = self.config.make_wsgi_app()
MM 1738         request = Request.blank('/foo', base_url='http://example.com')
1739         request.method = 'POST'
1740         request.headers['X-CSRF-Token'] = 'foo'
1741         response = request.get_response(app)
1742         self.assertTrue(b'hello' in response.body)
1743
07d4a4 1744
d11119 1745 class TestDerivationOrder(unittest.TestCase):
BJR 1746     def setUp(self):
1747         self.config = testing.setUp()
1748
1749     def tearDown(self):
1750         self.config = None
1751         testing.tearDown()
1752
1753     def test_right_order_user_sorted(self):
1754         from pyramid.interfaces import IViewDerivers
1755
cbf686 1756         self.config.add_view_deriver(None, 'deriv1')
0c29cf 1757         self.config.add_view_deriver(
MM 1758             None, 'deriv2', 'decorated_view', 'deriv1'
1759         )
a3db3c 1760         self.config.add_view_deriver(None, 'deriv3', 'deriv2', 'deriv1')
d11119 1761
46fd86 1762         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1763         derivers_sorted = derivers.sorted()
BJR 1764         dlist = [d for (d, _) in derivers_sorted]
0c29cf 1765         self.assertEqual(
MM 1766             [
1767                 'secured_view',
1768                 'csrf_view',
1769                 'owrapped_view',
1770                 'http_cached_view',
1771                 'decorated_view',
1772                 'deriv2',
1773                 'deriv3',
1774                 'deriv1',
1775                 'rendered_view',
1776                 'mapped_view',
1777             ],
1778             dlist,
1779         )
d11119 1780
BJR 1781     def test_right_order_implicit(self):
1782         from pyramid.interfaces import IViewDerivers
1783
cbf686 1784         self.config.add_view_deriver(None, 'deriv1')
MM 1785         self.config.add_view_deriver(None, 'deriv2')
1786         self.config.add_view_deriver(None, 'deriv3')
d11119 1787
46fd86 1788         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1789         derivers_sorted = derivers.sorted()
BJR 1790         dlist = [d for (d, _) in derivers_sorted]
0c29cf 1791         self.assertEqual(
MM 1792             [
1793                 'secured_view',
1794                 'csrf_view',
1795                 'owrapped_view',
1796                 'http_cached_view',
1797                 'decorated_view',
1798                 'deriv3',
1799                 'deriv2',
1800                 'deriv1',
1801                 'rendered_view',
1802                 'mapped_view',
1803             ],
1804             dlist,
1805         )
d11119 1806
cf9dcb 1807     def test_right_order_under_rendered_view(self):
d11119 1808         from pyramid.interfaces import IViewDerivers
BJR 1809
0c29cf 1810         self.config.add_view_deriver(
MM 1811             None, 'deriv1', 'rendered_view', 'mapped_view'
1812         )
d11119 1813
46fd86 1814         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1815         derivers_sorted = derivers.sorted()
BJR 1816         dlist = [d for (d, _) in derivers_sorted]
0c29cf 1817         self.assertEqual(
MM 1818             [
1819                 'secured_view',
1820                 'csrf_view',
1821                 'owrapped_view',
1822                 'http_cached_view',
1823                 'decorated_view',
1824                 'rendered_view',
1825                 'deriv1',
1826                 'mapped_view',
1827             ],
1828             dlist,
1829         )
d11119 1830
cf9dcb 1831     def test_right_order_under_rendered_view_others(self):
d11119 1832         from pyramid.interfaces import IViewDerivers
BJR 1833
0c29cf 1834         self.config.add_view_deriver(
MM 1835             None, 'deriv1', 'rendered_view', 'mapped_view'
1836         )
cbf686 1837         self.config.add_view_deriver(None, 'deriv2')
MM 1838         self.config.add_view_deriver(None, 'deriv3')
d11119 1839
46fd86 1840         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1841         derivers_sorted = derivers.sorted()
BJR 1842         dlist = [d for (d, _) in derivers_sorted]
0c29cf 1843         self.assertEqual(
MM 1844             [
1845                 'secured_view',
1846                 'csrf_view',
1847                 'owrapped_view',
1848                 'http_cached_view',
1849                 'decorated_view',
1850                 'deriv3',
1851                 'deriv2',
1852                 'rendered_view',
1853                 'deriv1',
1854                 'mapped_view',
1855             ],
1856             dlist,
1857         )
d11119 1858
BJR 1859
ceb1f2 1860 class TestAddDeriver(unittest.TestCase):
07d4a4 1861     def setUp(self):
CD 1862         self.config = testing.setUp()
1863
1864     def tearDown(self):
1865         self.config = None
174bb5 1866         testing.tearDown()
07d4a4 1867
ceb1f2 1868     def test_add_single_deriver(self):
07d4a4 1869         response = DummyResponse()
CD 1870         response.deriv = False
1871         view = lambda *arg: response
1872
007600 1873         def deriv(view, info):
07d4a4 1874             self.assertFalse(response.deriv)
CD 1875             response.deriv = True
1876             return view
1877
1878         result = self.config._derive_view(view)
1879         self.assertFalse(response.deriv)
cbf686 1880         self.config.add_view_deriver(deriv, 'test_deriv')
07d4a4 1881
10ddb6 1882         result = self.config._derive_view(view)  # noqa: F841
07d4a4 1883         self.assertTrue(response.deriv)
CD 1884
ceb1f2 1885     def test_override_deriver(self):
07d4a4 1886         flags = {}
CD 1887
1888         class AView:
1889             def __init__(self):
1890                 self.response = DummyResponse()
1891
a3db3c 1892         def deriv1(view, info):
07d4a4 1893             flags['deriv1'] = True
CD 1894             return view
1895
a3db3c 1896         def deriv2(view, info):
07d4a4 1897             flags['deriv2'] = True
CD 1898             return view
1899
1900         view1 = AView()
cbf686 1901         self.config.add_view_deriver(deriv1, 'test_deriv')
07d4a4 1902         result = self.config._derive_view(view1)
CD 1903         self.assertTrue(flags.get('deriv1'))
1904         self.assertFalse(flags.get('deriv2'))
1905
1906         flags.clear()
1907         view2 = AView()
cbf686 1908         self.config.add_view_deriver(deriv2, 'test_deriv')
10ddb6 1909         result = self.config._derive_view(view2)  # noqa: F841
07d4a4 1910         self.assertFalse(flags.get('deriv1'))
CD 1911         self.assertTrue(flags.get('deriv2'))
1912
c231d8 1913     def test_override_mapped_view(self):
MM 1914         from pyramid.viewderivers import VIEW
0c29cf 1915
c231d8 1916         response = DummyResponse()
MM 1917         view = lambda *arg: response
1918         flags = {}
1919
1920         def deriv1(view, info):
1921             flags['deriv1'] = True
1922             return view
1923
1924         result = self.config._derive_view(view)
1925         self.assertFalse(flags.get('deriv1'))
1926
1927         flags.clear()
1928         self.config.add_view_deriver(
0c29cf 1929             deriv1, name='mapped_view', under='rendered_view', over=VIEW
MM 1930         )
10ddb6 1931         result = self.config._derive_view(view)  # noqa: F841
c231d8 1932         self.assertTrue(flags.get('deriv1'))
MM 1933
ceb1f2 1934     def test_add_multi_derivers_ordered(self):
a3db3c 1935         from pyramid.viewderivers import INGRESS
0c29cf 1936
07d4a4 1937         response = DummyResponse()
CD 1938         view = lambda *arg: response
1939         response.deriv = []
1940
a3db3c 1941         def deriv1(view, info):
07d4a4 1942             response.deriv.append('deriv1')
CD 1943             return view
1944
a3db3c 1945         def deriv2(view, info):
07d4a4 1946             response.deriv.append('deriv2')
CD 1947             return view
1948
a3db3c 1949         def deriv3(view, info):
07d4a4 1950             response.deriv.append('deriv3')
CD 1951             return view
1952
cbf686 1953         self.config.add_view_deriver(deriv1, 'deriv1')
a3db3c 1954         self.config.add_view_deriver(deriv2, 'deriv2', INGRESS, 'deriv1')
MM 1955         self.config.add_view_deriver(deriv3, 'deriv3', 'deriv2', 'deriv1')
10ddb6 1956         result = self.config._derive_view(view)  # noqa: F841
a3db3c 1957         self.assertEqual(response.deriv, ['deriv1', 'deriv3', 'deriv2'])
MM 1958
1959     def test_add_deriver_without_name(self):
1960         from pyramid.interfaces import IViewDerivers
0c29cf 1961
10ddb6 1962         def deriv1(view, info):  # pragma: no cover
0c29cf 1963             pass
MM 1964
a3db3c 1965         self.config.add_view_deriver(deriv1)
MM 1966         derivers = self.config.registry.getUtility(IViewDerivers)
1967         self.assertTrue('deriv1' in derivers.names)
1968
1969     def test_add_deriver_reserves_ingress(self):
1970         from pyramid.exceptions import ConfigurationError
1971         from pyramid.viewderivers import INGRESS
0c29cf 1972
10ddb6 1973         def deriv1(view, info):  # pragma: no cover
0c29cf 1974             pass
MM 1975
a3db3c 1976         self.assertRaises(
0c29cf 1977             ConfigurationError, self.config.add_view_deriver, deriv1, INGRESS
MM 1978         )
a3db3c 1979
MM 1980     def test_add_deriver_enforces_ingress_is_first(self):
1981         from pyramid.exceptions import ConfigurationError
1982         from pyramid.viewderivers import INGRESS
0c29cf 1983
10ddb6 1984         def deriv1(view, info):  # pragma: no cover
0c29cf 1985             pass
MM 1986
a3db3c 1987         try:
c231d8 1988             self.config.add_view_deriver(deriv1, over=INGRESS)
a3db3c 1989         except ConfigurationError as ex:
c231d8 1990             self.assertTrue('cannot be over INGRESS' in ex.args[0])
0c29cf 1991         else:  # pragma: no cover
c231d8 1992             raise AssertionError
MM 1993
1994     def test_add_deriver_enforces_view_is_last(self):
1995         from pyramid.exceptions import ConfigurationError
1996         from pyramid.viewderivers import VIEW
0c29cf 1997
10ddb6 1998         def deriv1(view, info):  # pragma: no cover
0c29cf 1999             pass
MM 2000
c231d8 2001         try:
MM 2002             self.config.add_view_deriver(deriv1, under=VIEW)
2003         except ConfigurationError as ex:
2004             self.assertTrue('cannot be under VIEW' in ex.args[0])
0c29cf 2005         else:  # pragma: no cover
a3db3c 2006             raise AssertionError
MM 2007
2008     def test_add_deriver_enforces_mapped_view_is_last(self):
2009         from pyramid.exceptions import ConfigurationError
0c29cf 2010
10ddb6 2011         def deriv1(view, info):  # pragma: no cover
0c29cf 2012             pass
MM 2013
a3db3c 2014         try:
c231d8 2015             self.config.add_view_deriver(deriv1, 'deriv1', under='mapped_view')
a3db3c 2016         except ConfigurationError as ex:
c231d8 2017             self.assertTrue('cannot be under "mapped_view"' in ex.args[0])
0c29cf 2018         else:  # pragma: no cover
a3db3c 2019             raise AssertionError
cbf686 2020
d11119 2021
ceb1f2 2022 class TestDeriverIntegration(unittest.TestCase):
174bb5 2023     def setUp(self):
BJR 2024         self.config = testing.setUp()
2025
2026     def tearDown(self):
2027         self.config = None
2028         testing.tearDown()
2029
0c29cf 2030     def _getViewCallable(
MM 2031         self, config, ctx_iface=None, request_iface=None, name=''
2032     ):
174bb5 2033         from zope.interface import Interface
BJR 2034         from pyramid.interfaces import IRequest
2035         from pyramid.interfaces import IView
2036         from pyramid.interfaces import IViewClassifier
0c29cf 2037
174bb5 2038         classifier = IViewClassifier
BJR 2039         if ctx_iface is None:
2040             ctx_iface = Interface
2041         if request_iface is None:
2042             request_iface = IRequest
2043         return config.registry.adapters.lookup(
0c29cf 2044             (classifier, request_iface, ctx_iface),
MM 2045             IView,
2046             name=name,
2047             default=None,
2048         )
174bb5 2049
BJR 2050     def _makeRequest(self, config):
2051         request = DummyRequest()
2052         request.registry = config.registry
2053         return request
2054
2055     def test_view_options(self):
2056         response = DummyResponse()
2057         view = lambda *arg: response
2058         response.deriv = []
2059
007600 2060         def deriv1(view, info):
MM 2061             response.deriv.append(info.options['deriv1'])
174bb5 2062             return view
0c29cf 2063
e4b931 2064         deriv1.options = ('deriv1',)
174bb5 2065
007600 2066         def deriv2(view, info):
MM 2067             response.deriv.append(info.options['deriv2'])
174bb5 2068             return view
0c29cf 2069
e4b931 2070         deriv2.options = ('deriv2',)
174bb5 2071
cbf686 2072         self.config.add_view_deriver(deriv1, 'deriv1')
MM 2073         self.config.add_view_deriver(deriv2, 'deriv2')
174bb5 2074         self.config.add_view(view, deriv1='test1', deriv2='test2')
BJR 2075
2076         wrapper = self._getViewCallable(self.config)
2077         request = self._makeRequest(self.config)
2078         request.method = 'GET'
2079         self.assertEqual(wrapper(None, request), response)
2080         self.assertEqual(['test1', 'test2'], response.deriv)
6b089d 2081
e4b931 2082     def test_unexpected_view_options(self):
MM 2083         from pyramid.exceptions import ConfigurationError
0c29cf 2084
10ddb6 2085         def deriv1(view, info):  # pragma: no cover
0c29cf 2086             pass
MM 2087
cbf686 2088         self.config.add_view_deriver(deriv1, 'deriv1')
e4b931 2089         self.assertRaises(
MM 2090             ConfigurationError,
0c29cf 2091             lambda: self.config.add_view(lambda r: {}, deriv1='test1'),
MM 2092         )
2093
e4b931 2094
464973 2095 @implementer(IResponse)
CD 2096 class DummyResponse(object):
2097     content_type = None
2098     default_content_type = None
2099     body = None
2100
0c29cf 2101
464973 2102 class DummyRequest:
CD 2103     subpath = ()
2104     matchdict = None
0c29cf 2105     request_iface = IRequest
464973 2106
CD 2107     def __init__(self, environ=None):
2108         if environ is None:
2109             environ = {}
2110         self.environ = environ
2111         self.params = {}
de3d0c 2112         self.POST = {}
464973 2113         self.cookies = {}
9e9fa9 2114         self.headers = {}
464973 2115         self.response = DummyResponse()
CD 2116
0c29cf 2117
464973 2118 class DummyLogger:
CD 2119     def __init__(self):
2120         self.messages = []
0c29cf 2121
464973 2122     def info(self, msg):
CD 2123         self.messages.append(msg)
0c29cf 2124
464973 2125     warn = info
CD 2126     debug = info
0c29cf 2127
464973 2128
CD 2129 class DummySecurityPolicy:
2130     def __init__(self, permitted=True):
2131         self.permitted = permitted
2132
2133     def effective_principals(self, request):
2134         return []
2135
2136     def permits(self, context, principals, permission):
2137         return self.permitted
2138
0c29cf 2139
9e9fa9 2140 class DummySession(dict):
MM 2141     def get_csrf_token(self):
2142         return self['csrf_token']
2143
0c29cf 2144
611aa3 2145 def parse_httpdate(s):
CD 2146     import datetime
0c29cf 2147
611aa3 2148     # cannot use %Z, must use literal GMT; Jython honors timezone
CD 2149     # but CPython does not
2150     return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT")
0c29cf 2151
611aa3 2152
CD 2153 def assert_similar_datetime(one, two):
2154     for attr in ('year', 'month', 'day', 'hour', 'minute'):
2155         one_attr = getattr(one, attr)
2156         two_attr = getattr(two, attr)
0c29cf 2157         if not one_attr == two_attr:  # pragma: no cover
611aa3 2158             raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr))