FlaskTester 4.3__tar.gz → 5.1__tar.gz
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.
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.egg-info/PKG-INFO +21 -19
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.egg-info/SOURCES.txt +1 -3
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.egg-info/requires.txt +6 -5
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.py +22 -2
- flasktester-5.1/MANIFEST.in +1 -0
- {flasktester-4.3 → flasktester-5.1}/PKG-INFO +21 -19
- {flasktester-4.3 → flasktester-5.1}/README.md +12 -10
- {flasktester-4.3 → flasktester-5.1}/pyproject.toml +4 -6
- flasktester-4.3/MANIFEST.in +0 -1
- flasktester-4.3/tests/test_app.py +0 -385
- flasktester-4.3/tests/test_app2.py +0 -37
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.egg-info/dependency_links.txt +0 -0
- {flasktester-4.3 → flasktester-5.1}/FlaskTester.egg-info/top_level.txt +0 -0
- {flasktester-4.3 → flasktester-5.1}/LICENSE +0 -0
- {flasktester-4.3 → flasktester-5.1}/setup.cfg +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: FlaskTester
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.1
|
|
4
4
|
Summary: Pytest fixtures for Flask internal and external authenticated tests
|
|
5
5
|
Author-email: Fabien Coelho <flask.tester@coelho.net>
|
|
6
|
-
License: CC0
|
|
6
|
+
License-Expression: CC0-1.0
|
|
7
7
|
Project-URL: repository, https://github.com/zx80/flask-tester
|
|
8
8
|
Project-URL: documentation, https://zx80.github.io/flask-tester/
|
|
9
9
|
Project-URL: issues, https://github.com/zx80/flask-tester/issues
|
|
10
10
|
Project-URL: package, https://pypi.org/project/FlaskTester/
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
|
14
13
|
Classifier: Programming Language :: Python
|
|
15
14
|
Classifier: Environment :: Web Environment
|
|
16
15
|
Classifier: Framework :: Flask
|
|
@@ -26,13 +25,9 @@ Requires-Dist: pyright; extra == "dev"
|
|
|
26
25
|
Requires-Dist: ruff; extra == "dev"
|
|
27
26
|
Requires-Dist: coverage; extra == "dev"
|
|
28
27
|
Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
|
|
29
|
-
Requires-Dist: build; extra == "dev"
|
|
30
|
-
Requires-Dist: twine; extra == "dev"
|
|
31
|
-
Requires-Dist: wheel; extra == "dev"
|
|
32
28
|
Requires-Dist: types-flask; extra == "dev"
|
|
33
29
|
Requires-Dist: types-requests; extra == "dev"
|
|
34
|
-
Requires-Dist: FlaskSimpleAuth>=
|
|
35
|
-
Requires-Dist: passlib; extra == "dev"
|
|
30
|
+
Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
|
|
36
31
|
Requires-Dist: bcrypt; extra == "dev"
|
|
37
32
|
Requires-Dist: pydantic; extra == "dev"
|
|
38
33
|
Provides-Extra: doc
|
|
@@ -41,6 +36,11 @@ Requires-Dist: sphinx_rtd_theme; extra == "doc"
|
|
|
41
36
|
Requires-Dist: sphinx-autoapi; extra == "doc"
|
|
42
37
|
Requires-Dist: sphinx-lint; extra == "doc"
|
|
43
38
|
Requires-Dist: myst_parser; extra == "doc"
|
|
39
|
+
Provides-Extra: pub
|
|
40
|
+
Requires-Dist: build; extra == "pub"
|
|
41
|
+
Requires-Dist: twine; extra == "pub"
|
|
42
|
+
Requires-Dist: wheel; extra == "pub"
|
|
43
|
+
Dynamic: license-file
|
|
44
44
|
|
|
45
45
|
# FlaskTester - Pytest fixtures for Flask internal and external authenticated tests
|
|
46
46
|
|
|
@@ -53,7 +53,7 @@ Only one set of tests is needed, switching from internal to external is
|
|
|
53
53
|
achieved by setting an environment variable.
|
|
54
54
|
|
|
55
55
|

|
|
56
|
-

|
|
57
57
|

|
|
58
58
|

|
|
59
59
|

