fresco 3.3.2__py3-none-any.whl → 3.3.4__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.
- fresco/__init__.py +1 -1
- fresco/options.py +11 -3
- fresco/request.py +2 -2
- fresco/response.py +1 -1
- fresco/routing.py +1 -1
- 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 +319 -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.2.dist-info → fresco-3.3.4.dist-info}/METADATA +5 -6
- fresco-3.3.4.dist-info/RECORD +57 -0
- {fresco-3.3.2.dist-info → fresco-3.3.4.dist-info}/WHEEL +1 -1
- fresco-3.3.2.dist-info/RECORD +0 -34
- {fresco-3.3.2.dist-info → fresco-3.3.4.dist-info}/LICENSE.txt +0 -0
- {fresco-3.3.2.dist-info → fresco-3.3.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1038 @@
|
|
|
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 wsgiref.util import setup_testing_defaults
|
|
16
|
+
|
|
17
|
+
from unittest.mock import Mock, call
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
20
|
+
from fresco import Request, Response
|
|
21
|
+
from fresco import FrescoApp
|
|
22
|
+
from fresco import Route
|
|
23
|
+
from fresco import urlfor
|
|
24
|
+
from fresco import context
|
|
25
|
+
from fresco.routing import GET
|
|
26
|
+
from fresco.routing import POST
|
|
27
|
+
from fresco.routing import PUT
|
|
28
|
+
from fresco.routing import DELETE
|
|
29
|
+
from fresco.util.wsgi import ClosingIterator, make_environ
|
|
30
|
+
from . import fixtures
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_env(**env):
|
|
34
|
+
setup_testing_defaults(env)
|
|
35
|
+
return env
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CustomException(Exception):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestFrescoApp(object):
|
|
43
|
+
def test_route_operates_as_a_decorator(self):
|
|
44
|
+
app = FrescoApp()
|
|
45
|
+
|
|
46
|
+
@app.route("/", GET)
|
|
47
|
+
def view():
|
|
48
|
+
return Response([b"ok"])
|
|
49
|
+
|
|
50
|
+
with app.requestcontext("/"):
|
|
51
|
+
assert list(app.view().content_iterator) == [b"ok"]
|
|
52
|
+
|
|
53
|
+
def test_route_operates_as_a_function(self):
|
|
54
|
+
def view():
|
|
55
|
+
return Response([b"ok"])
|
|
56
|
+
|
|
57
|
+
app = FrescoApp()
|
|
58
|
+
app.route("/", GET, view)
|
|
59
|
+
with app.requestcontext("/"):
|
|
60
|
+
assert list(app.view().content_iterator) == [b"ok"]
|
|
61
|
+
|
|
62
|
+
def test_route_returns_route_instance(self):
|
|
63
|
+
def view():
|
|
64
|
+
return Response([b"ok"])
|
|
65
|
+
|
|
66
|
+
app = FrescoApp()
|
|
67
|
+
assert isinstance(app.route("/", GET, view), Route)
|
|
68
|
+
|
|
69
|
+
def test_route_http_methods(self):
|
|
70
|
+
def view():
|
|
71
|
+
return Response([context.request.environ["REQUEST_METHOD"]])
|
|
72
|
+
|
|
73
|
+
app = FrescoApp()
|
|
74
|
+
app.route("/get", GET, view)
|
|
75
|
+
app.route("/post", POST, view)
|
|
76
|
+
|
|
77
|
+
with app.requestcontext("/get", REQUEST_METHOD="GET"):
|
|
78
|
+
assert app.view().status_code == 200
|
|
79
|
+
|
|
80
|
+
with app.requestcontext("/get", REQUEST_METHOD="POST"):
|
|
81
|
+
assert app.view().status_code == 405
|
|
82
|
+
|
|
83
|
+
with app.requestcontext("/post", REQUEST_METHOD="GET"):
|
|
84
|
+
assert app.view().status_code == 405
|
|
85
|
+
|
|
86
|
+
with app.requestcontext("/post", REQUEST_METHOD="POST"):
|
|
87
|
+
assert app.view().status_code == 200
|
|
88
|
+
|
|
89
|
+
def test_HEAD_request_delegated_to_GET_view(self):
|
|
90
|
+
app = FrescoApp()
|
|
91
|
+
|
|
92
|
+
@app.route("/", GET)
|
|
93
|
+
def view():
|
|
94
|
+
return Response([], x_original_view="GET")
|
|
95
|
+
|
|
96
|
+
with app.requestcontext("/", REQUEST_METHOD="HEAD"):
|
|
97
|
+
assert app.view().get_header("X-Original-View") == "GET"
|
|
98
|
+
|
|
99
|
+
def test_NotFound_observed_when_raised_in_handler(self):
|
|
100
|
+
def app1():
|
|
101
|
+
from fresco.exceptions import NotFound
|
|
102
|
+
|
|
103
|
+
if "foo" in context.request.path_info:
|
|
104
|
+
raise NotFound()
|
|
105
|
+
return Response([b"app1"])
|
|
106
|
+
|
|
107
|
+
def app2():
|
|
108
|
+
return Response([b"app2"])
|
|
109
|
+
|
|
110
|
+
app = FrescoApp()
|
|
111
|
+
app.route_all("/", GET, app1)
|
|
112
|
+
app.route_all("/", GET, app2)
|
|
113
|
+
|
|
114
|
+
with app.requestcontext("/bar"):
|
|
115
|
+
assert list(app.view().content_iterator) == [b"app1"]
|
|
116
|
+
|
|
117
|
+
with app.requestcontext("/foo"):
|
|
118
|
+
assert list(app.view().content_iterator) == [b"app2"]
|
|
119
|
+
|
|
120
|
+
def test_NotFound_final_observed_when_raised_in_handler(self):
|
|
121
|
+
def app1():
|
|
122
|
+
from fresco.exceptions import NotFound
|
|
123
|
+
|
|
124
|
+
if "foo" in context.request.path_info:
|
|
125
|
+
raise NotFound(final=True)
|
|
126
|
+
return Response([b"app1"])
|
|
127
|
+
|
|
128
|
+
def app2():
|
|
129
|
+
return Response([b"app2"])
|
|
130
|
+
|
|
131
|
+
app = FrescoApp()
|
|
132
|
+
app.route_all("/", GET, app1)
|
|
133
|
+
app.route_all("/", GET, app2)
|
|
134
|
+
|
|
135
|
+
with app.requestcontext("/bar"):
|
|
136
|
+
assert list(app.view().content_iterator) == [b"app1"]
|
|
137
|
+
|
|
138
|
+
with app.requestcontext("/foo/"):
|
|
139
|
+
assert app.view().status_code == 404
|
|
140
|
+
|
|
141
|
+
def test_apps_called_in_correct_order(self):
|
|
142
|
+
def view(value=""):
|
|
143
|
+
return Response([value])
|
|
144
|
+
|
|
145
|
+
app = FrescoApp()
|
|
146
|
+
app.route_all("/f", GET, view, value=b"foo")
|
|
147
|
+
app.route_all("/", GET, view, value=b"bar")
|
|
148
|
+
|
|
149
|
+
with app.requestcontext("/f/bar"):
|
|
150
|
+
assert list(app.view().content_iterator) == [b"foo"]
|
|
151
|
+
|
|
152
|
+
with app.requestcontext("/b/bar"):
|
|
153
|
+
assert list(app.view().content_iterator) == [b"bar"]
|
|
154
|
+
|
|
155
|
+
def test_wsgi_app_handles_response_exceptions(self):
|
|
156
|
+
from fresco.exceptions import NotFound
|
|
157
|
+
|
|
158
|
+
def view():
|
|
159
|
+
raise NotFound()
|
|
160
|
+
|
|
161
|
+
app = FrescoApp()
|
|
162
|
+
app.route("/", GET, view)
|
|
163
|
+
|
|
164
|
+
with app.requestcontext("/"):
|
|
165
|
+
assert app.view().status_code == 404
|
|
166
|
+
|
|
167
|
+
def test_route_wsgi_app(self):
|
|
168
|
+
def wsgiapp(environ, start_response):
|
|
169
|
+
start_response("200 OK", [("Content-Type", "application/x-pachyderm")])
|
|
170
|
+
return [b"pretty pink elephants"]
|
|
171
|
+
|
|
172
|
+
app = FrescoApp()
|
|
173
|
+
app.route_wsgi("/", wsgiapp)
|
|
174
|
+
|
|
175
|
+
with app.requestcontext("/"):
|
|
176
|
+
assert list(app.view().content_iterator) == [b"pretty pink elephants"]
|
|
177
|
+
assert app.view().get_header("Content-Type") == "application/x-pachyderm"
|
|
178
|
+
|
|
179
|
+
def test_get_methods_matches_on_path(self):
|
|
180
|
+
app = FrescoApp()
|
|
181
|
+
app.route("/1", POST, lambda: None)
|
|
182
|
+
app.route("/1", PUT, lambda: None)
|
|
183
|
+
app.route("/2", GET, lambda: None)
|
|
184
|
+
app.route("/2", DELETE, lambda: None)
|
|
185
|
+
|
|
186
|
+
with app.requestcontext() as c:
|
|
187
|
+
assert app.get_methods(c.request, "/1") == set([POST, PUT])
|
|
188
|
+
|
|
189
|
+
with app.requestcontext() as c:
|
|
190
|
+
assert app.get_methods(c.request, "/2") == set([GET, DELETE])
|
|
191
|
+
|
|
192
|
+
def test_get_methods_matches_on_predicate(self):
|
|
193
|
+
p1 = Mock(return_value=True)
|
|
194
|
+
p2 = Mock(return_value=False)
|
|
195
|
+
|
|
196
|
+
app = FrescoApp()
|
|
197
|
+
app.route("/", POST, lambda: None, predicate=p1)
|
|
198
|
+
app.route("/", PUT, lambda: None, predicate=p2)
|
|
199
|
+
|
|
200
|
+
with app.requestcontext("/") as c:
|
|
201
|
+
assert app.get_methods(c.request, "/") == set([POST])
|
|
202
|
+
assert p1.call_args_list == [call(c.request)]
|
|
203
|
+
assert p2.call_args_list == [call(c.request)]
|
|
204
|
+
|
|
205
|
+
def test_invalid_path_encoding_triggers_bad_request(self):
|
|
206
|
+
app = FrescoApp()
|
|
207
|
+
with app.requestcontext(PATH_INFO=fixtures.misquoted_wsgi_unicode_path):
|
|
208
|
+
assert app.view().status_code == 400
|
|
209
|
+
|
|
210
|
+
def test_remove_middleware(self):
|
|
211
|
+
app = FrescoApp()
|
|
212
|
+
m1 = Mock()
|
|
213
|
+
m2 = Mock()
|
|
214
|
+
app.add_middleware(m1)
|
|
215
|
+
app.add_middleware(m2)
|
|
216
|
+
app.remove_middleware(m1)
|
|
217
|
+
app(create_env(), Mock()).close()
|
|
218
|
+
assert m1.call_count == 0
|
|
219
|
+
assert m2.call_count == 1
|
|
220
|
+
|
|
221
|
+
def test_insert_middleware(self):
|
|
222
|
+
calls = []
|
|
223
|
+
|
|
224
|
+
def middleware(app, name):
|
|
225
|
+
def middleware(env, start_response):
|
|
226
|
+
calls.append(name)
|
|
227
|
+
return app(env, start_response)
|
|
228
|
+
|
|
229
|
+
return middleware
|
|
230
|
+
|
|
231
|
+
app = FrescoApp()
|
|
232
|
+
app.add_middleware(middleware, "venus")
|
|
233
|
+
app.add_middleware(middleware, "mercury")
|
|
234
|
+
app.insert_middleware(0, middleware, "earth")
|
|
235
|
+
|
|
236
|
+
# Put a request through app to initialize the WSGI stack
|
|
237
|
+
with app.requestcontext("/"):
|
|
238
|
+
app.view()
|
|
239
|
+
|
|
240
|
+
# Middleware is called from the outside-in, so the item inserted in
|
|
241
|
+
# position 0 is last to be called
|
|
242
|
+
assert calls == ["mercury", "venus", "earth"]
|
|
243
|
+
|
|
244
|
+
def test_routing_falls_though_with_fallthrough_on(self):
|
|
245
|
+
app = FrescoApp()
|
|
246
|
+
app.route(
|
|
247
|
+
"/",
|
|
248
|
+
GET=lambda: Response(status="204 No Content"),
|
|
249
|
+
fallthrough_on={204},
|
|
250
|
+
)
|
|
251
|
+
app.route("/", GET=lambda: Response(status="200 OK"))
|
|
252
|
+
|
|
253
|
+
with app.requestcontext("/"):
|
|
254
|
+
response = app.view()
|
|
255
|
+
|
|
256
|
+
assert response.status_code == 200
|
|
257
|
+
|
|
258
|
+
def test_fallthrough_on_returns_last_error(self):
|
|
259
|
+
app = FrescoApp()
|
|
260
|
+
app.route("/", GET=lambda: Response(status="499"), fallthrough_on={499})
|
|
261
|
+
|
|
262
|
+
with app.requestcontext("/"):
|
|
263
|
+
response = app.view()
|
|
264
|
+
assert response.status_code == 499
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestAppHooks(object):
|
|
268
|
+
def test_process_request_continues_on_none(self):
|
|
269
|
+
app = FrescoApp()
|
|
270
|
+
view = Mock(return_value=Response())
|
|
271
|
+
app.route("/", GET, view)
|
|
272
|
+
|
|
273
|
+
@app.process_request
|
|
274
|
+
def process_request(request):
|
|
275
|
+
assert isinstance(request, Request)
|
|
276
|
+
return None
|
|
277
|
+
|
|
278
|
+
with app.requestcontext("/"):
|
|
279
|
+
app.view()
|
|
280
|
+
assert view.call_count == 1
|
|
281
|
+
|
|
282
|
+
def test_process_request_replaces_response(self):
|
|
283
|
+
app = FrescoApp()
|
|
284
|
+
view = Mock(return_value=Response())
|
|
285
|
+
app.route("/", GET, view)
|
|
286
|
+
new_response = Response()
|
|
287
|
+
|
|
288
|
+
@app.process_request
|
|
289
|
+
def process_request(request):
|
|
290
|
+
return new_response
|
|
291
|
+
|
|
292
|
+
with app.requestcontext("/"):
|
|
293
|
+
assert app.view() is new_response
|
|
294
|
+
assert view.call_count == 0
|
|
295
|
+
|
|
296
|
+
def test_process_request_once_only_called_once(self):
|
|
297
|
+
app = FrescoApp()
|
|
298
|
+
view = Mock(return_value=Response())
|
|
299
|
+
app.route("/", GET, view)
|
|
300
|
+
new_response = Response()
|
|
301
|
+
|
|
302
|
+
@app.process_request_once
|
|
303
|
+
def process_request(request):
|
|
304
|
+
return new_response
|
|
305
|
+
|
|
306
|
+
with app.requestcontext("/"):
|
|
307
|
+
assert app.view() is new_response
|
|
308
|
+
assert view.call_count == 0
|
|
309
|
+
assert app.view() is not new_response
|
|
310
|
+
assert view.call_count == 1
|
|
311
|
+
|
|
312
|
+
def test_process_view_continues_on_none(self):
|
|
313
|
+
app = FrescoApp()
|
|
314
|
+
view = Mock(return_value=Response())
|
|
315
|
+
app.route("/", GET, view)
|
|
316
|
+
|
|
317
|
+
@app.process_view
|
|
318
|
+
def process_view(request, v, args, kwargs):
|
|
319
|
+
assert v is view
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
with app.requestcontext("/"):
|
|
323
|
+
app.view()
|
|
324
|
+
assert view.call_count == 1
|
|
325
|
+
|
|
326
|
+
def test_process_view_replaces_view(self):
|
|
327
|
+
app = FrescoApp()
|
|
328
|
+
view = Mock(return_value=Response())
|
|
329
|
+
view2 = Mock(return_value=Response())
|
|
330
|
+
app.route("/", GET, view)
|
|
331
|
+
|
|
332
|
+
@app.process_view
|
|
333
|
+
def process_view(request, v, args, kwargs):
|
|
334
|
+
assert v is view
|
|
335
|
+
return view2
|
|
336
|
+
|
|
337
|
+
with app.requestcontext("/"):
|
|
338
|
+
app.view()
|
|
339
|
+
assert view.call_count == 0
|
|
340
|
+
assert view2.call_count == 1
|
|
341
|
+
|
|
342
|
+
def test_process_view_replaces_response(self):
|
|
343
|
+
app = FrescoApp()
|
|
344
|
+
new_response = Response()
|
|
345
|
+
view = Mock(return_value=Response())
|
|
346
|
+
app.route("/", GET, view)
|
|
347
|
+
|
|
348
|
+
@app.process_view
|
|
349
|
+
def process_view(request, v, args, kwargs):
|
|
350
|
+
assert v is view
|
|
351
|
+
return new_response
|
|
352
|
+
|
|
353
|
+
with app.requestcontext("/"):
|
|
354
|
+
assert app.view() is new_response
|
|
355
|
+
assert view.call_count == 0
|
|
356
|
+
|
|
357
|
+
def test_process_response_continues_on_none(self):
|
|
358
|
+
app = FrescoApp()
|
|
359
|
+
response = Response()
|
|
360
|
+
view = Mock(return_value=response)
|
|
361
|
+
app.route("/", GET, view)
|
|
362
|
+
|
|
363
|
+
@app.process_response
|
|
364
|
+
def process_response(req, res):
|
|
365
|
+
assert res is response
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
with app.requestcontext("/"):
|
|
369
|
+
assert app.view() is response
|
|
370
|
+
|
|
371
|
+
def test_process_response_replaces_response(self):
|
|
372
|
+
app = FrescoApp()
|
|
373
|
+
new_response = Response()
|
|
374
|
+
view = Mock(return_value=Response())
|
|
375
|
+
app.route("/", GET, view)
|
|
376
|
+
|
|
377
|
+
@app.process_response
|
|
378
|
+
def process_response(req, res):
|
|
379
|
+
return new_response
|
|
380
|
+
|
|
381
|
+
with app.requestcontext("/"):
|
|
382
|
+
assert app.view() is new_response
|
|
383
|
+
|
|
384
|
+
def test_process_http_error_response_continues_on_none(self):
|
|
385
|
+
app = FrescoApp()
|
|
386
|
+
response = Response.not_found()
|
|
387
|
+
view = Mock(return_value=response)
|
|
388
|
+
app.route("/", GET, view)
|
|
389
|
+
|
|
390
|
+
@app.process_http_error_response
|
|
391
|
+
def process_http_error_response(req, res):
|
|
392
|
+
return None
|
|
393
|
+
|
|
394
|
+
with app.requestcontext("/"):
|
|
395
|
+
assert app.view() is response
|
|
396
|
+
|
|
397
|
+
def test_process_http_error_response_replaces_response(self):
|
|
398
|
+
app = FrescoApp()
|
|
399
|
+
new_response = Response()
|
|
400
|
+
view = Mock(return_value=Response.not_found())
|
|
401
|
+
app.route("/", GET, view)
|
|
402
|
+
|
|
403
|
+
@app.process_http_error_response
|
|
404
|
+
def process_http_error_response(req, res):
|
|
405
|
+
return new_response
|
|
406
|
+
|
|
407
|
+
with app.requestcontext("/"):
|
|
408
|
+
assert app.view() is new_response
|
|
409
|
+
|
|
410
|
+
def test_process_http_error_response_called_on_internal_errors(self):
|
|
411
|
+
app = FrescoApp()
|
|
412
|
+
new_response = Response()
|
|
413
|
+
view = Mock(return_value=Response())
|
|
414
|
+
app.route("/", GET, view)
|
|
415
|
+
|
|
416
|
+
hook = Mock(return_value=new_response)
|
|
417
|
+
app.process_http_error_response(hook)
|
|
418
|
+
|
|
419
|
+
# POSTing to a GET route should generate 405 method not supported
|
|
420
|
+
with app.requestcontext_post("/"):
|
|
421
|
+
assert app.view() is new_response
|
|
422
|
+
hook_args, hook_kwargs = hook.call_args
|
|
423
|
+
assert hook_args[1].status_code == 405
|
|
424
|
+
|
|
425
|
+
# Should generate a 404 not found
|
|
426
|
+
with app.requestcontext_post("/asdf"):
|
|
427
|
+
assert app.view() is new_response
|
|
428
|
+
hook_args, hook_kwargs = hook.call_args
|
|
429
|
+
assert hook_args[1].status_code == 404
|
|
430
|
+
|
|
431
|
+
def test_process_http_error_response_not_called_on_success(self):
|
|
432
|
+
app = FrescoApp()
|
|
433
|
+
ok_response = Response([""], status="200 OK")
|
|
434
|
+
redirect_response = Response([""], status="302 Found", location="/")
|
|
435
|
+
ok_view = Mock(return_value=ok_response)
|
|
436
|
+
redirect_view = Mock(return_value=redirect_response)
|
|
437
|
+
app.route("/ok", GET, ok_view)
|
|
438
|
+
app.route("/redirect", GET, redirect_view)
|
|
439
|
+
|
|
440
|
+
@app.process_http_error_response
|
|
441
|
+
def process_http_error_response(req, res):
|
|
442
|
+
assert False, "Hook should not be called"
|
|
443
|
+
|
|
444
|
+
with app.requestcontext("/ok"):
|
|
445
|
+
assert app.view() is ok_response
|
|
446
|
+
|
|
447
|
+
with app.requestcontext("/redirect"):
|
|
448
|
+
assert app.view() is redirect_response
|
|
449
|
+
|
|
450
|
+
def test_process_http_error_response_can_be_associated_with_status(self):
|
|
451
|
+
app = FrescoApp()
|
|
452
|
+
process_404 = Mock(return_value=None)
|
|
453
|
+
process_500 = Mock(return_value=None)
|
|
454
|
+
app.route("/404", GET, Response.not_found)
|
|
455
|
+
app.route("/500", GET, Response.internal_server_error)
|
|
456
|
+
|
|
457
|
+
app.process_http_error_response(404)(process_404)
|
|
458
|
+
app.process_http_error_response(500)(process_500)
|
|
459
|
+
|
|
460
|
+
with app.requestcontext("/404"):
|
|
461
|
+
app.view()
|
|
462
|
+
assert process_500.call_count == 0
|
|
463
|
+
assert process_404.call_count == 1
|
|
464
|
+
|
|
465
|
+
with app.requestcontext("/500"):
|
|
466
|
+
app.view()
|
|
467
|
+
assert process_500.call_count == 1
|
|
468
|
+
assert process_404.call_count == 1
|
|
469
|
+
|
|
470
|
+
def test_process_teardown_called_on_teardown(self):
|
|
471
|
+
app = FrescoApp()
|
|
472
|
+
fn = Mock()
|
|
473
|
+
app.process_teardown(fn)
|
|
474
|
+
with app.requestcontext("/"):
|
|
475
|
+
app.view()
|
|
476
|
+
assert fn.call_count == 0
|
|
477
|
+
assert fn.call_count == 1
|
|
478
|
+
|
|
479
|
+
def test_adding_process_teardown_raises_error_after_app_starts(self):
|
|
480
|
+
app = FrescoApp()
|
|
481
|
+
with app.requestcontext("/") as c:
|
|
482
|
+
iterator = app(c.request.environ, Mock())
|
|
483
|
+
iterator.close()
|
|
484
|
+
with pytest.raises(Exception):
|
|
485
|
+
app.process_teardown(Mock())
|
|
486
|
+
|
|
487
|
+
def test_exception_in_process_hooks_do_not_stop_processing(self):
|
|
488
|
+
for hook, view in [
|
|
489
|
+
("process_request", Response),
|
|
490
|
+
("process_view", Response),
|
|
491
|
+
("process_response", Response),
|
|
492
|
+
("process_exception", Mock(side_effect=CustomException())),
|
|
493
|
+
("process_http_error_response", Response.not_found),
|
|
494
|
+
("process_teardown", Response),
|
|
495
|
+
]:
|
|
496
|
+
faulty_hook = Mock(side_effect=CustomException())
|
|
497
|
+
process_teardown = Mock(return_value=None)
|
|
498
|
+
|
|
499
|
+
app = FrescoApp()
|
|
500
|
+
app.route("/", GET, view) # type: ignore
|
|
501
|
+
getattr(app, hook)(faulty_hook)
|
|
502
|
+
app.process_teardown(process_teardown)
|
|
503
|
+
|
|
504
|
+
with app.requestcontext("/"):
|
|
505
|
+
app.view()
|
|
506
|
+
|
|
507
|
+
assert faulty_hook.call_count == 1, hook
|
|
508
|
+
assert process_teardown.call_count == 1
|
|
509
|
+
|
|
510
|
+
def test_it_raises_exception_in_route_resolution(self):
|
|
511
|
+
from fresco.routing import register_converter, StrConverter
|
|
512
|
+
|
|
513
|
+
@register_converter("bugs")
|
|
514
|
+
class Converter(StrConverter):
|
|
515
|
+
def from_string(self, s):
|
|
516
|
+
raise AssertionError("Oops!")
|
|
517
|
+
|
|
518
|
+
app = FrescoApp()
|
|
519
|
+
app.route("/<x:bugs>", GET=lambda: Response)
|
|
520
|
+
with app.requestcontext("/abc"):
|
|
521
|
+
with pytest.raises(AssertionError):
|
|
522
|
+
app.view()
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class TestProcessException(object):
|
|
526
|
+
def exception_view(self):
|
|
527
|
+
raise CustomException()
|
|
528
|
+
|
|
529
|
+
def iterator_exception_view(self):
|
|
530
|
+
def content_iterator():
|
|
531
|
+
yield b"fish"
|
|
532
|
+
raise CustomException()
|
|
533
|
+
yield b"chips"
|
|
534
|
+
|
|
535
|
+
return Response(content_iterator())
|
|
536
|
+
|
|
537
|
+
def test_it_reraises_if_no_error_handlers_installed(self):
|
|
538
|
+
app = FrescoApp()
|
|
539
|
+
|
|
540
|
+
app.route("/", GET, self.exception_view)
|
|
541
|
+
with app.requestcontext("/"):
|
|
542
|
+
with pytest.raises(CustomException):
|
|
543
|
+
app.view()
|
|
544
|
+
|
|
545
|
+
def test_it_reraises_if_no_500_error_handler_installed(self):
|
|
546
|
+
app = FrescoApp()
|
|
547
|
+
app.process_http_error_response(lambda req, res: None, 404)
|
|
548
|
+
|
|
549
|
+
app.route("/", GET, self.exception_view)
|
|
550
|
+
with app.requestcontext("/"):
|
|
551
|
+
with pytest.raises(CustomException):
|
|
552
|
+
app.view()
|
|
553
|
+
|
|
554
|
+
def test_it_calls_process_exception_handlers(self):
|
|
555
|
+
app = FrescoApp()
|
|
556
|
+
|
|
557
|
+
app.route("/", GET, self.exception_view)
|
|
558
|
+
new_response = Response()
|
|
559
|
+
process_exception = Mock(return_value=new_response)
|
|
560
|
+
app.process_exception(process_exception)
|
|
561
|
+
|
|
562
|
+
with app.requestcontext("/"):
|
|
563
|
+
assert app.view() is new_response
|
|
564
|
+
assert process_exception.call_count == 1
|
|
565
|
+
|
|
566
|
+
def test_it_returns_first_not_none_process_exception_handler(self):
|
|
567
|
+
app = FrescoApp()
|
|
568
|
+
|
|
569
|
+
app.route("/", GET, self.exception_view)
|
|
570
|
+
new_response1 = Response()
|
|
571
|
+
new_response2 = Response()
|
|
572
|
+
process_exception1 = Mock(return_value=None)
|
|
573
|
+
process_exception2 = Mock(return_value=new_response1)
|
|
574
|
+
process_exception3 = Mock(return_value=new_response2)
|
|
575
|
+
app.process_exception(process_exception1)
|
|
576
|
+
app.process_exception(process_exception2)
|
|
577
|
+
app.process_exception(process_exception3)
|
|
578
|
+
|
|
579
|
+
with app.requestcontext("/"):
|
|
580
|
+
assert app.view() is new_response1
|
|
581
|
+
assert process_exception1.call_count == 1
|
|
582
|
+
assert process_exception2.call_count == 1
|
|
583
|
+
assert process_exception3.call_count == 0
|
|
584
|
+
|
|
585
|
+
def test_it_calls_process_exception_handlers_in_content_iteration(self):
|
|
586
|
+
app = FrescoApp()
|
|
587
|
+
|
|
588
|
+
app.route("/", GET, self.iterator_exception_view)
|
|
589
|
+
process_exception = Mock(return_value=None)
|
|
590
|
+
app.process_exception(process_exception)
|
|
591
|
+
|
|
592
|
+
with app.requestcontext("/") as c:
|
|
593
|
+
content_iterator = app(c.request.environ, Mock())
|
|
594
|
+
assert process_exception.call_count == 0
|
|
595
|
+
list(content_iterator)
|
|
596
|
+
assert process_exception.call_count == 1
|
|
597
|
+
|
|
598
|
+
def test_it_reraises_when_exc_info_returned(self):
|
|
599
|
+
app = FrescoApp()
|
|
600
|
+
|
|
601
|
+
app.route("/", GET, self.exception_view)
|
|
602
|
+
app.process_exception(lambda req, exc_info: exc_info)
|
|
603
|
+
|
|
604
|
+
with app.requestcontext("/"):
|
|
605
|
+
with pytest.raises(CustomException):
|
|
606
|
+
app.view()
|
|
607
|
+
|
|
608
|
+
def test_it_calls_error_handlers_in_middleware(self):
|
|
609
|
+
"""
|
|
610
|
+
If a faulty middleware layer raises an exception it should
|
|
611
|
+
trigger error handling.
|
|
612
|
+
"""
|
|
613
|
+
|
|
614
|
+
def faulty_middleware(app):
|
|
615
|
+
def middleware(environ, start_response):
|
|
616
|
+
raise CustomException()
|
|
617
|
+
|
|
618
|
+
return middleware
|
|
619
|
+
|
|
620
|
+
app = FrescoApp([Route("/", GET, lambda: Response("foo"))])
|
|
621
|
+
app.add_middleware(faulty_middleware)
|
|
622
|
+
process_exception = Mock(return_value=None)
|
|
623
|
+
app.process_exception(process_exception)
|
|
624
|
+
process_http_error_response = Mock(return_value=Response(b"bar"))
|
|
625
|
+
app.process_http_error_response(process_http_error_response, 500)
|
|
626
|
+
|
|
627
|
+
content = list(app(make_environ(), Mock()))
|
|
628
|
+
assert process_exception.call_count == 1
|
|
629
|
+
assert process_http_error_response.call_count == 1
|
|
630
|
+
assert content == [b"bar"]
|
|
631
|
+
|
|
632
|
+
def test_it_associates_with_given_exception(self):
|
|
633
|
+
def exception_view():
|
|
634
|
+
raise ValueError()
|
|
635
|
+
|
|
636
|
+
app = FrescoApp()
|
|
637
|
+
app.route("/", GET, exception_view)
|
|
638
|
+
process_TypeError = Mock(return_value=None)
|
|
639
|
+
process_ValueError = Mock(return_value=None)
|
|
640
|
+
app.process_exception(TypeError)(process_TypeError)
|
|
641
|
+
app.process_exception(ValueError)(process_ValueError)
|
|
642
|
+
|
|
643
|
+
with app.requestcontext("/"):
|
|
644
|
+
app.view()
|
|
645
|
+
assert process_TypeError.call_count == 0
|
|
646
|
+
assert process_ValueError.call_count == 1
|
|
647
|
+
|
|
648
|
+
def test_it_calls_http_error_handler(self):
|
|
649
|
+
app = FrescoApp()
|
|
650
|
+
|
|
651
|
+
app.route("/", GET, self.exception_view)
|
|
652
|
+
new_response = Response()
|
|
653
|
+
process_http_error_response = Mock(return_value=new_response)
|
|
654
|
+
app.process_http_error_response(process_http_error_response)
|
|
655
|
+
|
|
656
|
+
with app.requestcontext("/"):
|
|
657
|
+
assert app.view() is new_response
|
|
658
|
+
assert process_http_error_response.call_count == 1
|
|
659
|
+
req, res = process_http_error_response.call_args[0]
|
|
660
|
+
assert res.status_code == 500
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
class TestIncludeApp(object):
|
|
664
|
+
def test_it_routes_to_an_included_app(self):
|
|
665
|
+
app = FrescoApp()
|
|
666
|
+
|
|
667
|
+
@app.route("/", GET)
|
|
668
|
+
def view():
|
|
669
|
+
return Response([b"ok"])
|
|
670
|
+
|
|
671
|
+
app2 = FrescoApp()
|
|
672
|
+
app2.include("/app1", app)
|
|
673
|
+
|
|
674
|
+
with app2.requestcontext("/"):
|
|
675
|
+
assert app2.view().status_code == 404
|
|
676
|
+
|
|
677
|
+
with app2.requestcontext("/app1/"):
|
|
678
|
+
assert list(app2.view().content_iterator) == [b"ok"]
|
|
679
|
+
|
|
680
|
+
def test_included_app_can_use_urlfor(self):
|
|
681
|
+
def view():
|
|
682
|
+
url = urlfor(view)
|
|
683
|
+
return Response(url)
|
|
684
|
+
|
|
685
|
+
app = FrescoApp()
|
|
686
|
+
app.route("/", GET, view)
|
|
687
|
+
app2 = FrescoApp()
|
|
688
|
+
app2.include("/app1", app)
|
|
689
|
+
|
|
690
|
+
with app2.requestcontext("/app1/"):
|
|
691
|
+
assert list(app2.view().content_iterator) == [b"http://localhost/app1/"]
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
class TestTrailingSlashes(object):
|
|
695
|
+
"""\
|
|
696
|
+
The general principle is that if a GET or HEAD request is received for a
|
|
697
|
+
URL without a trailing slash and no match is found, the app will look for a
|
|
698
|
+
URL with a trailing slash, and redirect the client if such a route exists.
|
|
699
|
+
"""
|
|
700
|
+
|
|
701
|
+
def test_no_trailing_slash(self):
|
|
702
|
+
def foo():
|
|
703
|
+
return Response(["foo"])
|
|
704
|
+
|
|
705
|
+
app = FrescoApp()
|
|
706
|
+
app.route("/foo/", GET, foo)
|
|
707
|
+
app.route("/", GET, foo)
|
|
708
|
+
|
|
709
|
+
with app.requestcontext("/foo"):
|
|
710
|
+
assert app.view().status_code == 301
|
|
711
|
+
assert app.view().get_header("location") == "http://localhost/foo/"
|
|
712
|
+
|
|
713
|
+
with app.requestcontext(""):
|
|
714
|
+
assert app.view().status_code == 301
|
|
715
|
+
assert app.view().get_header("location") == "http://localhost/"
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
class TestViewCollection(object):
|
|
719
|
+
def test_appdef(self):
|
|
720
|
+
app = FrescoApp()
|
|
721
|
+
app.include("/", fixtures.CBV("bananas!"))
|
|
722
|
+
with app.requestcontext("/"):
|
|
723
|
+
assert list(app.view().content_iterator) == [b"bananas!"]
|
|
724
|
+
|
|
725
|
+
def test_appdef_url_generation(self):
|
|
726
|
+
foo = fixtures.CBV("foo!")
|
|
727
|
+
bar = fixtures.CBV("bar!")
|
|
728
|
+
baz = fixtures.CBV("baz!")
|
|
729
|
+
|
|
730
|
+
app = FrescoApp(views=foo)
|
|
731
|
+
app.include("/bar", bar)
|
|
732
|
+
app.include("/baz", baz)
|
|
733
|
+
|
|
734
|
+
with app.requestcontext():
|
|
735
|
+
assert urlfor(foo.index_html) == "http://localhost/"
|
|
736
|
+
assert urlfor(bar.index_html) == "http://localhost/bar/"
|
|
737
|
+
assert urlfor(baz.index_html) == "http://localhost/baz/"
|
|
738
|
+
|
|
739
|
+
def test_instance_available_in_context(self):
|
|
740
|
+
s = []
|
|
741
|
+
|
|
742
|
+
class MyCBV(fixtures.CBV):
|
|
743
|
+
def index_html(self):
|
|
744
|
+
from fresco import context
|
|
745
|
+
|
|
746
|
+
s.append(context.view_self)
|
|
747
|
+
return Response([])
|
|
748
|
+
|
|
749
|
+
instance = MyCBV("foo!")
|
|
750
|
+
app = FrescoApp(views=instance)
|
|
751
|
+
|
|
752
|
+
with app.requestcontext("/"):
|
|
753
|
+
app.view()
|
|
754
|
+
assert s[0] is instance
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
class TestContextAttributes(object):
|
|
758
|
+
def test_app_is_set(self):
|
|
759
|
+
def check_app(expected):
|
|
760
|
+
assert context.app is expected
|
|
761
|
+
return Response([])
|
|
762
|
+
|
|
763
|
+
app1 = FrescoApp()
|
|
764
|
+
app2 = FrescoApp()
|
|
765
|
+
|
|
766
|
+
app1.route("/", GET, check_app, {"expected": app1})
|
|
767
|
+
app2.route("/", GET, check_app, {"expected": app2})
|
|
768
|
+
|
|
769
|
+
with app1.requestcontext("/"):
|
|
770
|
+
app1.view()
|
|
771
|
+
|
|
772
|
+
with app2.requestcontext("/"):
|
|
773
|
+
app2.view()
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
class TestAppRequestContext(object):
|
|
777
|
+
def _middleware(self, app):
|
|
778
|
+
def middleware(environ, start_response):
|
|
779
|
+
environ["sausages"] = 1
|
|
780
|
+
return app(environ, start_response)
|
|
781
|
+
|
|
782
|
+
return middleware
|
|
783
|
+
|
|
784
|
+
def test_creates_isolated_context(self):
|
|
785
|
+
app = FrescoApp()
|
|
786
|
+
with app.requestcontext():
|
|
787
|
+
context.request = "foo"
|
|
788
|
+
|
|
789
|
+
with app.requestcontext():
|
|
790
|
+
context.request = "bar"
|
|
791
|
+
assert context.request == "bar"
|
|
792
|
+
|
|
793
|
+
assert context.request == "foo"
|
|
794
|
+
|
|
795
|
+
def test_parses_full_url(self):
|
|
796
|
+
with FrescoApp().requestcontext("https://arthur@example.org:123/?x=y"):
|
|
797
|
+
assert context.request.environ["HTTPS"] == "on"
|
|
798
|
+
assert context.request.environ["REMOTE_USER"] == "arthur"
|
|
799
|
+
assert context.request.environ["HTTP_HOST"] == "example.org:123"
|
|
800
|
+
assert context.request.environ["SCRIPT_NAME"] == ""
|
|
801
|
+
assert context.request.environ["PATH_INFO"] == "/"
|
|
802
|
+
assert context.request.environ["QUERY_STRING"] == "x=y"
|
|
803
|
+
|
|
804
|
+
def test_it_posts_data(self):
|
|
805
|
+
with FrescoApp().requestcontext_post(data={"foo": "bar"}) as c:
|
|
806
|
+
assert c.request.method == "POST"
|
|
807
|
+
assert list(c.request.form.allitems()) == [("foo", "bar")]
|
|
808
|
+
assert list(c.request.query.allitems()) == []
|
|
809
|
+
|
|
810
|
+
def test_it_puts_data(self):
|
|
811
|
+
with FrescoApp().requestcontext_put(data={"foo": "bar"}) as c:
|
|
812
|
+
assert c.request.method == "PUT"
|
|
813
|
+
assert list(c.request.form.allitems()) == [("foo", "bar")]
|
|
814
|
+
|
|
815
|
+
def test_it_posts_data_multipart(self):
|
|
816
|
+
with FrescoApp().requestcontext_post(
|
|
817
|
+
files=[("foo", "foo.txt", "text/plain", b"bar")]
|
|
818
|
+
) as c:
|
|
819
|
+
assert c.request.files["foo"].filename == "foo.txt"
|
|
820
|
+
assert c.request.files["foo"].file.read() == b"bar"
|
|
821
|
+
|
|
822
|
+
def test_it_posts_raw_data(self):
|
|
823
|
+
with FrescoApp().requestcontext_post(
|
|
824
|
+
data=b"xyzzy", content_type="text/spell"
|
|
825
|
+
) as c:
|
|
826
|
+
assert c.request.body == "xyzzy"
|
|
827
|
+
|
|
828
|
+
def test_it_converts_header_names(self):
|
|
829
|
+
with FrescoApp().requestcontext(user_agent="foo"):
|
|
830
|
+
assert context.request.environ["HTTP_USER_AGENT"] == "foo"
|
|
831
|
+
|
|
832
|
+
def test_it_converts_wsgi_keys(self):
|
|
833
|
+
# CONTENT_TYPE is both a WSGI core environ key and a standard request
|
|
834
|
+
# header. The WSGI key must win.
|
|
835
|
+
with FrescoApp().requestcontext(content_type="foo"):
|
|
836
|
+
assert context.request.environ["CONTENT_TYPE"] == "foo"
|
|
837
|
+
|
|
838
|
+
def test_it_invokes_middleware(self):
|
|
839
|
+
app = FrescoApp()
|
|
840
|
+
app.add_middleware(self._middleware)
|
|
841
|
+
with app.requestcontext() as c:
|
|
842
|
+
assert "sausages" in c.request.environ
|
|
843
|
+
|
|
844
|
+
def test_it_skips_middleware(self):
|
|
845
|
+
app = FrescoApp()
|
|
846
|
+
app.add_middleware(self._middleware)
|
|
847
|
+
with app.requestcontext(middleware=False) as c:
|
|
848
|
+
assert "sausages" not in c.request.environ
|
|
849
|
+
|
|
850
|
+
def test_it_closes_middleware(self):
|
|
851
|
+
close = Mock()
|
|
852
|
+
|
|
853
|
+
def middleware(app):
|
|
854
|
+
def middleware(environ, start_response):
|
|
855
|
+
return ClosingIterator(app(environ, start_response), close)
|
|
856
|
+
|
|
857
|
+
return middleware
|
|
858
|
+
|
|
859
|
+
app = FrescoApp()
|
|
860
|
+
app.add_middleware(middleware)
|
|
861
|
+
with app.requestcontext():
|
|
862
|
+
pass
|
|
863
|
+
assert close.call_count == 1
|
|
864
|
+
|
|
865
|
+
def test_it_calls_first_iteration(self):
|
|
866
|
+
"""
|
|
867
|
+
Some middleware waits until the first iteration to do things, so make
|
|
868
|
+
sure we trigger this
|
|
869
|
+
"""
|
|
870
|
+
from itertools import count
|
|
871
|
+
|
|
872
|
+
counter = count()
|
|
873
|
+
|
|
874
|
+
def middleware(app):
|
|
875
|
+
def middleware(environ, start_response):
|
|
876
|
+
iterator = app(environ, start_response)
|
|
877
|
+
for item in iterator:
|
|
878
|
+
yield next(counter)
|
|
879
|
+
|
|
880
|
+
return middleware
|
|
881
|
+
|
|
882
|
+
app = FrescoApp()
|
|
883
|
+
app.add_middleware(middleware)
|
|
884
|
+
with app.requestcontext():
|
|
885
|
+
assert next(counter) == 1
|
|
886
|
+
|
|
887
|
+
def test_it_calls_late_added_middleware(self):
|
|
888
|
+
calls = []
|
|
889
|
+
|
|
890
|
+
def middleware(app):
|
|
891
|
+
calls.append("middleware initialized")
|
|
892
|
+
|
|
893
|
+
def middleware(env, start_response):
|
|
894
|
+
calls.append("middleware called")
|
|
895
|
+
return app(env, start_response)
|
|
896
|
+
|
|
897
|
+
return middleware
|
|
898
|
+
|
|
899
|
+
app = FrescoApp()
|
|
900
|
+
app.route("/", GET, lambda: Response())
|
|
901
|
+
|
|
902
|
+
# Put a request through app to initialize the WSGI stack
|
|
903
|
+
with app.requestcontext("/"):
|
|
904
|
+
app.view()
|
|
905
|
+
|
|
906
|
+
app.add_middleware(middleware)
|
|
907
|
+
|
|
908
|
+
# The request should cause the WSGI app to be rebuilt with the new
|
|
909
|
+
# middleware included
|
|
910
|
+
with app.requestcontext("/"):
|
|
911
|
+
app.view()
|
|
912
|
+
|
|
913
|
+
assert calls == ["middleware initialized", "middleware called"]
|
|
914
|
+
|
|
915
|
+
def test_it_passes_a_valid_start_response_to_middleware(self):
|
|
916
|
+
calls = []
|
|
917
|
+
|
|
918
|
+
def middleware(app):
|
|
919
|
+
def middleware(env, start_response):
|
|
920
|
+
calls.append("middleware called")
|
|
921
|
+
write = start_response("200 OK", [])
|
|
922
|
+
assert callable(write)
|
|
923
|
+
return app(env, lambda *a, **k: lambda s: None)
|
|
924
|
+
|
|
925
|
+
return middleware
|
|
926
|
+
|
|
927
|
+
app = FrescoApp()
|
|
928
|
+
app.add_middleware(middleware)
|
|
929
|
+
app.route("/", GET, lambda: Response())
|
|
930
|
+
|
|
931
|
+
with app.requestcontext("/"):
|
|
932
|
+
app.view()
|
|
933
|
+
|
|
934
|
+
assert calls == ["middleware called"]
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
class TestUrlfor(object):
|
|
938
|
+
def test_urlfor_on_aliased_functions(self):
|
|
939
|
+
def view():
|
|
940
|
+
return None
|
|
941
|
+
|
|
942
|
+
setattr(fixtures, "aliased_view", view)
|
|
943
|
+
|
|
944
|
+
app = FrescoApp()
|
|
945
|
+
app.route("/", GET, view)
|
|
946
|
+
with app.requestcontext():
|
|
947
|
+
assert urlfor(view) == "http://localhost/"
|
|
948
|
+
assert urlfor("fresco.tests.fixtures.aliased_view") == "http://localhost/"
|
|
949
|
+
|
|
950
|
+
delattr(fixtures, "aliased_view")
|
|
951
|
+
|
|
952
|
+
def test_urlfor_with_view_function(self):
|
|
953
|
+
def view():
|
|
954
|
+
return Response()
|
|
955
|
+
|
|
956
|
+
app = FrescoApp()
|
|
957
|
+
app.route("/foo", GET, view)
|
|
958
|
+
with app.requestcontext():
|
|
959
|
+
assert urlfor(view) == "http://localhost/foo"
|
|
960
|
+
|
|
961
|
+
def test_urlfor_allows_script_name(self):
|
|
962
|
+
def view():
|
|
963
|
+
return Response()
|
|
964
|
+
|
|
965
|
+
app = FrescoApp()
|
|
966
|
+
app.route("/foo", GET, view)
|
|
967
|
+
with app.requestcontext():
|
|
968
|
+
assert urlfor(view, _script_name="/abc") == "http://localhost/abc/foo"
|
|
969
|
+
|
|
970
|
+
def test_urlfor_with_string(self):
|
|
971
|
+
app = FrescoApp()
|
|
972
|
+
app.route("/myviewfunc", GET, fixtures.module_level_function)
|
|
973
|
+
with app.requestcontext():
|
|
974
|
+
assert (
|
|
975
|
+
urlfor("fresco.tests.fixtures.module_level_function")
|
|
976
|
+
== "http://localhost/myviewfunc"
|
|
977
|
+
)
|
|
978
|
+
|
|
979
|
+
def test_urlfor_drops_query(self):
|
|
980
|
+
def myviewfunc():
|
|
981
|
+
return Response()
|
|
982
|
+
|
|
983
|
+
app = FrescoApp()
|
|
984
|
+
app.route("/", GET, myviewfunc)
|
|
985
|
+
with app.requestcontext():
|
|
986
|
+
assert urlfor(myviewfunc) == "http://localhost/"
|
|
987
|
+
|
|
988
|
+
def test_urlfor_generates_first_route(self):
|
|
989
|
+
def myviewfunc():
|
|
990
|
+
return Response()
|
|
991
|
+
|
|
992
|
+
app = FrescoApp()
|
|
993
|
+
app.route("/1", GET, myviewfunc)
|
|
994
|
+
app.route("/2", GET, myviewfunc)
|
|
995
|
+
with app.requestcontext():
|
|
996
|
+
assert urlfor(myviewfunc) == "http://localhost/1"
|
|
997
|
+
|
|
998
|
+
def test_urlfor_with_class_based_view_spec(self):
|
|
999
|
+
app = FrescoApp()
|
|
1000
|
+
app.include("/foo", fixtures.CBV("x"))
|
|
1001
|
+
with app.requestcontext():
|
|
1002
|
+
assert (
|
|
1003
|
+
urlfor("fresco.tests.fixtures.CBV.index_html")
|
|
1004
|
+
== "http://localhost/foo/"
|
|
1005
|
+
)
|
|
1006
|
+
|
|
1007
|
+
def test_it_uses_values_from_path_defaults(self):
|
|
1008
|
+
app = FrescoApp()
|
|
1009
|
+
app.route("/<test:int>", GET, lambda: None, name="test", test_default=1)
|
|
1010
|
+
with app.requestcontext():
|
|
1011
|
+
assert urlfor("test") == "http://localhost/1"
|
|
1012
|
+
|
|
1013
|
+
def test_it_uses_callable_values_from_path_defaults(self):
|
|
1014
|
+
generate_value = Mock(return_value=1)
|
|
1015
|
+
app = FrescoApp()
|
|
1016
|
+
app.route(
|
|
1017
|
+
"/<test:int>",
|
|
1018
|
+
GET,
|
|
1019
|
+
lambda: None,
|
|
1020
|
+
name="test",
|
|
1021
|
+
test_default=generate_value,
|
|
1022
|
+
)
|
|
1023
|
+
with app.requestcontext() as c:
|
|
1024
|
+
assert urlfor("test") == "http://localhost/1"
|
|
1025
|
+
assert generate_value.call_args_list == [call(c.request)]
|
|
1026
|
+
|
|
1027
|
+
def test_urlfor_adds_query(self):
|
|
1028
|
+
def f():
|
|
1029
|
+
return Response()
|
|
1030
|
+
|
|
1031
|
+
app = FrescoApp()
|
|
1032
|
+
app.route("/", GET, f)
|
|
1033
|
+
with app.requestcontext():
|
|
1034
|
+
assert urlfor(f, _query="a=1") == "http://localhost/?a=1"
|
|
1035
|
+
assert urlfor(f, _query={"a": "1"}) == "http://localhost/?a=1"
|
|
1036
|
+
assert (
|
|
1037
|
+
urlfor(f, _query=[("a", "1"), ("a", 2)]) == "http://localhost/?a=1&a=2"
|
|
1038
|
+
)
|