-
Notifications
You must be signed in to change notification settings - Fork 20
/
test_requestmethod.py
136 lines (113 loc) · 5.28 KB
/
test_requestmethod.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#############################################################################
#
# Copyright (c) 2007 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from AccessControl.requestmethod import requestmethod
from AccessControl.requestmethod import getfullargspec
from AccessControl.requestmethod import buildfacade
from zope.interface import implementer
from zope.publisher.interfaces.browser import IBrowserRequest
import unittest
import zExceptions
@implementer(IBrowserRequest)
class DummyRequest:
def __init__(self, method):
self.method = method
GET = DummyRequest('GET')
POST = DummyRequest('POST')
class RequestMethodDecoratorsTests(unittest.TestCase):
"""Using request method decorators, you can limit functions or methods to
only be callable when the HTTP request was made using a particular method.
"""
def test_requires_the_defined_HTTP_mehtod(self):
# To limit access to a function or method to POST requests, use the
# requestmethod decorator factory:
@requestmethod('POST')
def foo(bar, REQUEST):
return bar
# When this method is accessed through a request that does not use
# POST, the Forbidden exception will be raised:
with self.assertRaises(zExceptions.Forbidden) as err:
foo('spam', GET)
self.assertEqual('Request must be POST', str(err.exception))
# Only when the request was made using POST, will the call succeed:
self.assertEqual('spam', foo('spam', POST))
def test_it_does_not_matter_if_REQUEST_is_positional_or_keyword_arg(self):
# It doesn't matter if REQUEST is a positional or a keyword parameter:
@requestmethod('POST')
def foo(bar, REQUEST=None):
return bar
with self.assertRaises(zExceptions.Forbidden) as err:
foo('spam', GET)
self.assertEqual('Request must be POST', str(err.exception))
# *Not* passing an optional REQUEST always succeeds::
self.assertEqual('spam', foo('spam', POST))
def test_REQUEST_parameter_is_a_requirement(self):
# Note that the REQUEST parameter is a requirement for the decorator to
# operate, not including it in the callable signature results in an
# error:
try:
@requestmethod('POST')
def foo(bar):
return bar
except ValueError as e:
self.assertEqual(
'No REQUEST parameter in callable signature', str(e))
else:
self.fail('Did not raise')
def test_preserves_keyword_parameter_defaults(self):
# Because the Zope Publisher uses introspection to match REQUEST
# variables against callable signatures, the result of the decorator
# must match the original closely, and keyword parameter defaults must
# be preserved:
mutabledefault = dict()
@requestmethod('POST')
def foo(bar, baz=mutabledefault, egg=mutabledefault, REQUEST=None,
*args):
return bar, baz is mutabledefault, egg is None, REQUEST
self.assertEqual((['bar', 'baz', 'egg', 'REQUEST'], 'args', None),
getfullargspec(foo)[:3])
self.assertEqual(('spam', True, True, None), foo('spam', egg=None))
with self.assertRaises(TypeError) as err:
foo(monty='python')
self.assertEqual("foo() got an unexpected keyword argument 'monty'",
str(err.exception))
def test_can_be_used_for_any_request_method(self):
# The `requestmethod` decorator factory can be used for any request
# method, simply pass in the desired request method:
@requestmethod('PUT')
def foo(bar, REQUEST=None):
return bar
with self.assertRaises(zExceptions.Forbidden) as err:
foo('spam', GET)
self.assertEqual('Request must be PUT', str(err.exception))
def test_allows_multiple_request_methods(self):
# You can pass in multiple request methods allow access by any of them:
@requestmethod('GET', 'HEAD', 'PROPFIND')
def foo(bar, REQUEST=None):
return bar
self.assertEqual('spam', foo('spam', GET))
with self.assertRaises(zExceptions.Forbidden) as err:
foo('spam', POST)
self.assertEqual('Request must be GET, HEAD or PROPFIND',
str(err.exception))
def test_facade_render_correct_args_kwargs(self):
""" s. https://github.com/zopefoundation/AccessControl/issues/69
"""
def foo(bar, baz, *args, **kwargs):
"""foo doc"""
return baz
got = buildfacade('foo', foo, foo.__doc__)
expected = '\n'.join([
'def foo(bar, baz, *args, **kwargs):',
' """foo doc"""',
' return _curried(bar, baz, *args, **kwargs)'])
self.assertEqual(expected, got)