|
|
@@ -75,18 +75,20 @@ import pytest
|
|
|
75
75
|
from FlaskTester import ft_authenticator, ft_client
|
|
76
76
|
import secret
|
|
77
77
|
|
|
78
|
+
def authHook(api, user: str, pwd: str|None):
|
|
79
|
+
if pwd is not None: # get a token when a login/password is provided
|
|
80
|
+
res = api.get("/login", login=user, auth="basic", status=200)
|
|
81
|
+
api.setToken(user, res.json["token"])
|
|
82
|
+
else: # remove token
|
|
83
|
+
api.setToken(user, None)
|
|
84
|
+
|
|
78
85
|
@pytest.fixture
|
|
79
86
|
def app(ft_client):
|
|
87
|
+
# register authentication hook
|
|
88
|
+
ft_client.setHook(authHook)
|
|
80
89
|
# add test passwords for Calvin and Hobbes (must be consistent with app!)
|
|
81
90
|
ft_client.setPass("calvin", secret.PASSES["calvin"])
|
|
82
91
|
ft_client.setPass("hobbes", secret.PASSES["hobbes"])
|
|
83
|
-
# get user tokens, assume json result {"token": "<token-value>"}
|
|
84
|
-
res = ft_client.get("/login", login="calvin", auth="basic", status=200)
|
|
85
|
-
assert res.is_json
|
|
86
|
-
ft_client.setToken("calvin", res.json["token"])
|
|
87
|
-
res = ft_client.post("/login", login="hobbes", auth="param", status=201)
|
|
88
|
-
assert res.is_json
|
|
89
|
-
ft_client.setToken("hobbes", res.json["token"])
|
|
90
92
|
# also set a cookie
|
|
91
93
|
ft_client.setCookie("hobbes", "lang", "fr")
|
|
92
94
|
ft_client.setCookie("calvin", "lang", "en")
|
|
@@ -102,13 +104,13 @@ def test_app_admin(app):
|
|
|
102
104
|
assert 'not in group "ADMIN"' in res.text
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
This can be run against a
|
|
107
|
+
This can be run against a local or remote server:
|
|
106
108
|
|
|
107
109
|
```shell
|
|
108
110
|
export TEST_SEED="some-random-data" # shared test seed
|
|
109
111
|
flask --app app:app run & # start flask app
|
|
110
112
|
pid=$! # keep pid
|
|
111
|
-
export FLASK_TESTER_APP="http://localhost:5000" # set app local
|
|
113
|
+
export FLASK_TESTER_APP="http://localhost:5000" # set app url, local or remote
|
|
112
114
|
pytest test.py # run external tests
|
|
113
115
|
kill $pid # stop app with pid
|
|
114
116
|
```
|
|
@@ -7,13 +7,9 @@ pyright
|
|
|
7
7
|
ruff
|
|
8
8
|
coverage
|
|
9
9
|
pymarkdownlnt!=0.9.5
|
|
10
|
-
build
|
|
11
|
-
twine
|
|
12
|
-
wheel
|
|
13
10
|
types-flask
|
|
14
11
|
types-requests
|
|
15
|
-
FlaskSimpleAuth>=
|
|
16
|
-
passlib
|
|
12
|
+
FlaskSimpleAuth>=35.0
|
|
17
13
|
bcrypt
|
|
18
14
|
pydantic
|
|
19
15
|
|
|
@@ -23,3 +19,8 @@ sphinx_rtd_theme
|
|
|
23
19
|
sphinx-autoapi
|
|
24
20
|
sphinx-lint
|
|
25
21
|
myst_parser
|
|
22
|
+
|
|
23
|
+
[pub]
|
|
24
|
+
build
|
|
25
|
+
twine
|
|
26
|
+
wheel
|
|
@@ -6,7 +6,8 @@ PYTEST_DONT_REWRITE: local assertions are really that, not pytest assertions.
|
|
|
6
6
|
import os
|
|
7
7
|
import io
|
|
8
8
|
import re
|
|
9
|
-
|
|
9
|
+
# Python >= 3.11: Self
|
|
10
|
+
from typing import Any, Callable
|
|
10
11
|
import importlib
|
|
11
12
|
import logging
|
|
12
13
|
import pytest # for explicit fail calls, see _pytestFail
|
|
@@ -89,6 +90,9 @@ class Authenticator:
|
|
|
89
90
|
_AUTH_SCHEMES.update(_TOKEN_SCHEMES)
|
|
90
91
|
_AUTH_SCHEMES.update(_PASS_SCHEMES)
|
|
91
92
|
|
|
93
|
+
# authenticator login/pass hook
|
|
94
|
+
_AuthHook = Callable[[str, str|None], None]
|
|
95
|
+
|
|
92
96
|
def __init__(self,
|
|
93
97
|
allow: list[str] = ["bearer", "basic", "param", "none"],
|
|
94
98
|
# parameter names for "basic" and "param"
|
|
@@ -124,6 +128,9 @@ class Authenticator:
|
|
|
124
128
|
assert ptype in ("json", "data")
|
|
125
129
|
self._ptype = ptype
|
|
126
130
|
|
|
131
|
+
# _AuthHook|None, but python cannot stand it:-(
|
|
132
|
+
self._auth_hook: Any = None
|
|
133
|
+
|
|
127
134
|
# password and token credentials, cookies
|
|
128
135
|
self._passes: dict[str, str] = {}
|
|
129
136
|
self._tokens: dict[str, str] = {}
|
|
@@ -138,6 +145,10 @@ class Authenticator:
|
|
|
138
145
|
assert isinstance(val, str)
|
|
139
146
|
store[key] = val
|
|
140
147
|
|
|
148
|
+
def setHook(self, hook: _AuthHook):
|
|
149
|
+
"""Set on password hook."""
|
|
150
|
+
self._auth_hook = hook
|
|
151
|
+
|
|
141
152
|
def setPass(self, login: str, pw: str|None):
|
|
142
153
|
"""Associate a password to a user.
|
|
143
154
|
|
|
@@ -146,6 +157,7 @@ class Authenticator:
|
|
|
146
157
|
if not self._has_pass:
|
|
147
158
|
raise AuthError("cannot set password, no password scheme allowed")
|
|
148
159
|
self._set(login, pw, self._passes)
|
|
160
|
+
_ = self._auth_hook and self._auth_hook(login, pw)
|
|
149
161
|
|
|
150
162
|
def setPasses(self, pws: list[str]):
|
|
151
163
|
"""Associate a list of *login:password*."""
|
|
@@ -298,11 +310,19 @@ class Client:
|
|
|
298
310
|
:param default_login: When ``login`` is not set.
|
|
299
311
|
"""
|
|
300
312
|
|
|
313
|
+
# client login/pass hook (with mypy workaround)
|
|
314
|
+
# Python >= 3.11: Self
|
|
315
|
+
AuthHook = Callable[[Any, str, str|None], None] # type: ignore
|
|
316
|
+
|
|
301
317
|
def __init__(self, auth: Authenticator, default_login: str|None = None):
|
|
302
318
|
self._auth = auth
|
|
303
319
|
self._cookies: dict[str, dict[str, str]] = {} # login -> name -> value
|
|
304
320
|
self._default_login = default_login
|
|
305
321
|
|
|
322
|
+
def setHook(self, hook: AuthHook):
|
|
323
|
+
"""Set on password hook."""
|
|
324
|
+
self._auth.setHook(lambda u, p: hook(self, u, p))
|
|
325
|
+
|
|
306
326
|
def setToken(self, login: str, token: str|None):
|
|
307
327
|
"""Associate a token to a login, *None* to remove."""
|
|
308
328
|
self._auth.setToken(login, token)
|
|
@@ -603,7 +623,7 @@ def _ft_client(authenticator):
|
|
|
603
623
|
# none found
|
|
604
624
|
if not app:
|
|
605
625
|
raise FlaskTesterError(f"cannot find Flask app in {pkg_name} ({test_app})")
|
|
606
|
-
client = FlaskClient(authenticator, app.test_client(), default_login)
|
|
626
|
+
client = FlaskClient(authenticator, app.test_client(), default_login) # type: ignore
|
|
607
627
|
|
|
608
628
|
return client
|
|
609
629
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude docs/* tests/* */Makefile
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: FlaskTester
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.1
|
|
4
4
|
Summary: Pytest fixtures for Flask internal and external authenticated tests
|
|
5
5
|
Author-email: Fabien Coelho <flask.tester@coelho.net>
|
|
6
|
-
License: CC0
|
|
6
|
+
License-Expression: CC0-1.0
|
|
7
7
|
Project-URL: repository, https://github.com/zx80/flask-tester
|
|
8
8
|
Project-URL: documentation, https://zx80.github.io/flask-tester/
|
|
9
9
|
Project-URL: issues, https://github.com/zx80/flask-tester/issues
|
|
10
10
|
Project-URL: package, https://pypi.org/project/FlaskTester/
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
|
14
13
|
Classifier: Programming Language :: Python
|
|
15
14
|
Classifier: Environment :: Web Environment
|
|
16
15
|
Classifier: Framework :: Flask
|
|
@@ -26,13 +25,9 @@ Requires-Dist: pyright; extra == "dev"
|
|
|
26
25
|
Requires-Dist: ruff; extra == "dev"
|
|
27
26
|
Requires-Dist: coverage; extra == "dev"
|
|
28
27
|
Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
|
|
29
|
-
Requires-Dist: build; extra == "dev"
|
|
30
|
-
Requires-Dist: twine; extra == "dev"
|
|
31
|
-
Requires-Dist: wheel; extra == "dev"
|
|
32
28
|
Requires-Dist: types-flask; extra == "dev"
|
|
33
29
|
Requires-Dist: types-requests; extra == "dev"
|
|
34
|
-
Requires-Dist: FlaskSimpleAuth>=
|
|
35
|
-
Requires-Dist: passlib; extra == "dev"
|
|
30
|
+
Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
|
|
36
31
|
Requires-Dist: bcrypt; extra == "dev"
|
|
37
32
|
Requires-Dist: pydantic; extra == "dev"
|
|
38
33
|
Provides-Extra: doc
|
|
@@ -41,6 +36,11 @@ Requires-Dist: sphinx_rtd_theme; extra == "doc"
|
|
|
41
36
|
Requires-Dist: sphinx-autoapi; extra == "doc"
|
|
42
37
|
Requires-Dist: sphinx-lint; extra == "doc"
|
|
43
38
|
Requires-Dist: myst_parser; extra == "doc"
|
|
39
|
+
Provides-Extra: pub
|
|
40
|
+
Requires-Dist: build; extra == "pub"
|
|
41
|
+
Requires-Dist: twine; extra == "pub"
|
|
42
|
+
Requires-Dist: wheel; extra == "pub"
|
|
43
|
+
Dynamic: license-file
|
|
44
44
|
|
|
45
45
|
# FlaskTester - Pytest fixtures for Flask internal and external authenticated tests
|
|
46
46
|
|
|
@@ -53,7 +53,7 @@ Only one set of tests is needed, switching from internal to external is
|
|
|
53
53
|
achieved by setting an environment variable.
|
|
54
54
|
|
|
55
55
|

|
|
56
|
-

|
|
57
57
|

|
|
58
58
|

|
|
59
59
|

|
|
@@ -75,18 +75,20 @@ import pytest
|
|
|
75
75
|
from FlaskTester import ft_authenticator, ft_client
|
|
76
76
|
import secret
|
|
77
77
|
|
|
78
|
+
def authHook(api, user: str, pwd: str|None):
|
|
79
|
+
if pwd is not None: # get a token when a login/password is provided
|
|
80
|
+
res = api.get("/login", login=user, auth="basic", status=200)
|
|
81
|
+
api.setToken(user, res.json["token"])
|
|
82
|
+
else: # remove token
|
|
83
|
+
api.setToken(user, None)
|
|
84
|
+
|
|
78
85
|
@pytest.fixture
|
|
79
86
|
def app(ft_client):
|
|
87
|
+
# register authentication hook
|
|
88
|
+
ft_client.setHook(authHook)
|
|
80
89
|
# add test passwords for Calvin and Hobbes (must be consistent with app!)
|
|
81
90
|
ft_client.setPass("calvin", secret.PASSES["calvin"])
|
|
82
91
|
ft_client.setPass("hobbes", secret.PASSES["hobbes"])
|
|
83
|
-
# get user tokens, assume json result {"token": "<token-value>"}
|
|
84
|
-
res = ft_client.get("/login", login="calvin", auth="basic", status=200)
|
|
85
|
-
assert res.is_json
|
|
86
|
-
ft_client.setToken("calvin", res.json["token"])
|
|
87
|
-
res = ft_client.post("/login", login="hobbes", auth="param", status=201)
|
|
88
|
-
assert res.is_json
|
|
89
|
-
ft_client.setToken("hobbes", res.json["token"])
|
|
90
92
|
# also set a cookie
|
|
91
93
|
ft_client.setCookie("hobbes", "lang", "fr")
|
|
92
94
|
ft_client.setCookie("calvin", "lang", "en")
|
|
@@ -102,13 +104,13 @@ def test_app_admin(app):
|
|
|
102
104
|
assert 'not in group "ADMIN"' in res.text
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
This can be run against a
|
|
107
|
+
This can be run against a local or remote server:
|
|
106
108
|
|
|
107
109
|
```shell
|
|
108
110
|
export TEST_SEED="some-random-data" # shared test seed
|
|
109
111
|
flask --app app:app run & # start flask app
|
|
110
112
|
pid=$! # keep pid
|
|
111
|
-
export FLASK_TESTER_APP="http://localhost:5000" # set app local
|
|
113
|
+
export FLASK_TESTER_APP="http://localhost:5000" # set app url, local or remote
|
|
112
114
|
pytest test.py # run external tests
|
|
113
115
|
kill $pid # stop app with pid
|
|
114
116
|
```
|
|
@@ -9,7 +9,7 @@ Only one set of tests is needed, switching from internal to external is
|
|
|
9
9
|
achieved by setting an environment variable.
|
|
10
10
|
|
|
11
11
|

|
|
12
|
-

|
|
13
13
|

|
|
14
14
|

|
|
15
15
|

|
|
@@ -31,18 +31,20 @@ import pytest
|
|
|
31
31
|
from FlaskTester import ft_authenticator, ft_client
|
|
32
32
|
import secret
|
|
33
33
|
|
|
34
|
+
def authHook(api, user: str, pwd: str|None):
|
|
35
|
+
if pwd is not None: # get a token when a login/password is provided
|
|
36
|
+
res = api.get("/login", login=user, auth="basic", status=200)
|
|
37
|
+
api.setToken(user, res.json["token"])
|
|
38
|
+
else: # remove token
|
|
39
|
+
api.setToken(user, None)
|
|
40
|
+
|
|
34
41
|
@pytest.fixture
|
|
35
42
|
def app(ft_client):
|
|
43
|
+
# register authentication hook
|
|
44
|
+
ft_client.setHook(authHook)
|
|
36
45
|
# add test passwords for Calvin and Hobbes (must be consistent with app!)
|
|
37
46
|
ft_client.setPass("calvin", secret.PASSES["calvin"])
|
|
38
47
|
ft_client.setPass("hobbes", secret.PASSES["hobbes"])
|
|
39
|
-
# get user tokens, assume json result {"token": "<token-value>"}
|
|
40
|
-
res = ft_client.get("/login", login="calvin", auth="basic", status=200)
|
|
41
|
-
assert res.is_json
|
|
42
|
-
ft_client.setToken("calvin", res.json["token"])
|
|
43
|
-
res = ft_client.post("/login", login="hobbes", auth="param", status=201)
|
|
44
|
-
assert res.is_json
|
|
45
|
-
ft_client.setToken("hobbes", res.json["token"])
|
|
46
48
|
# also set a cookie
|
|
47
49
|
ft_client.setCookie("hobbes", "lang", "fr")
|
|
48
50
|
ft_client.setCookie("calvin", "lang", "en")
|
|
@@ -58,13 +60,13 @@ def test_app_admin(app):
|
|
|
58
60
|
assert 'not in group "ADMIN"' in res.text
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
This can be run against a
|
|
63
|
+
This can be run against a local or remote server:
|
|
62
64
|
|
|
63
65
|
```shell
|
|
64
66
|
export TEST_SEED="some-random-data" # shared test seed
|
|
65
67
|
flask --app app:app run & # start flask app
|
|
66
68
|
pid=$! # keep pid
|
|
67
|
-
export FLASK_TESTER_APP="http://localhost:5000" # set app local
|
|
69
|
+
export FLASK_TESTER_APP="http://localhost:5000" # set app url, local or remote
|
|
68
70
|
pytest test.py # run external tests
|
|
69
71
|
kill $pid # stop app with pid
|
|
70
72
|
```
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "FlaskTester"
|
|
7
|
-
version = "
|
|
7
|
+
version = "5.1"
|
|
8
8
|
authors = [ { name = "Fabien Coelho", email = "flask.tester@coelho.net" } ]
|
|
9
9
|
description = "Pytest fixtures for Flask internal and external authenticated tests"
|
|
10
10
|
readme = "README.md"
|
|
11
|
-
license =
|
|
11
|
+
license = "CC0-1.0"
|
|
12
12
|
requires-python = ">=3.10"
|
|
13
13
|
dependencies = [
|
|
14
14
|
"requests",
|
|
@@ -17,7 +17,6 @@ dependencies = [
|
|
|
17
17
|
classifiers = [
|
|
18
18
|
"Development Status :: 5 - Production/Stable",
|
|
19
19
|
"Intended Audience :: Developers",
|
|
20
|
-
"License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
|
|
21
20
|
"Programming Language :: Python",
|
|
22
21
|
"Environment :: Web Environment",
|
|
23
22
|
"Framework :: Flask",
|
|
@@ -35,13 +34,12 @@ package = "https://pypi.org/project/FlaskTester/"
|
|
|
35
34
|
dev = [
|
|
36
35
|
# static checks…
|
|
37
36
|
"mypy", "pyright", "ruff", "coverage", "pymarkdownlnt!=0.9.5",
|
|
38
|
-
# packaging
|
|
39
|
-
"build", "twine", "wheel",
|
|
40
37
|
# tests
|
|
41
|
-
"types-flask", "types-requests", "FlaskSimpleAuth>=
|
|
38
|
+
"types-flask", "types-requests", "FlaskSimpleAuth>=35.0", "bcrypt", "pydantic"
|
|
42
39
|
]
|
|
43
40
|
# documentation generation
|
|
44
41
|
doc = ["sphinx", "sphinx_rtd_theme", "sphinx-autoapi", "sphinx-lint", "myst_parser"]
|
|
42
|
+
pub = ["build", "twine", "wheel"]
|
|
45
43
|
|
|
46
44
|
[tool.setuptools]
|
|
47
45
|
py-modules = [ "FlaskTester" ]
|
flasktester-4.3/MANIFEST.in
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
exclude docs/* */Makefile
|
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import pytest
|
|
3
|
-
import FlaskSimpleAuth as fsa
|
|
4
|
-
import FlaskTester as ft
|
|
5
|
-
from FlaskTester import ft_client, ft_authenticator
|
|
6
|
-
import secret
|
|
7
|
-
import http.server as htsv
|
|
8
|
-
import threading
|
|
9
|
-
import io
|
|
10
|
-
import logging
|
|
11
|
-
import model
|
|
12
|
-
|
|
13
|
-
logging.basicConfig(level=logging.INFO)
|
|
14
|
-
# logging.basicConfig(level=logging.DEBUG)
|
|
15
|
-
# log = logging.getLogger("test")
|
|
16
|
-
|
|
17
|
-
# set authn for ft_authenticator
|
|
18
|
-
os.environ.update(
|
|
19
|
-
FLASK_TESTER_TESTING="*",
|
|
20
|
-
FLASK_TESTER_ALLOW="bearer basic param none",
|
|
21
|
-
FLASK_TESTER_AUTH=",".join(f"{l}:{p}" for l, p in secret.PASSES.items()),
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
def test_sanity():
|
|
25
|
-
# must provide url or package of Flask application to test
|
|
26
|
-
assert "FLASK_TESTER_APP" in os.environ
|
|
27
|
-
# log.debug(f"TEST_SEED={os.environ.get('TEST_SEED')}")
|
|
28
|
-
|
|
29
|
-
# example from README.md
|
|
30
|
-
@pytest.fixture
|
|
31
|
-
def app(ft_client):
|
|
32
|
-
# add test passwords for Calvin and Hobbes (must be consistent with app!)
|
|
33
|
-
ft_client.setPass("calvin", secret.PASSES["calvin"])
|
|
34
|
-
ft_client.setPass("hobbes", secret.PASSES["hobbes"])
|
|
35
|
-
# get user tokens, assume json result {"token": "<token-value>"}
|
|
36
|
-
res = ft_client.get("/login", login="calvin", auth="basic", status=200)
|
|
37
|
-
assert res.is_json
|
|
38
|
-
ft_client.setToken("calvin", res.json["token"])
|
|
39
|
-
res = ft_client.post("/login", login="hobbes", auth="param", status=201)
|
|
40
|
-
assert res.is_json
|
|
41
|
-
ft_client.setToken("hobbes", res.json["token"])
|
|
42
|
-
# also set a cookie
|
|
43
|
-
ft_client.setCookie("hobbes", "lang", "fr")
|
|
44
|
-
ft_client.setCookie("calvin", "lang", "en")
|
|
45
|
-
# return working client
|
|
46
|
-
yield ft_client
|
|
47
|
-
|
|
48
|
-
def test_app_admin(app): # GET /admin
|
|
49
|
-
app.get("/admin", login=None, status=401)
|
|
50
|
-
for auth in ["bearer", "basic", "param"]:
|
|
51
|
-
res = app.get("/admin", login="calvin", auth=auth, status=200)
|
|
52
|
-
assert res.json["user"] == "calvin" and res.json["isadmin"]
|
|
53
|
-
res = app.get("/admin", login="hobbes", auth=auth, status=403)
|
|
54
|
-
assert 'not in group "ADMIN"' in res.text
|
|
55
|
-
|
|
56
|
-
def test_app_who_am_i(app): # GET /who-am-i
|
|
57
|
-
app.get("/who-am-i", login=None, status=401)
|
|
58
|
-
for auth in ["bearer", "basic", "param"]:
|
|
59
|
-
res = app.get("/who-am-i", login="calvin", auth=auth, status=200)
|
|
60
|
-
assert res.json["lang"] == "en" and res.json["isadmin"]
|
|
61
|
-
res = app.get("/who-am-i", login="hobbes", auth=auth, status=200)
|
|
62
|
-
assert res.json["lang"] == "fr" and not res.json["isadmin"]
|
|
63
|
-
|
|
64
|
-
@pytest.fixture
|
|
65
|
-
def api(ft_client):
|
|
66
|
-
# set a default
|
|
67
|
-
ft_client._default_login = "calvin"
|
|
68
|
-
# bad password and token
|
|
69
|
-
ft_client.setPass("moe", None)
|
|
70
|
-
ft_client.setToken("moe", None)
|
|
71
|
-
ft_client.setPass("moe", "bad password")
|
|
72
|
-
ft_client.setToken("moe", "bad token")
|
|
73
|
-
# set language cookies
|
|
74
|
-
ft_client.setCookie("calvin", "lang", "en")
|
|
75
|
-
ft_client.setCookie("hobbes", "lang", "fr")
|
|
76
|
-
# get valid tokens using password authn on /login
|
|
77
|
-
res = ft_client.get("/login", login="calvin", auth="basic", status=200)
|
|
78
|
-
assert res.json["user"] == "calvin"
|
|
79
|
-
ft_client._auth.setToken("calvin", res.json["token"])
|
|
80
|
-
res = ft_client.post("/login", login="hobbes", auth="param", status=201, data={})
|
|
81
|
-
assert res.json["user"] == "hobbes"
|
|
82
|
-
ft_client._auth.setToken("hobbes", res.json["token"])
|
|
83
|
-
res = ft_client.post("/login", login="susie", auth="param", status=201, json={})
|
|
84
|
-
assert res.json["user"] == "susie"
|
|
85
|
-
ft_client._auth.setToken("susie", res.json["token"])
|
|
86
|
-
ft_client.get("/login", login="calvin", auth="none", status=401)
|
|
87
|
-
# check that token auth and cookie is ok
|
|
88
|
-
res = ft_client.get("/who-am-i", login="calvin", status=200, auth="bearer")
|
|
89
|
-
assert res.json["user"] == "calvin" and res.json["lang"] == "en"
|
|
90
|
-
res = ft_client.get("/who-am-i", login="hobbes", status=200, auth="bearer")
|
|
91
|
-
assert res.json["user"] == "hobbes" and res.json["lang"] == "fr"
|
|
92
|
-
res = ft_client.get("/who-am-i", login="susie", status=200, auth="bearer")
|
|
93
|
-
assert res.json["user"] == "susie" and res.json["lang"] is None
|
|
94
|
-
# with defaults
|
|
95
|
-
res = ft_client.get("/who-am-i", auth="basic", status=200)
|
|
96
|
-
assert res.json["user"] == "calvin"
|
|
97
|
-
res = ft_client.get("/who-am-i", auth="param", status=200)
|
|
98
|
-
assert res.json["user"] == "calvin"
|
|
99
|
-
res = ft_client.get("/who-am-i", auth="bearer", status=200)
|
|
100
|
-
assert res.json["user"] == "calvin"
|
|
101
|
-
res = ft_client.get("/who-am-i", status=200)
|
|
102
|
-
assert res.json["user"] == "calvin"
|
|
103
|
-
# ready for testing routes
|
|
104
|
-
yield ft_client
|
|
105
|
-
|
|
106
|
-
def test_admin(api):
|
|
107
|
-
# check authentication schemes
|
|
108
|
-
for auth in (None, "basic", "param", "bearer"):
|
|
109
|
-
api.get("/admin", status=200, login="calvin", auth=auth)
|
|
110
|
-
api.get("/admin", 200, login="susie", auth=auth)
|
|
111
|
-
api.get("/admin", 403, login="hobbes", auth=auth)
|
|
112
|
-
api.get("/admin", 401, login="moe", auth=auth)
|
|
113
|
-
api.get("/admin", 401, login=None, auth=auth)
|
|
114
|
-
# test status error
|
|
115
|
-
try:
|
|
116
|
-
api.get("/admin", status=599, login="calvin", auth=auth)
|
|
117
|
-
pytest.fail("assert on status must fail") # pragma: no cover
|
|
118
|
-
except ft._AssertError as e:
|
|
119
|
-
assert "200" in str(e)
|
|
120
|
-
# test content error
|
|
121
|
-
try:
|
|
122
|
-
api.get("/admin", status=200, login="calvin", auth=auth, content="NOT THERE")
|
|
123
|
-
pytest.fail("assert on content must fail") # pragma: no cover
|
|
124
|
-
except ft._AssertError as e:
|
|
125
|
-
assert "NOT THERE" in str(e)
|
|
126
|
-
|
|
127
|
-
def test_errors(api):
|
|
128
|
-
# these schemes are not allowed
|
|
129
|
-
for scheme in ("header", "cookie", "fake", "tparam"):
|
|
130
|
-
try:
|
|
131
|
-
api.get("/login", login="calvin", auth=scheme)
|
|
132
|
-
pytest.fail("must raise an exception") # pragma: no cover
|
|
133
|
-
except ft.AuthError as e:
|
|
134
|
-
assert "auth is not allowed" in str(e)
|
|
135
|
-
try:
|
|
136
|
-
api.get("/login", login="calvin", auth="foobla")
|
|
137
|
-
pytest.fail("must raise an exception") # pragma: no cover
|
|
138
|
-
except ft.AuthError as e:
|
|
139
|
-
assert "unexpected auth" in str(e)
|
|
140
|
-
|
|
141
|
-
def test_methods(api):
|
|
142
|
-
res = api.get("/who-am-i", login="susie", status=200, cookies={"lang": "it"})
|
|
143
|
-
assert res.json["lang"] == "it"
|
|
144
|
-
api.post("/who-am-i", login="hobbes", status=405)
|
|
145
|
-
api.put("/who-am-i", login="hobbes", status=405)
|
|
146
|
-
api.patch("/who-am-i", login="hobbes", status=405)
|
|
147
|
-
api.delete("/who-am-i", login="hobbes", status=405)
|
|
148
|
-
|
|
149
|
-
def test_hello(api):
|
|
150
|
-
res = api.get("/hello", login="calvin", auth="none", status=200)
|
|
151
|
-
assert res.json["lang"] == "en" and res.json["hello"] == "Hi"
|
|
152
|
-
assert res.headers["FSA-User"] == "None (None)"
|
|
153
|
-
res = api.get("/hello", login="hobbes", auth="none", status=200)
|
|
154
|
-
assert res.json["lang"] == "fr" and res.json["hello"] == "Salut"
|
|
155
|
-
assert res.headers["FSA-User"] == "None (None)"
|
|
156
|
-
res = api.get("/hello", login="susie", auth="none", status=200, cookies={"lang": "it"})
|
|
157
|
-
assert res.json["lang"] == "it" and res.json["hello"] == "Ciao"
|
|
158
|
-
assert res.headers["FSA-User"] == "None (None)"
|
|
159
|
-
res = api.get("/hello", login="moe", auth="none", status=200)
|
|
160
|
-
assert res.json["lang"] == "en" and res.json["hello"] == "Hi"
|
|
161
|
-
assert res.headers["FSA-User"] == "None (None)"
|
|
162
|
-
|
|
163
|
-
def test_authenticator_token():
|
|
164
|
-
# all token carriers
|
|
165
|
-
auth = ft.Authenticator(allow=["bearer", "header", "tparam", "cookie"])
|
|
166
|
-
auth.setToken("calvin", "clv-token")
|
|
167
|
-
auth.setToken("susie", "ss-token")
|
|
168
|
-
auth.setToken("hobbes", "hbs-token")
|
|
169
|
-
auth.setToken("moe", "m-token")
|
|
170
|
-
auth.setCookie("calvin", "what", "clv-cookie")
|
|
171
|
-
kwargs, cookies = {}, {}
|
|
172
|
-
auth.setAuth("calvin", kwargs, cookies, auth="bearer")
|
|
173
|
-
assert kwargs["headers"]["Authorization"] == "Bearer clv-token"
|
|
174
|
-
assert cookies["what"] == "clv-cookie"
|
|
175
|
-
kwargs, cookies = {}, {}
|
|
176
|
-
auth.setAuth("susie", kwargs, cookies, auth="header")
|
|
177
|
-
assert kwargs["headers"]["Auth"] == "ss-token"
|
|
178
|
-
assert not cookies
|
|
179
|
-
kwargs, cookies = {"data": {}}, {}
|
|
180
|
-
auth.setAuth("hobbes", kwargs, cookies, auth="tparam")
|
|
181
|
-
assert kwargs["data"]["AUTH"] == "hbs-token"
|
|
182
|
-
assert not cookies
|
|
183
|
-
kwargs, cookies = {"json": {}}, {}
|
|
184
|
-
auth.setAuth("hobbes", kwargs, cookies, auth="tparam")
|
|
185
|
-
assert kwargs["json"]["AUTH"] == "hbs-token"
|
|
186
|
-
assert not cookies
|
|
187
|
-
kwargs, cookies = {}, {}
|
|
188
|
-
auth.setAuth("moe", kwargs, cookies, auth="cookie")
|
|
189
|
-
assert not kwargs
|
|
190
|
-
assert cookies["auth"] == "m-token"
|
|
191
|
-
# dad does not have a token
|
|
192
|
-
try:
|
|
193
|
-
kwargs, cookies = {}, {}
|
|
194
|
-
auth.setAuth("dad", kwargs, cookies)
|
|
195
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
196
|
-
except ft.FlaskTesterError:
|
|
197
|
-
assert True, "error raised"
|
|
198
|
-
# rosalyn as a password, but no password carrier is allowed
|
|
199
|
-
try:
|
|
200
|
-
auth.setPass("rosalyn", "rsln-pass")
|
|
201
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
202
|
-
except ft.AuthError:
|
|
203
|
-
assert True, "error raised"
|
|
204
|
-
# force to trigger later errors
|
|
205
|
-
auth._has_pass = True
|
|
206
|
-
auth.setPass("rosalyn", "rsln-pass")
|
|
207
|
-
try:
|
|
208
|
-
kwargs, cookies = {}, {}
|
|
209
|
-
auth.setAuth("rosalyn", kwargs, cookies)
|
|
210
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
211
|
-
except ft.FlaskTesterError:
|
|
212
|
-
assert True, "error raised"
|
|
213
|
-
|
|
214
|
-
def test_authenticator_password():
|
|
215
|
-
# all password carriers plus fake
|
|
216
|
-
auth = ft.Authenticator(allow=["basic", "param", "fake"])
|
|
217
|
-
auth.setPass("calvin", "clv-pass")
|
|
218
|
-
auth.setPass("hobbes", "hbs-pass")
|
|
219
|
-
auth.setPass("moe", "m-pass")
|
|
220
|
-
auth.setPass("rosalyn", "rsln-pass")
|
|
221
|
-
auth.setCookie("moe", "hello", "world!")
|
|
222
|
-
kwargs, cookies = {}, {}
|
|
223
|
-
auth.setAuth("calvin", kwargs, cookies, auth="basic")
|
|
224
|
-
assert kwargs["auth"] == ("calvin", "clv-pass")
|
|
225
|
-
assert not cookies
|
|
226
|
-
kwargs, cookies = {"data": {}}, {}
|
|
227
|
-
auth.setAuth("hobbes", kwargs, cookies, auth="param")
|
|
228
|
-
assert kwargs["data"]["USER"] == "hobbes" and kwargs["data"]["PASS"] == "hbs-pass"
|
|
229
|
-
assert not cookies
|
|
230
|
-
kwargs, cookies = {"json": {}}, {}
|
|
231
|
-
auth.setAuth("moe", kwargs, cookies, auth="param")
|
|
232
|
-
assert kwargs["json"]["USER"] == "moe" and kwargs["json"]["PASS"] == "m-pass"
|
|
233
|
-
assert cookies["hello"] == "world!"
|
|
234
|
-
kwargs, cookies = {"data": {}}, {}
|
|
235
|
-
auth.setAuth("rosalyn", kwargs, cookies, auth="fake")
|
|
236
|
-
assert kwargs["data"]["LOGIN"] == "rosalyn"
|
|
237
|
-
assert not cookies
|
|
238
|
-
kwargs, cookies = {"json": {}}, {}
|
|
239
|
-
auth.setAuth("hobbes", kwargs, cookies, auth="fake")
|
|
240
|
-
assert kwargs["json"]["LOGIN"] == "hobbes"
|
|
241
|
-
assert not cookies
|
|
242
|
-
# susie as a token, but no token carrier is allowed
|
|
243
|
-
try:
|
|
244
|
-
auth.setToken("susie", "ss-token")
|
|
245
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
246
|
-
except ft.FlaskTesterError:
|
|
247
|
-
assert True, "error raised"
|
|
248
|
-
# force to trigger later error
|
|
249
|
-
auth._has_token = True
|
|
250
|
-
auth.setToken("susie", "ss-token")
|
|
251
|
-
try:
|
|
252
|
-
kwargs, cookies = {}, {}
|
|
253
|
-
auth.setAuth("susie", kwargs, cookies)
|
|
254
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
255
|
-
except ft.FlaskTesterError:
|
|
256
|
-
assert True, "error raised"
|
|
257
|
-
|
|
258
|
-
def test_request_flask_response():
|
|
259
|
-
|
|
260
|
-
class RequestResponse:
|
|
261
|
-
"""Local class for testing RequestFlaskResponse."""
|
|
262
|
-
|
|
263
|
-
def __init__(self, is_json: bool):
|
|
264
|
-
self._is_json = is_json
|
|
265
|
-
self.status_code = 200
|
|
266
|
-
self.content = b"hello world!"
|
|
267
|
-
self.text = "hello world!"
|
|
268
|
-
self.headers = {"Server": "test/0.1"}
|
|
269
|
-
self.cookies = {}
|
|
270
|
-
|
|
271
|
-
def json(self):
|
|
272
|
-
if self._is_json:
|
|
273
|
-
return {"hello": "world!"}
|
|
274
|
-
else:
|
|
275
|
-
raise Exception("not json!")
|
|
276
|
-
|
|
277
|
-
jres = ft.RequestFlaskResponse(RequestResponse(True))
|
|
278
|
-
assert jres.is_json and jres.json is not None
|
|
279
|
-
|
|
280
|
-
xres = ft.RequestFlaskResponse(RequestResponse(False))
|
|
281
|
-
assert not xres.is_json and xres.json is None
|
|
282
|
-
|
|
283
|
-
def test_client():
|
|
284
|
-
# abstract class for coverage
|
|
285
|
-
client = ft.Client(ft.Authenticator())
|
|
286
|
-
try:
|
|
287
|
-
client._request("GET", "/", {})
|
|
288
|
-
pytest.fail("must raise an error") # pragma: no cover
|
|
289
|
-
except NotImplementedError:
|
|
290
|
-
assert True, "expected error raised"
|
|
291
|
-
|
|
292
|
-
def test_request_client():
|
|
293
|
-
# start a tmp server on port 8888 for URL client coverage
|
|
294
|
-
httpd = htsv.HTTPServer(("", 8888), htsv.SimpleHTTPRequestHandler)
|
|
295
|
-
thread = threading.Thread(target = lambda: httpd.serve_forever())
|
|
296
|
-
thread.start()
|
|
297
|
-
try:
|
|
298
|
-
client = ft.RequestClient(ft.Authenticator(), "http://localhost:8888")
|
|
299
|
-
client.get("/", 200)
|
|
300
|
-
client.request("GET", "/", 200)
|
|
301
|
-
hello = io.BytesIO(b"hello world")
|
|
302
|
-
client.post("/", 501, data={"hello": hello})
|
|
303
|
-
hello = io.BytesIO(b"hello world")
|
|
304
|
-
client.post("/", 501, data={"hello": (hello, "hello.txt", "text/plain")})
|
|
305
|
-
client.post("/", 501, data={"hello": "world!"})
|
|
306
|
-
finally:
|
|
307
|
-
httpd.shutdown()
|
|
308
|
-
|
|
309
|
-
def test_client_fixture():
|
|
310
|
-
# ft_client coverage
|
|
311
|
-
auth = ft._ft_authenticator()
|
|
312
|
-
# check and save env
|
|
313
|
-
app = None
|
|
314
|
-
if "FLASK_TESTER_APP" in os.environ:
|
|
315
|
-
app = os.environ["FLASK_TESTER_APP"]
|
|
316
|
-
del os.environ["FLASK_TESTER_APP"]
|
|
317
|
-
# no url nor app, defaults to "app"
|
|
318
|
-
init = ft._ft_client(auth)
|
|
319
|
-
# url
|
|
320
|
-
os.environ["FLASK_TESTER_APP"] = "http://localhost:5000"
|
|
321
|
-
init = ft._ft_client(auth)
|
|
322
|
-
assert isinstance(init, ft.RequestClient)
|
|
323
|
-
del os.environ["FLASK_TESTER_APP"]
|
|
324
|
-
# bad package
|
|
325
|
-
os.environ["FLASK_TESTER_APP"] = "no_such_package"
|
|
326
|
-
try:
|
|
327
|
-
init = ft._ft_client(auth)
|
|
328
|
-
pytest.fail("must fail on missing package") # pragma: no cover
|
|
329
|
-
except ModuleNotFoundError:
|
|
330
|
-
assert True, "expected error raised"
|
|
331
|
-
# bad name
|
|
332
|
-
os.environ["FLASK_TESTER_APP"] = "app:no_such_app"
|
|
333
|
-
try:
|
|
334
|
-
init = ft._ft_client(auth)
|
|
335
|
-
pytest.fail("must fail on missing package") # pragma: no cover
|
|
336
|
-
except ft.FlaskTesterError:
|
|
337
|
-
assert True, "expected error raised"
|
|
338
|
-
del os.environ["FLASK_TESTER_APP"]
|
|
339
|
-
# reset env
|
|
340
|
-
if app:
|
|
341
|
-
os.environ["FLASK_TESTER_APP"] = app
|
|
342
|
-
|
|
343
|
-
def test_classes(api):
|
|
344
|
-
|
|
345
|
-
def thing_eq(ta, tb):
|
|
346
|
-
tb = model.Thing1(**tb) if isinstance(tb, dict) else tb
|
|
347
|
-
return ta.tid == tb.tid and ta.name == tb.name and ta.owner == tb.owner
|
|
348
|
-
|
|
349
|
-
# Things
|
|
350
|
-
t0 = {"tid": 0, "name": "zero", "owner": "Rosalyn"}
|
|
351
|
-
t1 = model.Thing1(tid=1, name="one", owner="Susie")
|
|
352
|
-
t2 = model.Thing2(tid=2, name="two", owner="Calvin")
|
|
353
|
-
t3 = model.Thing3(tid=3, name="three", owner="Hobbes")
|
|
354
|
-
|
|
355
|
-
n = 0
|
|
356
|
-
# check all combinations
|
|
357
|
-
for path in ["/t0", "/t1", "/t2", "/t3"]:
|
|
358
|
-
for param in [t0, t1, t2, t3]:
|
|
359
|
-
for tclass in [model.Thing1, model.Thing2, model.Thing3]:
|
|
360
|
-
for method in ["GET", "POST"]:
|
|
361
|
-
for mode in ["data", "json"]:
|
|
362
|
-
n +=1
|
|
363
|
-
parameter = {mode: {"t": param}}
|
|
364
|
-
res = api.request(method, path, 200, **parameter)
|
|
365
|
-
assert res.is_json
|
|
366
|
-
json = res.json
|
|
367
|
-
assert isinstance(json, dict)
|
|
368
|
-
assert thing_eq(tclass(**json), param)
|
|
369
|
-
assert n == 192
|
|
370
|
-
|
|
371
|
-
# simple types translation
|
|
372
|
-
assert api.get("/t0", 200, json={"t": None}).json is None
|
|
373
|
-
assert api.get("/t0", 200, data={"t": None}).json is None
|
|
374
|
-
assert api.get("/t0", 200, json={"t": True}).json == True
|
|
375
|
-
assert api.get("/t0", 200, json={"t": False}).json == False
|
|
376
|
-
# non working, bad JsonData conversion
|
|
377
|
-
api.get("/t0", 400, data={"t": True})
|
|
378
|
-
api.get("/t0", 400, data={"t": False})
|
|
379
|
-
assert api.get("/t0", 200, json={"t": 2345}).json == 2345
|
|
380
|
-
assert api.get("/t0", 200, data={"t": 5432}).json == 5432
|
|
381
|
-
assert api.get("/t0", 200, json={"t": 23.45}).json == 23.45
|
|
382
|
-
assert api.get("/t0", 200, data={"t": 54.32}).json == 54.32
|
|
383
|
-
assert api.get("/t0", 200, json={"t": "Susie"}).json == "Susie"
|
|
384
|
-
api.get("/t0", 400, data={"t": "Susie"}) # raw str is not JSON
|
|
385
|
-
assert api.get("/t0", 200, data={"t": "\"Susie\""}).json == "Susie"
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import pytest
|
|
3
|
-
from FlaskTester import ft_client, ft_authenticator
|
|
4
|
-
import secret
|
|
5
|
-
|
|
6
|
-
os.environ.update(FLASK_TESTER_ALLOW="basic param none")
|
|
7
|
-
|
|
8
|
-
@pytest.fixture
|
|
9
|
-
def app(ft_client):
|
|
10
|
-
ft_client.setPass("calvin", secret.PASSES["calvin"])
|
|
11
|
-
ft_client.setCookie("calvin", "lang", "en")
|
|
12
|
-
ft_client.setPass("hobbes", secret.PASSES["hobbes"])
|
|
13
|
-
ft_client.setCookie("hobbes", "lang", "fr")
|
|
14
|
-
yield ft_client
|
|
15
|
-
|
|
16
|
-
def test_something(app):
|
|
17
|
-
# requires an authentication
|
|
18
|
-
app.get("/authenticated", 401, login=None)
|
|
19
|
-
app.get("/authenticated", 200, "Hello", login="calvin")
|
|
20
|
-
app.get("/authenticated", 200, "Bonjour", login="hobbes")
|
|
21
|
-
# only allowed to calvin
|
|
22
|
-
app.get("/only-admin", 401, login=None)
|
|
23
|
-
app.get("/only-admin", 200, "administrateur", login="calvin")
|
|
24
|
-
app.get("/only-admin", 403, "not in group", login="hobbes")
|
|
25
|
-
# no authentication required, but depends on lang
|
|
26
|
-
app.get("/open", 200, "Hello", login="calvin", auth="none")
|
|
27
|
-
app.get("/open", 200, "Bonjour", login="hobbes", auth="none")
|
|
28
|
-
app.get("/open", 200, "Guten Tag", login=None)
|
|
29
|
-
|
|
30
|
-
def test_params(app):
|
|
31
|
-
res = app.get("/add", 200, data={"i": 39, "j": 3}, login=None)
|
|
32
|
-
assert res.is_json and res.json["sum"] == 42
|
|
33
|
-
res = app.get("/add", 200, json={"i": 35, "j": 7}, login=None)
|
|
34
|
-
assert res.is_json and res.json["sum"] == 42
|
|
35
|
-
# mixing data/json is okay for FlaskTester
|
|
36
|
-
res = app.get("/add", 200, data={"i": 30}, json={"j": 12}, login=None)
|
|
37
|
-
assert res.is_json and res.json["sum"] == 42
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|