fresco 3.3.1__py3-none-any.whl → 3.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fresco might be problematic. Click here for more details.
- fresco/__init__.py +1 -1
- fresco/request.py +2 -2
- fresco/response.py +1 -1
- fresco/routing.py +1 -0
- fresco/tests/__init__.py +0 -0
- fresco/tests/fixtures.py +67 -0
- fresco/tests/test_cookie.py +59 -0
- fresco/tests/test_core.py +1038 -0
- fresco/tests/test_decorators.py +40 -0
- fresco/tests/test_exceptions.py +30 -0
- fresco/tests/test_middleware.py +92 -0
- fresco/tests/test_multidict.py +234 -0
- fresco/tests/test_options.py +314 -0
- fresco/tests/test_request.py +448 -0
- fresco/tests/test_requestcontext.py +107 -0
- fresco/tests/test_response.py +224 -0
- fresco/tests/test_routeargs.py +223 -0
- fresco/tests/test_routing.py +1126 -0
- fresco/tests/test_static.py +124 -0
- fresco/tests/test_subrequests.py +236 -0
- fresco/tests/util/__init__.py +0 -0
- fresco/tests/util/form_data.py +79 -0
- fresco/tests/util/test_common.py +34 -0
- fresco/tests/util/test_http.py +323 -0
- fresco/tests/util/test_security.py +34 -0
- fresco/tests/util/test_urls.py +176 -0
- fresco/tests/util/test_wsgi.py +107 -0
- fresco/util/contentencodings.py +2 -1
- fresco/util/http.py +3 -1
- fresco/util/wsgi.py +1 -1
- {fresco-3.3.1.dist-info → fresco-3.3.3.dist-info}/METADATA +5 -6
- fresco-3.3.3.dist-info/RECORD +57 -0
- {fresco-3.3.1.dist-info → fresco-3.3.3.dist-info}/WHEEL +1 -1
- fresco-3.3.1.dist-info/RECORD +0 -34
- {fresco-3.3.1.dist-info → fresco-3.3.3.dist-info}/LICENSE.txt +0 -0
- {fresco-3.3.1.dist-info → fresco-3.3.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Copyright 2015 Oliver Cope
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
import sys
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
from calendar import timegm
|
|
19
|
+
from email.utils import formatdate, parsedate
|
|
20
|
+
from unittest.mock import Mock
|
|
21
|
+
from tempfile import mkstemp
|
|
22
|
+
from fresco import FrescoApp
|
|
23
|
+
from fresco.static import serve_static_file
|
|
24
|
+
from fresco.util.wsgi import ClosingIterator
|
|
25
|
+
from fresco.util.wsgi import apply_request
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestServeStaticFile:
|
|
29
|
+
def setup_method(self):
|
|
30
|
+
fh, tmpname = mkstemp()
|
|
31
|
+
self.tmpname = tmpname
|
|
32
|
+
self.mtime = int(os.path.getmtime(self.tmpname))
|
|
33
|
+
os.close(fh)
|
|
34
|
+
|
|
35
|
+
def teardown_method(self):
|
|
36
|
+
os.unlink(self.tmpname)
|
|
37
|
+
|
|
38
|
+
def parse_lmh(self, last_modified):
|
|
39
|
+
return timegm(parsedate(last_modified)) # type: ignore
|
|
40
|
+
|
|
41
|
+
def make_ims(self, since, tz=0):
|
|
42
|
+
return {"HTTP_IF_MODIFIED_SINCE": formatdate(since, tz)}
|
|
43
|
+
|
|
44
|
+
def test_sets_last_modified_header(self):
|
|
45
|
+
with FrescoApp().requestcontext():
|
|
46
|
+
response = serve_static_file(self.tmpname)
|
|
47
|
+
response.content_iterator.close()
|
|
48
|
+
|
|
49
|
+
last_modified = response.get_header("Last-Modified")
|
|
50
|
+
assert self.parse_lmh(last_modified) == self.mtime
|
|
51
|
+
|
|
52
|
+
def test_full_response_when_if_modified_before_mtime(self):
|
|
53
|
+
with FrescoApp().requestcontext(**self.make_ims(0)):
|
|
54
|
+
response = serve_static_file(self.tmpname)
|
|
55
|
+
response.content_iterator.close()
|
|
56
|
+
|
|
57
|
+
last_modified = response.get_header("Last-Modified")
|
|
58
|
+
assert response.status_code == 200
|
|
59
|
+
assert self.parse_lmh(last_modified) == self.mtime
|
|
60
|
+
|
|
61
|
+
def test_304_when_if_modified_after_mtime(self):
|
|
62
|
+
with FrescoApp().requestcontext(**self.make_ims(self.mtime + 1)):
|
|
63
|
+
response = serve_static_file(self.tmpname)
|
|
64
|
+
assert response.status_code == 304
|
|
65
|
+
|
|
66
|
+
def test_304_when_if_modified_after_mtime_with_tz1(self):
|
|
67
|
+
with FrescoApp().requestcontext(**self.make_ims(self.mtime + 1, +1)):
|
|
68
|
+
response = serve_static_file(self.tmpname)
|
|
69
|
+
assert response.status_code == 304
|
|
70
|
+
|
|
71
|
+
def test_304_when_if_modified_after_mtime_with_tz2(self):
|
|
72
|
+
with FrescoApp().requestcontext(**self.make_ims(self.mtime + 1, -1)):
|
|
73
|
+
response = serve_static_file(self.tmpname)
|
|
74
|
+
assert response.status_code == 304
|
|
75
|
+
|
|
76
|
+
def test_it_doesnt_fail_with_an_out_of_range_modified_since_value1(self):
|
|
77
|
+
year = sys.maxsize
|
|
78
|
+
with FrescoApp().requestcontext(
|
|
79
|
+
HTTP_IF_MODIFIED_SINCE='"Wed, 09 Feb %s 04:46:40 GMT"' % year
|
|
80
|
+
):
|
|
81
|
+
response = serve_static_file(self.tmpname)
|
|
82
|
+
assert response.status_code == 400
|
|
83
|
+
|
|
84
|
+
def test_it_doesnt_fail_with_an_out_of_range_modified_since_value2(self):
|
|
85
|
+
with FrescoApp().requestcontext(
|
|
86
|
+
HTTP_IF_MODIFIED_SINCE='"Wed, 01 Feb -2001 04:46:40 GMT"'
|
|
87
|
+
):
|
|
88
|
+
response = serve_static_file(self.tmpname)
|
|
89
|
+
assert response.status_code == 400
|
|
90
|
+
|
|
91
|
+
def test_last_modified_format_is_correct(self):
|
|
92
|
+
with FrescoApp().requestcontext():
|
|
93
|
+
response = serve_static_file(self.tmpname)
|
|
94
|
+
response.content_iterator.close()
|
|
95
|
+
|
|
96
|
+
assert re.match(
|
|
97
|
+
r"^\w{3}, \d{1,2} \w{3} \d{4} \d\d:\d\d:\d\d GMT",
|
|
98
|
+
response.get_header("last-modified"),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def test_filewrapper_used_if_present(self):
|
|
102
|
+
with FrescoApp().requestcontext() as c:
|
|
103
|
+
|
|
104
|
+
def closefile():
|
|
105
|
+
file_wrapper.call_args.args[0].close()
|
|
106
|
+
|
|
107
|
+
file_wrapper = Mock()
|
|
108
|
+
file_wrapper.return_value = ClosingIterator([], closefile)
|
|
109
|
+
c.request.environ["wsgi.file_wrapper"] = file_wrapper
|
|
110
|
+
response = serve_static_file(self.tmpname)
|
|
111
|
+
apply_request(c.request, response)
|
|
112
|
+
|
|
113
|
+
assert file_wrapper.call_count == 1
|
|
114
|
+
|
|
115
|
+
def test_it_returns_not_found(self):
|
|
116
|
+
with FrescoApp().requestcontext():
|
|
117
|
+
response = serve_static_file(self.tmpname + "x")
|
|
118
|
+
assert response.status_code == 404
|
|
119
|
+
|
|
120
|
+
def test_it_returns_forbidden(self):
|
|
121
|
+
with FrescoApp().requestcontext():
|
|
122
|
+
os.chmod(self.tmpname, 0)
|
|
123
|
+
response = serve_static_file(self.tmpname)
|
|
124
|
+
assert response.status_code == 403
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
from fresco.core import FrescoApp
|
|
3
|
+
from fresco.routing import GET
|
|
4
|
+
from fresco.routing import Route
|
|
5
|
+
from fresco.requestcontext import context
|
|
6
|
+
from fresco.response import Response
|
|
7
|
+
from fresco.routeargs import GetArg, routearg
|
|
8
|
+
from fresco.subrequests import subrequest
|
|
9
|
+
from fresco.subrequests import subrequest_bytes
|
|
10
|
+
from fresco.subrequests import subrequest_raw
|
|
11
|
+
from fresco.subrequests import subrequest_str
|
|
12
|
+
|
|
13
|
+
from unittest.mock import Mock
|
|
14
|
+
import typing as t
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestSubRequest(object):
|
|
19
|
+
def test_it_raises_exception_if_decoding_impossible(self):
|
|
20
|
+
def view_bytes():
|
|
21
|
+
return Response(b"caf\xe9", content_type="text/plain")
|
|
22
|
+
|
|
23
|
+
with FrescoApp().requestcontext():
|
|
24
|
+
with pytest.raises(ValueError):
|
|
25
|
+
subrequest(view_bytes)
|
|
26
|
+
|
|
27
|
+
def test_it_decodes_response_content(self):
|
|
28
|
+
def view_latin1():
|
|
29
|
+
return Response(
|
|
30
|
+
"café".encode("latin1"),
|
|
31
|
+
content_type="text/plain; charset=Latin-1",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def view_utf8():
|
|
35
|
+
return Response(
|
|
36
|
+
"café".encode("UTF-8"),
|
|
37
|
+
content_type="text/plain; charset=UTF-8",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def view_string():
|
|
41
|
+
return Response("café", content_type="text/plain; charset=UTF-8")
|
|
42
|
+
|
|
43
|
+
def view_list_of_strings():
|
|
44
|
+
return Response(["café"], content_type="text/plain; charset=UTF-8")
|
|
45
|
+
|
|
46
|
+
with FrescoApp().requestcontext():
|
|
47
|
+
assert subrequest(view_utf8) == "café"
|
|
48
|
+
assert subrequest(view_latin1) == "café"
|
|
49
|
+
assert subrequest(view_string) == "café"
|
|
50
|
+
assert subrequest(view_list_of_strings) == "café"
|
|
51
|
+
|
|
52
|
+
def test_it_returns_a_markup_string(self):
|
|
53
|
+
def view_html():
|
|
54
|
+
return Response("<html>", content_type="text/html; charset=UTF-8")
|
|
55
|
+
|
|
56
|
+
def view_text():
|
|
57
|
+
return Response("text", content_type="text/plain; charset=UTF-8")
|
|
58
|
+
|
|
59
|
+
with FrescoApp().requestcontext():
|
|
60
|
+
assert hasattr(subrequest(view_html), "__html__")
|
|
61
|
+
assert not hasattr(subrequest(view_text), "__html__")
|
|
62
|
+
|
|
63
|
+
def test_it_returns_raw_response(self):
|
|
64
|
+
r = Response("foo")
|
|
65
|
+
with FrescoApp().requestcontext():
|
|
66
|
+
assert subrequest_raw(lambda: r) is r
|
|
67
|
+
|
|
68
|
+
def test_it_returns_content_as_bytes(self):
|
|
69
|
+
def view():
|
|
70
|
+
return Response(
|
|
71
|
+
"café".encode("latin1"),
|
|
72
|
+
content_type="text/plain; charset=Latin-1",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
with FrescoApp().requestcontext():
|
|
76
|
+
assert subrequest_bytes(view) == b"caf\xe9"
|
|
77
|
+
|
|
78
|
+
def test_it_calls_response_onclose(self):
|
|
79
|
+
m = Mock()
|
|
80
|
+
r = Response(onclose=m)
|
|
81
|
+
with FrescoApp().requestcontext():
|
|
82
|
+
assert subrequest(lambda: r) == ""
|
|
83
|
+
assert m.call_count == 1
|
|
84
|
+
|
|
85
|
+
def test_viewspec_resolves(self):
|
|
86
|
+
def view():
|
|
87
|
+
return Response("foo")
|
|
88
|
+
|
|
89
|
+
app = FrescoApp()
|
|
90
|
+
app.route("/view", GET, view, name="named view")
|
|
91
|
+
with app.requestcontext():
|
|
92
|
+
assert subrequest("named view") == "foo"
|
|
93
|
+
|
|
94
|
+
def test_viewspec_can_be_a_path(self):
|
|
95
|
+
def view():
|
|
96
|
+
return Response("foo")
|
|
97
|
+
|
|
98
|
+
app = FrescoApp()
|
|
99
|
+
app.route("/view", GET, view, name="named view")
|
|
100
|
+
with app.requestcontext():
|
|
101
|
+
assert subrequest("/view") == "foo"
|
|
102
|
+
|
|
103
|
+
def test_viewspec_uses_routeargs(self):
|
|
104
|
+
def view(a, b):
|
|
105
|
+
return Response(["*" + a + b + "*"])
|
|
106
|
+
|
|
107
|
+
app = FrescoApp()
|
|
108
|
+
app.route("/view", GET, view, a=GetArg(), b=routearg(lambda r: r.method))
|
|
109
|
+
|
|
110
|
+
with app.requestcontext("/view?a=foo"):
|
|
111
|
+
assert subrequest(view, _resolve=True) == "*fooGET*"
|
|
112
|
+
assert subrequest(view, _resolve=True, a="bar", b="baz") == "*barbaz*"
|
|
113
|
+
|
|
114
|
+
def test_viewspec_resolves_dynamic(self):
|
|
115
|
+
class Views(object):
|
|
116
|
+
def __init__(self, a):
|
|
117
|
+
self.a = a
|
|
118
|
+
|
|
119
|
+
def view(self, b):
|
|
120
|
+
return Response("*" + self.a + b + "*")
|
|
121
|
+
|
|
122
|
+
__routes__ = [Route("/<b:str>", GET, "view", name="named view")]
|
|
123
|
+
|
|
124
|
+
app = FrescoApp()
|
|
125
|
+
app.delegate("/<a:str>", Views, dynamic=True, name="named collection")
|
|
126
|
+
|
|
127
|
+
with app.requestcontext():
|
|
128
|
+
assert (
|
|
129
|
+
subrequest("named collection:named view", a="foo", b="bar")
|
|
130
|
+
== "*foobar*"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def test_it_does_a_full_request(self):
|
|
134
|
+
request_hook = Mock(return_value=None)
|
|
135
|
+
view = Mock(return_value=Response())
|
|
136
|
+
|
|
137
|
+
app = FrescoApp()
|
|
138
|
+
app.route("/view", GET, view)
|
|
139
|
+
app.process_request(request_hook)
|
|
140
|
+
with app.requestcontext():
|
|
141
|
+
subrequest(view)
|
|
142
|
+
assert view.call_count == 1
|
|
143
|
+
assert request_hook.call_count == 0
|
|
144
|
+
|
|
145
|
+
subrequest(view, _full=True)
|
|
146
|
+
assert view.call_count == 2
|
|
147
|
+
assert request_hook.call_count == 1
|
|
148
|
+
|
|
149
|
+
def test_it_maintains_the_environ(self):
|
|
150
|
+
subreq_environ = None
|
|
151
|
+
|
|
152
|
+
def view():
|
|
153
|
+
nonlocal subreq_environ
|
|
154
|
+
subreq_environ = context.request.environ
|
|
155
|
+
return Response()
|
|
156
|
+
|
|
157
|
+
app = FrescoApp()
|
|
158
|
+
app.route("/view", GET, view)
|
|
159
|
+
|
|
160
|
+
with app.requestcontext("/xyzzy") as c:
|
|
161
|
+
original_environ = c.request.environ
|
|
162
|
+
subrequest(view)
|
|
163
|
+
assert subreq_environ is original_environ
|
|
164
|
+
|
|
165
|
+
def test_it_passes_original_environ_values(self):
|
|
166
|
+
subreq_environ = None
|
|
167
|
+
|
|
168
|
+
def view():
|
|
169
|
+
nonlocal subreq_environ
|
|
170
|
+
subreq_environ = context.request.environ
|
|
171
|
+
return Response()
|
|
172
|
+
|
|
173
|
+
app = FrescoApp()
|
|
174
|
+
app.route("/view", GET, view)
|
|
175
|
+
environ_copied = {
|
|
176
|
+
"HTTP_COOKIE": "1234",
|
|
177
|
+
"REMOTE_IP": "192.168.0.1",
|
|
178
|
+
"REMOTE_USER": "username",
|
|
179
|
+
}
|
|
180
|
+
environ_not_copied = {
|
|
181
|
+
"PATH_INFO": "/not-copied",
|
|
182
|
+
"QUERY_STRING": "not-copied",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
with app.requestcontext("/xyzzy") as c:
|
|
186
|
+
original_environ = c.request.environ
|
|
187
|
+
c.request.environ.update(environ_copied)
|
|
188
|
+
c.request.environ.update(environ_not_copied)
|
|
189
|
+
subrequest(view, _full=True, _query="test")
|
|
190
|
+
assert isinstance(subreq_environ, dict)
|
|
191
|
+
assert subreq_environ is not original_environ
|
|
192
|
+
assert all(subreq_environ[k] == environ_copied[k] for k in environ_copied)
|
|
193
|
+
assert all(
|
|
194
|
+
subreq_environ[k] != environ_not_copied[k] for k in environ_not_copied
|
|
195
|
+
)
|
|
196
|
+
assert subreq_environ["PATH_INFO"] == "/view"
|
|
197
|
+
assert subreq_environ["QUERY_STRING"] == "test"
|
|
198
|
+
|
|
199
|
+
def test_it_merges_custom_environ_keys(self):
|
|
200
|
+
subreq_environ: t.Optional[dict] = None
|
|
201
|
+
|
|
202
|
+
def view():
|
|
203
|
+
nonlocal subreq_environ
|
|
204
|
+
subreq_environ = context.request.environ
|
|
205
|
+
return Response()
|
|
206
|
+
|
|
207
|
+
app = FrescoApp()
|
|
208
|
+
app.route("/view", GET, view)
|
|
209
|
+
|
|
210
|
+
with app.requestcontext("/xyzzy") as c:
|
|
211
|
+
original_environ = c.request.environ
|
|
212
|
+
subrequest(view, _full=True, _env={"hey.there": True})
|
|
213
|
+
assert subreq_environ is not None
|
|
214
|
+
assert "hey.there" in subreq_environ
|
|
215
|
+
assert "hey.there" not in original_environ
|
|
216
|
+
|
|
217
|
+
def test_it_exhausts_content_iterator_before_closing_response(self):
|
|
218
|
+
tracker = ["view inited"]
|
|
219
|
+
|
|
220
|
+
def view():
|
|
221
|
+
def responder():
|
|
222
|
+
tracker.append("responder started")
|
|
223
|
+
yield ",".join(tracker)
|
|
224
|
+
|
|
225
|
+
r = Response(responder())
|
|
226
|
+
r = r.add_onclose(lambda: tracker.append("response closed"))
|
|
227
|
+
return r
|
|
228
|
+
|
|
229
|
+
app = FrescoApp()
|
|
230
|
+
app.route("/", GET, view)
|
|
231
|
+
|
|
232
|
+
with app.requestcontext("/"):
|
|
233
|
+
result = subrequest_str(view, _full=True)
|
|
234
|
+
assert ",".join(tracker) == "view inited,responder started,response closed"
|
|
235
|
+
|
|
236
|
+
assert result == "view inited,responder started"
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Copyright 2015 Oliver Cope
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
# flake8: noqa
|
|
16
|
+
from base64 import b64decode
|
|
17
|
+
|
|
18
|
+
multipart_samples = []
|
|
19
|
+
formencoded_samples = []
|
|
20
|
+
|
|
21
|
+
# 1k block of random bytes
|
|
22
|
+
FILE_UPLOAD_DATA = b64decode(
|
|
23
|
+
b"""
|
|
24
|
+
QJHX77oVa5GoHaIthmjSe8i8r6AhNIpIYx9Yw46gDqifTyd14aE/hWGoTCMR0hjFcE0GAQ7KE/S7
|
|
25
|
+
wnpoaAMManjCR4wD39lzhUsUPLy1N/ANwAEo0fk93y8ggEhbN8lEi0zl5rIecC1ZsEA8WG4dw3gh
|
|
26
|
+
61FRZdZ/fG8sojnfAjWM9UnSdhkd6idvF8qwKGBRpFPFUT4gRNu+di4i9r6+XQ7IiXbc2KXiXyy0
|
|
27
|
+
7AsrcOsOOiincLkw/IvDVYUgZmUiTqs0XKHfsFexpznIVIKubYGGlGpIyBdq04VmtPoQvj9weqZh
|
|
28
|
+
di5F9jlGBJ479vHNS+6xwbnxqpSNkAkzsBOQ6a68Ae9in1R0t+UIlUlRKG6jsUelkbJlq3+qnzqt
|
|
29
|
+
RFBuOneh/vdlFEhuXQSxZ4a7oPl+wuv94fE6AsWyiV4urZR16CaioAbeozrd5MOqBkKk7Q2TyWX/
|
|
30
|
+
hHA05mlv6g4/34lh5N4UWH8YTLEjgL9ykMJ6zodmGujmvmYwIiVQuhIcjsZ5n0m0zYBlDP4BDspV
|
|
31
|
+
bQsTxPa+0ptiQJ+qTStqPWCDaNFO6Fwdia7jxs7asPAuCwIcgUL2ztmCfafvlniMSwC8lmjs77Zt
|
|
32
|
+
gvgeCWr6IoASWOso1oEFsCgk6HMEU+XW5bLD5h5cva0/on/K1wwpFF+FQlQEqvd2cAEFZrZ8Dk4E
|
|
33
|
+
zKK+tdU3qK5maooKTIwpj8llk3ENpRAZXJUYRpUzvdb3m4kDg+sHCbZw5t8bpC5Oph89AHytc+Op
|
|
34
|
+
zY6+/UUHiUuIuf0hflVqQgb9uangMAkHkMyjw6o7dFPPanfu2j0DouqGF2QKRczdcs9itbWh65/O
|
|
35
|
+
PX1KbztEqAAxTqZixNI9qkG8A9rW24lDX6QFSws/0Wjh/UUvixAq9u/Dobbqn+hUgsQl2xqoMtci
|
|
36
|
+
sRJajasOPIyIOgwpLP5ul2DMIqZUDmAii3OcOQL4y4b0vgUHk26iNp9omPpzenJuAECoyIE8KnEf
|
|
37
|
+
SK1Cv9mQOU6Gbl3gyLsMp9fdsespzLFv8M+VrlaLCDTgL6OL2DW9SwOH4FWKH8qAjJfTsWnhqpbS
|
|
38
|
+
PBRsHlujXX1lAZC5eKCA4ekug2+aW7g+Mpse2Rt1XP4w8UZ231bxCYmYsKrzsB5f/0Pfp/vAjYHm
|
|
39
|
+
GYtj6RBhRoTym2Sm/A1VOOOBYSRPZXeqdx2WoIrGtSlkEZnH2ySM5z1Se6v9y4uI0TN5Oa5q691G
|
|
40
|
+
UegOAO9Yx4oa1L+7BXs+lkIqz94bVCLNI6gwLC+QOhz6iC96LyagKZtcYyagLeiQGhh91oJXWCCm
|
|
41
|
+
sz4EbqNbiEtfTa6LhAs+mkLSOfrAsyWLr/reTu4uHwJXsGzgn17Uhe/+VxV74/yEuwTLTbHHgA==
|
|
42
|
+
"""
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.0.11) Gecko/2009061410 Firefox/3.0.11
|
|
46
|
+
multipart_samples.append(
|
|
47
|
+
{
|
|
48
|
+
"content_length": "1638",
|
|
49
|
+
"data": b'-----------------------------1282332249122276511499183892\r\nContent-Disposition: form-data; name="empty-text-input"\r\n\r\n\r\n-----------------------------1282332249122276511499183892\r\nContent-Disposition: form-data; name="text-input-ascii"\r\n\r\nabcdef\r\n-----------------------------1282332249122276511499183892\r\nContent-Disposition: form-data; name="text-input-unicode"\r\n\r\n\xce\xb1\xce\xb2\xce\xb3\xce\xb4\r\n-----------------------------1282332249122276511499183892\r\nContent-Disposition: form-data; name="file-upload"; filename="test.data"\r\nContent-Type: application/octet-stream\r\n\r\n@\x91\xd7\xef\xba\x15k\x91\xa8\x1d\xa2-\x86h\xd2{\xc8\xbc\xaf\xa0!4\x8aHc\x1fX\xc3\x8e\xa0\x0e\xa8\x9fO\'u\xe1\xa1?\x85a\xa8L#\x11\xd2\x18\xc5pM\x06\x01\x0e\xca\x13\xf4\xbb\xc2zhh\x03\x0cjx\xc2G\x8c\x03\xdf\xd9s\x85K\x14<\xbc\xb57\xf0\r\xc0\x01(\xd1\xf9=\xdf/ \x80H[7\xc9D\x8bL\xe5\xe6\xb2\x1ep-Y\xb0@<Xn\x1d\xc3x!\xebQQe\xd6\x7f|o,\xa29\xdf\x025\x8c\xf5I\xd2v\x19\x1d\xea\'o\x17\xca\xb0(`Q\xa4S\xc5Q> D\xdb\xbev."\xf6\xbe\xbe]\x0e\xc8\x89v\xdc\xd8\xa5\xe2_,\xb4\xec\x0b+p\xeb\x0e:(\xa7p\xb90\xfc\x8b\xc3U\x85 fe"N\xab4\\\xa1\xdf\xb0W\xb1\xa79\xc8T\x82\xaem\x81\x86\x94jH\xc8\x17j\xd3\x85f\xb4\xfa\x10\xbe?pz\xa6av.E\xf69F\x04\x9e;\xf6\xf1\xcdK\xee\xb1\xc1\xb9\xf1\xaa\x94\x8d\x90\t3\xb0\x13\x90\xe9\xae\xbc\x01\xefb\x9fTt\xb7\xe5\x08\x95IQ(n\xa3\xb1G\xa5\x91\xb2e\xab\x7f\xaa\x9f:\xadDPn:w\xa1\xfe\xf7e\x14Hn]\x04\xb1g\x86\xbb\xa0\xf9~\xc2\xeb\xfd\xe1\xf1:\x02\xc5\xb2\x89^.\xad\x94u\xe8&\xa2\xa0\x06\xde\xa3:\xdd\xe4\xc3\xaa\x06B\xa4\xed\r\x93\xc9e\xff\x84p4\xe6io\xea\x0e?\xdf\x89a\xe4\xde\x14X\x7f\x18L\xb1#\x80\xbfr\x90\xc2z\xce\x87f\x1a\xe8\xe6\xbef0"%P\xba\x12\x1c\x8e\xc6y\x9fI\xb4\xcd\x80e\x0c\xfe\x01\x0e\xcaUm\x0b\x13\xc4\xf6\xbe\xd2\x9bb@\x9f\xaaM+j=`\x83h\xd1N\xe8\\\x1d\x89\xae\xe3\xc6\xce\xda\xb0\xf0.\x0b\x02\x1c\x81B\xf6\xce\xd9\x82}\xa7\xef\x96x\x8cK\x00\xbc\x96h\xec\xef\xb6m\x82\xf8\x1e\tj\xfa"\x80\x12X\xeb(\xd6\x81\x05\xb0($\xe8s\x04S\xe5\xd6\xe5\xb2\xc3\xe6\x1e\\\xbd\xad?\xa2\x7f\xca\xd7\x0c)\x14_\x85BT\x04\xaa\xf7vp\x01\x05f\xb6|\x0eN\x04\xcc\xa2\xbe\xb5\xd57\xa8\xaefj\x8a\nL\x8c)\x8f\xc9e\x93q\r\xa5\x10\x19\\\x95\x18F\x953\xbd\xd6\xf7\x9b\x89\x03\x83\xeb\x07\t\xb6p\xe6\xdf\x1b\xa4.N\xa6\x1f=\x00|\xads\xe3\xa9\xcd\x8e\xbe\xfdE\x07\x89K\x88\xb9\xfd!~UjB\x06\xfd\xb9\xa9\xe00\t\x07\x90\xcc\xa3\xc3\xaa;tS\xcfjw\xee\xda=\x03\xa2\xea\x86\x17d\nE\xcc\xddr\xcfb\xb5\xb5\xa1\xeb\x9f\xce=}Jo;D\xa8\x001N\xa6b\xc4\xd2=\xaaA\xbc\x03\xda\xd6\xdb\x89C_\xa4\x05K\x0b?\xd1h\xe1\xfdE/\x8b\x10*\xf6\xef\xc3\xa1\xb6\xea\x9f\xe8T\x82\xc4%\xdb\x1a\xa82\xd7"\xb1\x12Z\x8d\xab\x0e<\x8c\x88:\x0c),\xfen\x97`\xcc"\xa6T\x0e`"\x8bs\x9c9\x02\xf8\xcb\x86\xf4\xbe\x05\x07\x93n\xa26\x9fh\x98\xfaszrn\x00@\xa8\xc8\x81<*q\x1fH\xadB\xbf\xd9\x909N\x86n]\xe0\xc8\xbb\x0c\xa7\xd7\xdd\xb1\xeb)\xcc\xb1o\xf0\xcf\x95\xaeV\x8b\x084\xe0/\xa3\x8b\xd85\xbdK\x03\x87\xe0U\x8a\x1f\xca\x80\x8c\x97\xd3\xb1i\xe1\xaa\x96\xd2<\x14l\x1e[\xa3]}e\x01\x90\xb9x\xa0\x80\xe1\xe9.\x83o\x9a[\xb8>2\x9b\x1e\xd9\x1bu\\\xfe0\xf1Fv\xdfV\xf1\t\x89\x98\xb0\xaa\xf3\xb0\x1e_\xffC\xdf\xa7\xfb\xc0\x8d\x81\xe6\x19\x8bc\xe9\x10aF\x84\xf2\x9bd\xa6\xfc\rU8\xe3\x81a$Oew\xaaw\x1d\x96\xa0\x8a\xc6\xb5)d\x11\x99\xc7\xdb$\x8c\xe7=R{\xab\xfd\xcb\x8b\x88\xd13y9\xaej\xeb\xddFQ\xe8\x0e\x00\xefX\xc7\x8a\x1a\xd4\xbf\xbb\x05{>\x96B*\xcf\xde\x1bT"\xcd#\xa80,/\x90:\x1c\xfa\x88/z/&\xa0)\x9b\\c&\xa0-\xe8\x90\x1a\x18}\xd6\x82WX \xa6\xb3>\x04n\xa3[\x88K_M\xae\x8b\x84\x0b>\x9aB\xd29\xfa\xc0\xb3%\x8b\xaf\xfa\xdeN\xee.\x1f\x02W\xb0l\xe0\x9f^\xd4\x85\xef\xfeW\x15{\xe3\xfc\x84\xbb\x04\xcbM\xb1\xc7\x80\r\n-----------------------------1282332249122276511499183892--\r\n',
|
|
50
|
+
"content_type": "multipart/form-data; boundary=---------------------------1282332249122276511499183892",
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.0.11) Gecko/2009061410 Firefox/3.0.11
|
|
55
|
+
formencoded_samples.append(
|
|
56
|
+
{
|
|
57
|
+
"content_length": "85",
|
|
58
|
+
"data": b"empty-text-input=&text-input-ascii=abcdef&text-input-unicode=%CE%B1%CE%B2%CE%B3%CE%B4",
|
|
59
|
+
"content_type": "application/x-www-form-urlencoded",
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; InfoPath.2)
|
|
64
|
+
multipart_samples.append(
|
|
65
|
+
{
|
|
66
|
+
"content_length": "1616",
|
|
67
|
+
"data": b'-----------------------------7d91772e200be\r\nContent-Disposition: form-data; name="empty-text-input"\r\n\r\n\r\n-----------------------------7d91772e200be\r\nContent-Disposition: form-data; name="text-input-ascii"\r\n\r\nabcdef\r\n-----------------------------7d91772e200be\r\nContent-Disposition: form-data; name="text-input-unicode"\r\n\r\n\xce\xb1\xce\xb2\xce\xb3\xce\xb4\r\n-----------------------------7d91772e200be\r\nContent-Disposition: form-data; name="file-upload"; filename="C:\\Documents and Settings\\Administrator\\My Documents\\test.data"\r\nContent-Type: application/octet-stream\r\n\r\n@\x91\xd7\xef\xba\x15k\x91\xa8\x1d\xa2-\x86h\xd2{\xc8\xbc\xaf\xa0!4\x8aHc\x1fX\xc3\x8e\xa0\x0e\xa8\x9fO\'u\xe1\xa1?\x85a\xa8L#\x11\xd2\x18\xc5pM\x06\x01\x0e\xca\x13\xf4\xbb\xc2zhh\x03\x0cjx\xc2G\x8c\x03\xdf\xd9s\x85K\x14<\xbc\xb57\xf0\r\xc0\x01(\xd1\xf9=\xdf/ \x80H[7\xc9D\x8bL\xe5\xe6\xb2\x1ep-Y\xb0@<Xn\x1d\xc3x!\xebQQe\xd6\x7f|o,\xa29\xdf\x025\x8c\xf5I\xd2v\x19\x1d\xea\'o\x17\xca\xb0(`Q\xa4S\xc5Q> D\xdb\xbev."\xf6\xbe\xbe]\x0e\xc8\x89v\xdc\xd8\xa5\xe2_,\xb4\xec\x0b+p\xeb\x0e:(\xa7p\xb90\xfc\x8b\xc3U\x85 fe"N\xab4\\\xa1\xdf\xb0W\xb1\xa79\xc8T\x82\xaem\x81\x86\x94jH\xc8\x17j\xd3\x85f\xb4\xfa\x10\xbe?pz\xa6av.E\xf69F\x04\x9e;\xf6\xf1\xcdK\xee\xb1\xc1\xb9\xf1\xaa\x94\x8d\x90\t3\xb0\x13\x90\xe9\xae\xbc\x01\xefb\x9fTt\xb7\xe5\x08\x95IQ(n\xa3\xb1G\xa5\x91\xb2e\xab\x7f\xaa\x9f:\xadDPn:w\xa1\xfe\xf7e\x14Hn]\x04\xb1g\x86\xbb\xa0\xf9~\xc2\xeb\xfd\xe1\xf1:\x02\xc5\xb2\x89^.\xad\x94u\xe8&\xa2\xa0\x06\xde\xa3:\xdd\xe4\xc3\xaa\x06B\xa4\xed\r\x93\xc9e\xff\x84p4\xe6io\xea\x0e?\xdf\x89a\xe4\xde\x14X\x7f\x18L\xb1#\x80\xbfr\x90\xc2z\xce\x87f\x1a\xe8\xe6\xbef0"%P\xba\x12\x1c\x8e\xc6y\x9fI\xb4\xcd\x80e\x0c\xfe\x01\x0e\xcaUm\x0b\x13\xc4\xf6\xbe\xd2\x9bb@\x9f\xaaM+j=`\x83h\xd1N\xe8\\\x1d\x89\xae\xe3\xc6\xce\xda\xb0\xf0.\x0b\x02\x1c\x81B\xf6\xce\xd9\x82}\xa7\xef\x96x\x8cK\x00\xbc\x96h\xec\xef\xb6m\x82\xf8\x1e\tj\xfa"\x80\x12X\xeb(\xd6\x81\x05\xb0($\xe8s\x04S\xe5\xd6\xe5\xb2\xc3\xe6\x1e\\\xbd\xad?\xa2\x7f\xca\xd7\x0c)\x14_\x85BT\x04\xaa\xf7vp\x01\x05f\xb6|\x0eN\x04\xcc\xa2\xbe\xb5\xd57\xa8\xaefj\x8a\nL\x8c)\x8f\xc9e\x93q\r\xa5\x10\x19\\\x95\x18F\x953\xbd\xd6\xf7\x9b\x89\x03\x83\xeb\x07\t\xb6p\xe6\xdf\x1b\xa4.N\xa6\x1f=\x00|\xads\xe3\xa9\xcd\x8e\xbe\xfdE\x07\x89K\x88\xb9\xfd!~UjB\x06\xfd\xb9\xa9\xe00\t\x07\x90\xcc\xa3\xc3\xaa;tS\xcfjw\xee\xda=\x03\xa2\xea\x86\x17d\nE\xcc\xddr\xcfb\xb5\xb5\xa1\xeb\x9f\xce=}Jo;D\xa8\x001N\xa6b\xc4\xd2=\xaaA\xbc\x03\xda\xd6\xdb\x89C_\xa4\x05K\x0b?\xd1h\xe1\xfdE/\x8b\x10*\xf6\xef\xc3\xa1\xb6\xea\x9f\xe8T\x82\xc4%\xdb\x1a\xa82\xd7"\xb1\x12Z\x8d\xab\x0e<\x8c\x88:\x0c),\xfen\x97`\xcc"\xa6T\x0e`"\x8bs\x9c9\x02\xf8\xcb\x86\xf4\xbe\x05\x07\x93n\xa26\x9fh\x98\xfaszrn\x00@\xa8\xc8\x81<*q\x1fH\xadB\xbf\xd9\x909N\x86n]\xe0\xc8\xbb\x0c\xa7\xd7\xdd\xb1\xeb)\xcc\xb1o\xf0\xcf\x95\xaeV\x8b\x084\xe0/\xa3\x8b\xd85\xbdK\x03\x87\xe0U\x8a\x1f\xca\x80\x8c\x97\xd3\xb1i\xe1\xaa\x96\xd2<\x14l\x1e[\xa3]}e\x01\x90\xb9x\xa0\x80\xe1\xe9.\x83o\x9a[\xb8>2\x9b\x1e\xd9\x1bu\\\xfe0\xf1Fv\xdfV\xf1\t\x89\x98\xb0\xaa\xf3\xb0\x1e_\xffC\xdf\xa7\xfb\xc0\x8d\x81\xe6\x19\x8bc\xe9\x10aF\x84\xf2\x9bd\xa6\xfc\rU8\xe3\x81a$Oew\xaaw\x1d\x96\xa0\x8a\xc6\xb5)d\x11\x99\xc7\xdb$\x8c\xe7=R{\xab\xfd\xcb\x8b\x88\xd13y9\xaej\xeb\xddFQ\xe8\x0e\x00\xefX\xc7\x8a\x1a\xd4\xbf\xbb\x05{>\x96B*\xcf\xde\x1bT"\xcd#\xa80,/\x90:\x1c\xfa\x88/z/&\xa0)\x9b\\c&\xa0-\xe8\x90\x1a\x18}\xd6\x82WX \xa6\xb3>\x04n\xa3[\x88K_M\xae\x8b\x84\x0b>\x9aB\xd29\xfa\xc0\xb3%\x8b\xaf\xfa\xdeN\xee.\x1f\x02W\xb0l\xe0\x9f^\xd4\x85\xef\xfeW\x15{\xe3\xfc\x84\xbb\x04\xcbM\xb1\xc7\x80\r\n-----------------------------7d91772e200be--\r\n',
|
|
68
|
+
"content_type": "multipart/form-data; boundary=---------------------------7d91772e200be",
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; InfoPath.2)
|
|
73
|
+
formencoded_samples.append(
|
|
74
|
+
{
|
|
75
|
+
"content_length": "85",
|
|
76
|
+
"data": b"empty-text-input=&text-input-ascii=abcdef&text-input-unicode=%CE%B1%CE%B2%CE%B3%CE%B4",
|
|
77
|
+
"content_type": "application/x-www-form-urlencoded",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Copyright 2015 Oliver Cope
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
from fresco.util.common import object_or_404
|
|
16
|
+
from fresco.exceptions import NotFound
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestObjectOr404(object):
|
|
21
|
+
def test_it_returns_the_object(self):
|
|
22
|
+
ob = object()
|
|
23
|
+
assert object_or_404(ob) is ob
|
|
24
|
+
|
|
25
|
+
def test_it_raises_NotFound(self):
|
|
26
|
+
with pytest.raises(NotFound):
|
|
27
|
+
object_or_404(None)
|
|
28
|
+
|
|
29
|
+
def test_it_raises_custom_exception(self):
|
|
30
|
+
class Foo(Exception):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
with pytest.raises(Foo):
|
|
34
|
+
object_or_404(None, exception=Foo)
|