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.

@@ -0,0 +1,224 @@
1
+ # encoding=UTF-8
2
+ # Copyright 2015 Oliver Cope
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ from unittest.mock import Mock
17
+ import pytest
18
+
19
+ from fresco import FrescoApp
20
+ from fresco.cookie import Cookie
21
+ from fresco.response import Response
22
+ from fresco.routing import GET
23
+ from fresco.routing import RouteNotFound
24
+
25
+
26
+ class TestCannedResponses(object):
27
+ def test_redirect(self):
28
+ with FrescoApp().requestcontext():
29
+ r = Response.redirect("/new_location")
30
+ assert b"http://localhost/new_location" in b"".join(r.content_iterator)
31
+ assert r.get_header("Location") == "http://localhost/new_location"
32
+ assert r.status == "302 Found"
33
+
34
+ def test_redirect_temporary(self):
35
+ with FrescoApp().requestcontext():
36
+ r = Response.redirect_temporary("/new_location")
37
+ assert b"http://localhost/new_location" in b"".join(r.content_iterator)
38
+ assert r.get_header("Location") == "http://localhost/new_location"
39
+ assert r.status == "302 Found"
40
+
41
+ def test_redirect_permanent(self):
42
+ with FrescoApp().requestcontext():
43
+ r = Response.redirect_permanent("/new_location")
44
+ assert b"http://localhost/new_location" in b"".join(r.content_iterator)
45
+ assert r.get_header("Location") == "http://localhost/new_location"
46
+ assert r.status == "301 Moved Permanently"
47
+
48
+ def test_redirect_to_view(self):
49
+ app = FrescoApp()
50
+ view = Mock(return_value=Response())
51
+ app.route("/arfle/<barfle:int>", GET, view)
52
+ with app.requestcontext():
53
+ r = Response.redirect(view, barfle=42)
54
+ assert r.get_header("Location") == "http://localhost/arfle/42"
55
+
56
+ def test_redirect_to_view_spec(self):
57
+ app = FrescoApp()
58
+ view = Mock(return_value=Response())
59
+ app.route("/arfle/<barfle:int>", GET, view, name="gloop")
60
+ with app.requestcontext():
61
+ r = Response.redirect("gloop", barfle=42)
62
+ assert r.get_header("Location") == "http://localhost/arfle/42"
63
+
64
+ def test_redirect_to_simple_string(self):
65
+ with FrescoApp().requestcontext():
66
+ r = Response.redirect("gloop")
67
+ assert r.get_header("Location") == "http://localhost/gloop"
68
+
69
+ def test_redirect_raises_RouteNotFound(self):
70
+ view = Mock()
71
+ with FrescoApp().requestcontext():
72
+ with pytest.raises(RouteNotFound):
73
+ Response.redirect("gloop", arfle="a")
74
+ with pytest.raises(RouteNotFound):
75
+ Response.redirect(view)
76
+
77
+ def test_redirect_to_unsafe_url(self):
78
+ with pytest.raises(ValueError):
79
+ with FrescoApp().requestcontext():
80
+ Response.redirect("http://bad.example.com/")
81
+
82
+ def test_redirect_uses_fallback_for_unsafe_url(self):
83
+ app = FrescoApp()
84
+ view = Mock(return_value=Response())
85
+ app.route("/arfle/<barfle:int>", GET, view, name="gloop")
86
+ with app.requestcontext():
87
+ r = Response.redirect("http://bad.example.com/", "gloop", barfle=23)
88
+ assert r.get_header("Location") == "http://localhost/arfle/23"
89
+
90
+ def test_not_found(self):
91
+ with FrescoApp().requestcontext():
92
+ r = Response.not_found()
93
+ assert b"not found" in b"".join(r.content_iterator).lower()
94
+ assert r.status_code == 404
95
+
96
+ def test_error(self):
97
+ with FrescoApp().requestcontext():
98
+ r = Response.error()
99
+ assert b"internal server error" in b"".join(r.content_iterator).lower()
100
+ assert r.status_code == 500
101
+
102
+ def test_method_not_allowed(self):
103
+ with FrescoApp().requestcontext():
104
+ r = Response.method_not_allowed(valid_methods=("PUT", "DELETE"))
105
+ assert b"not allowed" in b"".join(r.content_iterator).lower()
106
+ assert r.status == "405 Method Not Allowed"
107
+ assert r.get_header("Allow") == "PUT,DELETE"
108
+
109
+ def test_unauthorized(self):
110
+ with FrescoApp().requestcontext():
111
+ r = Response.unauthorized(authenticate='myscheme realm="spaghetti"')
112
+ assert r.status == "401 Unauthorized"
113
+ assert r.get_header("WWW-Authenticate") == 'myscheme realm="spaghetti"'
114
+
115
+ def test_unauthorized_basic(self):
116
+ with FrescoApp().requestcontext():
117
+ r = Response.unauthorized_basic(realm="spaghetti")
118
+ assert r.status == "401 Unauthorized"
119
+ assert r.get_header("WWW-Authenticate") == 'Basic realm="spaghetti"'
120
+
121
+ def test_meta_refresh(self):
122
+ with FrescoApp().requestcontext():
123
+ r = Response.meta_refresh("/next_page")
124
+ content = b"".join(r.content_iterator)
125
+ assert b'<a href="http://localhost/next_page">' in content
126
+ assert (
127
+ b'<meta http-equiv="refresh" content="0;'
128
+ b' url=http://localhost/next_page">' in content
129
+ )
130
+ assert r.status == "200 OK"
131
+ assert r.get_header("Content-Type") == "text/html"
132
+
133
+ def test_json(self):
134
+ with FrescoApp().requestcontext():
135
+ r = Response.json(
136
+ {"arfle": "barfle"},
137
+ status=201,
138
+ indent=4,
139
+ separators=(",", ":\t"),
140
+ )
141
+ assert r.status == "201 Created"
142
+ assert r.get_header("Content-Type") == "application/json"
143
+ assert b"".join(r.content_iterator) == b'{\n "arfle":\t"barfle"\n}'
144
+
145
+
146
+ class TestResponse(object):
147
+ def test_unknown_response_header_from_python_symbol(self):
148
+ r = Response(x_myheader="Foo", content_type=None)
149
+ assert r.headers == [("X-Myheader", "Foo")]
150
+
151
+ def test_standard_headers_capitalized_from_symbol(self):
152
+ r = Response(etag="foo")
153
+ assert r.headers == [("ETag", "foo")]
154
+
155
+ r = Response(x_ua_compatible="foo")
156
+ assert r.headers == [("X-UA-Compatible", "foo")]
157
+
158
+ def test_content_type_added_only_if_content(self):
159
+ r = Response(content=[])
160
+ assert "Content-Type" in dict(r.headers)
161
+
162
+ r = Response()
163
+ assert "Content-Type" not in dict(r.headers)
164
+
165
+ r = Response(content=None)
166
+ assert "Content-Type" not in dict(r.headers)
167
+
168
+ def test_add_response_header(self):
169
+ r = Response(x_myheader="Foo")
170
+ r = r.add_header("X-Myheader", "Bar")
171
+ assert r.headers == [("X-Myheader", "Foo"), ("X-Myheader", "Bar")]
172
+
173
+ def test_set_content_type_header(self):
174
+ r = Response(content_type="text/rtf; charset=ISO-8859-1")
175
+ assert r.get_header("Content-Type") == "text/rtf; charset=ISO-8859-1"
176
+
177
+ def test_set_cookie(self):
178
+ r = Response(set_cookie=Cookie(name="fruit", value="banana"))
179
+ assert r.get_header("Set-Cookie") == "fruit=banana;Path=/;SameSite=Lax"
180
+
181
+ def test_delete_cookie(self):
182
+ r = Response().delete_cookie(name="fruit")
183
+ assert r.get_header("Set-Cookie") == (
184
+ "fruit=;Expires=Thu, 01 Jan 1970 00:00:00 GMT;Max-Age=0;Path=/;SameSite=Lax"
185
+ )
186
+
187
+ def test_response_get_headers(self):
188
+ r = Response(
189
+ ["whoa nelly!"],
190
+ content_type="text/plain",
191
+ x_test_header=["1", "2"],
192
+ )
193
+ assert r.get_header("content-type") == "text/plain"
194
+ assert r.get_header("x-test-header") == "1,2"
195
+ assert r.get_header("X-Test-Header") == "1,2"
196
+ assert r.get_header("x-does-not-exist", "boo!") == "boo!"
197
+
198
+ def test_response_buffered_sets_content_length(self):
199
+ r = Response(["whoa nelly!"]).buffered()
200
+ assert r.get_header("content-length") == "11"
201
+
202
+ r = Response(["whoa nélly!"]).buffered()
203
+ assert r.get_header("content-length") == "12"
204
+
205
+ def test_it_encodes_a_unicode_string_according_to_content_type(self):
206
+ r = Response("café", content_type="text/plain; charset=utf-8")
207
+ assert list(r({}, Mock())) == ["café".encode("utf-8")]
208
+ r = Response("café", content_type="text/plain; charset=latin-1")
209
+ assert list(r({}, Mock())) == ["café".encode("latin-1")]
210
+
211
+
212
+ class TestAddVary(object):
213
+ def test_it_adds_a_vary_header(self):
214
+ r = Response(content_type="text/plain").add_vary("Accept-Encoding")
215
+ assert ("Vary", "Accept-Encoding") in r.headers
216
+
217
+ def test_it_extends_an_existing_header(self):
218
+ r = (
219
+ Response(content_type="text/plain")
220
+ .add_vary("Accept-Encoding")
221
+ .add_vary("Cookie")
222
+ )
223
+ assert "Cookie" in r.get_header("vary")
224
+ assert "Accept-Encoding" in r.get_header("vary")
@@ -0,0 +1,223 @@
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 unittest.mock import Mock, call
16
+
17
+ from fresco import routeargs
18
+ from fresco.core import FrescoApp
19
+ from fresco.response import Response
20
+ from fresco.routing import GET
21
+ from fresco.routing import POST
22
+ from fresco.routing import Route
23
+
24
+
25
+ class TestRouteArg(object):
26
+ def test_routekwarg_configured(self):
27
+ A = routeargs.RouteArg()
28
+ route = Route("/", GET, lambda r: None, x=A)
29
+ assert A.route is route
30
+ assert A.name == "x"
31
+
32
+ def test_routekwarg_value_passed(self):
33
+ view = Mock(return_value=Response())
34
+ routekwarg = Mock(spec=routeargs.RouteArg)
35
+ routekwarg.return_value = "xyzzy"
36
+
37
+ app = FrescoApp()
38
+ app.route("/", GET, view, x=routekwarg)
39
+ with app.requestcontext("/") as c:
40
+ app.view()
41
+ assert routekwarg.call_args_list == [call(c.request)]
42
+ assert view.call_args_list == [call(x="xyzzy")]
43
+
44
+ def test_queryarg_value_passed(self):
45
+ view = Mock(return_value=Response())
46
+ app = FrescoApp()
47
+ app.route("/", GET, view, x=routeargs.QueryArg())
48
+ with app.requestcontext("/?x=foo"):
49
+ app.view()
50
+ assert view.call_args_list == [call(x="foo")]
51
+
52
+ def test_formarg_value_passed(self):
53
+ view = Mock(return_value=Response())
54
+ app = FrescoApp()
55
+ app.route("/", GET, view, x=routeargs.FormArg())
56
+ with app.requestcontext("/?x=foo"):
57
+ app.view()
58
+ assert view.call_args_list == [call(x="foo")]
59
+
60
+ def test_sessionarg_value_passed(self):
61
+ view = Mock(return_value=Response())
62
+ app = FrescoApp()
63
+ app.route("/", GET, view, x=routeargs.SessionArg())
64
+ with app.requestcontext("/?x=foo") as c:
65
+ c.request.environ[c.request.SESSION_ENV_KEY] = {"x": "foo"}
66
+ app.view()
67
+ assert view.call_args_list == [call(x="foo")]
68
+
69
+ def test_cookiearg_value_passed(self):
70
+ view = Mock(return_value=Response())
71
+ app = FrescoApp()
72
+ app.route("/", GET, view, x=routeargs.CookieArg())
73
+ with app.requestcontext("/", HTTP_COOKIE="x=foo"):
74
+ app.view()
75
+ assert view.call_args_list == [call(x="foo")]
76
+
77
+ def test_cookiearg_listvalue_passed(self):
78
+ view = Mock(return_value=Response())
79
+ app = FrescoApp()
80
+ app.route("/", GET, view, x=routeargs.CookieArg([str]))
81
+ with app.requestcontext("/", HTTP_COOKIE="x=foo;x=bar"):
82
+ app.view()
83
+ assert view.call_args_list == [call(x=["foo", "bar"])]
84
+
85
+ def test_requestarg_value_converted(self):
86
+ view = Mock(return_value=Response())
87
+ app = FrescoApp()
88
+ app.route("/", GET, view, x=routeargs.FormArg(float))
89
+ with app.requestcontext("/?x=0"):
90
+ app.view()
91
+ assert view.call_args_list == [call(x=0.0)]
92
+
93
+ def test_it_converts_lists(self):
94
+ arg = routeargs.RequestArg(converter=[str])
95
+ arg.configure(None, "foo")
96
+ with FrescoApp().requestcontext("/?foo=a&foo=b") as r:
97
+ assert arg(r.request) == ["a", "b"]
98
+
99
+ def test_requestarg_default_value(self):
100
+ view = Mock(return_value=Response())
101
+ app = FrescoApp()
102
+ app.route("/", GET, view, x=routeargs.FormArg(default="d"))
103
+ with app.requestcontext("/"):
104
+ app.view()
105
+ assert view.call_args_list == [call(x="d")]
106
+
107
+ def test_it_doesnt_convert_default_values(self):
108
+ view = Mock(return_value=Response())
109
+ app = FrescoApp()
110
+ app.route("/", GET, view, x=routeargs.FormArg(int, default=None))
111
+ with app.requestcontext("/"):
112
+ app.view()
113
+ assert view.call_args_list == [call(x=None)]
114
+
115
+ def test_access_to_missing_requestarg_returns_badrequest(self):
116
+ def view(x):
117
+ x == 0
118
+
119
+ app = FrescoApp()
120
+ app.route("/", GET, view, x=routeargs.FormArg())
121
+ with app.requestcontext("/"):
122
+ assert "Bad Request" in app.view().status
123
+
124
+ def test_missing_requestarg_with_conversion_returns_badrequest(self):
125
+ def view(x):
126
+ x == 0
127
+
128
+ app = FrescoApp()
129
+ app.route("/", GET, view, x=routeargs.FormArg(int))
130
+ with app.requestcontext("/"):
131
+ assert "Bad Request" in app.view().status
132
+
133
+ def test_routeargs_work_as_positional_arguments(self):
134
+ view = Mock(return_value=Response())
135
+ app = FrescoApp()
136
+ app.route("/", GET, view, args=[routeargs.FormArg(key="x")])
137
+ with app.requestcontext("/?x=foo"):
138
+ app.view()
139
+ assert view.call_args_list == [call("foo")]
140
+
141
+ def test_routearg_classes_are_auto_instantiated(self):
142
+ view = Mock(return_value=Response())
143
+ app = FrescoApp()
144
+ app.route("/", GET, view, x=routeargs.FormArg)
145
+ with app.requestcontext("/?x=foo"):
146
+ app.view()
147
+ assert view.call_args_list == [call(x="foo")]
148
+
149
+
150
+ class TestRequestObject(object):
151
+ def test_it_passes_the_request(self):
152
+ view = Mock(return_value=Response())
153
+ app = FrescoApp()
154
+ app.route("/", GET, view, x=routeargs.RequestObject())
155
+ with app.requestcontext("/") as c:
156
+ app.view()
157
+ x = view.call_args[1]["x"]
158
+ assert x is c.request
159
+
160
+
161
+ class Test_routearg(object):
162
+ def test_it_calls_func(object):
163
+ view = Mock(return_value=Response())
164
+ argfunc = Mock()
165
+ app = FrescoApp()
166
+ app.route("/", GET, view, x=routeargs.routearg(argfunc))
167
+ with app.requestcontext("/"):
168
+ app.view()
169
+ assert argfunc.call_count == 1, argfunc.call_count
170
+ assert view.call_args[1]["x"] is argfunc()
171
+
172
+ def test_it_passes_additional_args(object):
173
+ view = Mock(return_value=Response())
174
+ argfunc = Mock()
175
+ app = FrescoApp()
176
+ app.route("/", GET, view, x=routeargs.routearg(argfunc, "x", y=42))
177
+ with app.requestcontext("/") as c:
178
+ app.view()
179
+ assert argfunc.call_args == ((c.request, "x"), {"y": 42})
180
+ assert view.call_args[1]["x"] is argfunc()
181
+
182
+
183
+ class TestFormData(object):
184
+ def test_it_passes_the_form_dict(self):
185
+ view = Mock(return_value=Response())
186
+ app = FrescoApp()
187
+ app.route("/", GET, view, x=routeargs.FormData())
188
+ with app.requestcontext("/") as c:
189
+ app.view()
190
+ x = view.call_args[1]["x"]
191
+ assert x is c.request.form
192
+
193
+
194
+ class TestJSONPayload(object):
195
+ def test_it_passes_json_data(self):
196
+ view = Mock(return_value=Response())
197
+ app = FrescoApp()
198
+ app.route("/", POST, view, x=routeargs.JSONPayload())
199
+ with app.requestcontext_post(
200
+ "/", data=b'{"foo":"bar"}', CONTENT_TYPE="application/json"
201
+ ):
202
+ app.view()
203
+ x = view.call_args[1]["x"]
204
+ assert x == {"foo": "bar"}
205
+
206
+ def test_it_returns_bad_request_on_invalid_payload(self):
207
+ view = Mock(return_value=Response())
208
+ app = FrescoApp()
209
+ app.route("/", POST, view, x=routeargs.JSONPayload())
210
+ with app.requestcontext_post(
211
+ "/", data=b"invalid json!", CONTENT_TYPE="application/json"
212
+ ):
213
+ r = app.view()
214
+ assert r.status_code == 400
215
+
216
+ def test_it_returns_a_default_value(self):
217
+ view = Mock(return_value=Response())
218
+ app = FrescoApp()
219
+ app.route("/", POST, view, x=routeargs.JSONPayload(default="default value"))
220
+ with app.requestcontext_post("/"):
221
+ app.view()
222
+ x = view.call_args[1]["x"]
223
+ assert x == "default value"