FlaskTester 4.2__tar.gz → 5.0__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: FlaskTester
3
- Version: 4.2
3
+ Version: 5.0
4
4
  Summary: Pytest fixtures for Flask internal and external authenticated tests
5
5
  Author-email: Fabien Coelho <flask.tester@coelho.net>
6
6
  License: CC0
@@ -19,7 +19,6 @@ Requires-Python: >=3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: requests
22
- Requires-Dist: flask
23
22
  Requires-Dist: pytest
24
23
  Provides-Extra: dev
25
24
  Requires-Dist: mypy; extra == "dev"
@@ -27,13 +26,10 @@ Requires-Dist: pyright; extra == "dev"
27
26
  Requires-Dist: ruff; extra == "dev"
28
27
  Requires-Dist: coverage; extra == "dev"
29
28
  Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
30
- Requires-Dist: build; extra == "dev"
31
- Requires-Dist: twine; extra == "dev"
32
- Requires-Dist: wheel; extra == "dev"
33
29
  Requires-Dist: types-flask; extra == "dev"
34
30
  Requires-Dist: types-requests; extra == "dev"
35
- Requires-Dist: FlaskSimpleAuth>=30.0; extra == "dev"
36
- Requires-Dist: passlib; extra == "dev"
31
+ Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
32
+ Requires-Dist: bcrypt; extra == "dev"
37
33
  Requires-Dist: pydantic; extra == "dev"
38
34
  Provides-Extra: doc
39
35
  Requires-Dist: sphinx; extra == "doc"
@@ -41,6 +37,10 @@ Requires-Dist: sphinx_rtd_theme; extra == "doc"
41
37
  Requires-Dist: sphinx-autoapi; extra == "doc"
42
38
  Requires-Dist: sphinx-lint; extra == "doc"
43
39
  Requires-Dist: myst_parser; extra == "doc"
40
+ Provides-Extra: pub
41
+ Requires-Dist: build; extra == "pub"
42
+ Requires-Dist: twine; extra == "pub"
43
+ Requires-Dist: wheel; extra == "pub"
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
  ![Status](https://github.com/zx80/flask-tester/actions/workflows/package.yml/badge.svg?branch=main&style=flat)
56
- ![Tests](https://img.shields.io/badge/tests-14%20✓-success)
56
+ ![Tests](https://img.shields.io/badge/tests-16%20✓-success)
57
57
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
58
  ![Issues](https://img.shields.io/github/issues/zx80/flask-tester?style=flat)
59
59
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -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 (local) server:
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 url
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
  ```
@@ -1,5 +1,4 @@
1
1
  requests
2
- flask
3
2
  pytest
4
3
 
5
4
  [dev]
@@ -8,13 +7,10 @@ pyright
8
7
  ruff
9
8
  coverage
10
9
  pymarkdownlnt!=0.9.5
11
- build
12
- twine
13
- wheel
14
10
  types-flask
15
11
  types-requests
16
- FlaskSimpleAuth>=30.0
17
- passlib
12
+ FlaskSimpleAuth>=35.0
13
+ bcrypt
18
14
  pydantic
19
15
 
20
16
  [doc]
@@ -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
- from typing import Any
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)
@@ -348,11 +368,13 @@ class Client:
348
368
  cookies.update(kwargs["cookies"])
349
369
  del kwargs["cookies"]
350
370
 
351
- # FIXME allow or forbid?
352
- # if "json" in kwargs and "data" in kwargs:
353
- # log.warning("mix of json and data parameters in request")
371
+ # this is forbidden by Flask client
372
+ if "json" in kwargs and "data" in kwargs:
373
+ # merge into data to possibly keep uploads
374
+ kwargs["data"].update(kwargs["json"])
375
+ del kwargs["json"]
354
376
 
355
- # convert json parameters
377
+ # convert json parameters to json
356
378
  if "json" in kwargs:
357
379
  json_param = kwargs["json"]
358
380
  assert isinstance(json_param, dict)
@@ -368,7 +390,7 @@ class Client:
368
390
  else: # pydantic or standard dataclasses?
369
391
  json_param[name] = dataclasses.asdict(val)
370
392
 
371
- # convert data parameters
393
+ # convert data parameters to simple strings
372
394
  if "data" in kwargs:
373
395
  data_param = kwargs["data"]
374
396
  assert isinstance(data_param, dict)
@@ -388,6 +410,7 @@ class Client:
388
410
  else:
389
411
  data_param[name] = json.dumps(dataclasses.asdict(val))
390
412
 
413
+ # now set authentication headers and do the query
391
414
  self._auth.setAuth(login, kwargs, cookies, auth=auth)
392
415
  res = self._request(method, path, cookies, **kwargs) # type: ignore
393
416
 
@@ -530,6 +553,7 @@ def _ft_authenticator():
530
553
  auth = Authenticator(allow, user=user, pwd=pwd, login=login, bearer=bearer,
531
554
  header=header, cookie=cookie, tparam=tparam, ptype=ptype)
532
555
 
556
+ # possibly load credentials from the environment
533
557
  if "FLASK_TESTER_AUTH" in os.environ:
534
558
  auth.setPasses(os.environ["FLASK_TESTER_AUTH"].split(","))
535
559
 
@@ -599,7 +623,7 @@ def _ft_client(authenticator):
599
623
  # none found
600
624
  if not app:
601
625
  raise FlaskTesterError(f"cannot find Flask app in {pkg_name} ({test_app})")
602
- client = FlaskClient(authenticator, app.test_client(), default_login)
626
+ client = FlaskClient(authenticator, app.test_client(), default_login) # type: ignore
603
627
 
604
628
  return client
605
629
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: FlaskTester
3
- Version: 4.2
3
+ Version: 5.0
4
4
  Summary: Pytest fixtures for Flask internal and external authenticated tests
5
5
  Author-email: Fabien Coelho <flask.tester@coelho.net>
6
6
  License: CC0
@@ -19,7 +19,6 @@ Requires-Python: >=3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: requests
22
- Requires-Dist: flask
23
22
  Requires-Dist: pytest
24
23
  Provides-Extra: dev
25
24
  Requires-Dist: mypy; extra == "dev"
@@ -27,13 +26,10 @@ Requires-Dist: pyright; extra == "dev"
27
26
  Requires-Dist: ruff; extra == "dev"
28
27
  Requires-Dist: coverage; extra == "dev"
29
28
  Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
30
- Requires-Dist: build; extra == "dev"
31
- Requires-Dist: twine; extra == "dev"
32
- Requires-Dist: wheel; extra == "dev"
33
29
  Requires-Dist: types-flask; extra == "dev"
34
30
  Requires-Dist: types-requests; extra == "dev"
35
- Requires-Dist: FlaskSimpleAuth>=30.0; extra == "dev"
36
- Requires-Dist: passlib; extra == "dev"
31
+ Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
32
+ Requires-Dist: bcrypt; extra == "dev"
37
33
  Requires-Dist: pydantic; extra == "dev"
38
34
  Provides-Extra: doc
39
35
  Requires-Dist: sphinx; extra == "doc"
@@ -41,6 +37,10 @@ Requires-Dist: sphinx_rtd_theme; extra == "doc"
41
37
  Requires-Dist: sphinx-autoapi; extra == "doc"
42
38
  Requires-Dist: sphinx-lint; extra == "doc"
43
39
  Requires-Dist: myst_parser; extra == "doc"
40
+ Provides-Extra: pub
41
+ Requires-Dist: build; extra == "pub"
42
+ Requires-Dist: twine; extra == "pub"
43
+ Requires-Dist: wheel; extra == "pub"
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
  ![Status](https://github.com/zx80/flask-tester/actions/workflows/package.yml/badge.svg?branch=main&style=flat)
56
- ![Tests](https://img.shields.io/badge/tests-14%20✓-success)
56
+ ![Tests](https://img.shields.io/badge/tests-16%20✓-success)
57
57
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
58
  ![Issues](https://img.shields.io/github/issues/zx80/flask-tester?style=flat)
59
59
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -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 (local) server:
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 url
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
  ![Status](https://github.com/zx80/flask-tester/actions/workflows/package.yml/badge.svg?branch=main&style=flat)
12
- ![Tests](https://img.shields.io/badge/tests-14%20✓-success)
12
+ ![Tests](https://img.shields.io/badge/tests-16%20✓-success)
13
13
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
14
14
  ![Issues](https://img.shields.io/github/issues/zx80/flask-tester?style=flat)
15
15
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -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 (local) server:
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 url
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,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "FlaskTester"
7
- version = "4.2"
7
+ version = "5.0"
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"
@@ -12,7 +12,6 @@ license = { text = "CC0" }
12
12
  requires-python = ">=3.10"
13
13
  dependencies = [
14
14
  "requests",
15
- "flask",
16
15
  "pytest"
17
16
  ]
18
17
  classifiers = [
@@ -36,13 +35,12 @@ package = "https://pypi.org/project/FlaskTester/"
36
35
  dev = [
37
36
  # static checks…
38
37
  "mypy", "pyright", "ruff", "coverage", "pymarkdownlnt!=0.9.5",
39
- # packaging
40
- "build", "twine", "wheel",
41
38
  # tests
42
- "types-flask", "types-requests", "FlaskSimpleAuth>=30.0", "passlib", "pydantic"
39
+ "types-flask", "types-requests", "FlaskSimpleAuth>=35.0", "bcrypt", "pydantic"
43
40
  ]
44
41
  # documentation generation
45
42
  doc = ["sphinx", "sphinx_rtd_theme", "sphinx-autoapi", "sphinx-lint", "myst_parser"]
43
+ pub = ["build", "twine", "wheel"]
46
44
 
47
45
  [tool.setuptools]
48
46
  py-modules = [ "FlaskTester" ]
@@ -12,7 +12,7 @@ import model
12
12
 
13
13
  logging.basicConfig(level=logging.INFO)
14
14
  # logging.basicConfig(level=logging.DEBUG)
15
- # log = logging.getLogger("test")
15
+ log = logging.getLogger("test")
16
16
 
17
17
  # set authn for ft_authenticator
18
18
  os.environ.update(
@@ -27,23 +27,31 @@ def test_sanity():
27
27
  # log.debug(f"TEST_SEED={os.environ.get('TEST_SEED')}")
28
28
 
29
29
  # example from README.md
30
+ def authHook(api, user: str, pwd: str|None):
31
+ if pwd is not None: # get token
32
+ try:
33
+ res = api.get("/login", login=user, auth="basic", status=200)
34
+ api.setToken(user, res.json["token"])
35
+ except ft.FlaskTesterError as e: # pragma: no cover
36
+ log.warning(f"error: {e}")
37
+ else: # cleanup
38
+ api.setToken(user, None)
39
+
30
40
  @pytest.fixture
31
41
  def app(ft_client):
32
- # add test passwords for Calvin and Hobbes (must be consistent with app!)
42
+ # hook when adding login/passwords
43
+ ft_client.setHook(authHook)
44
+ # Calvin authentication
33
45
  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
46
  ft_client.setCookie("hobbes", "lang", "fr")
47
+ # Hobbes authentication
48
+ ft_client.setPass("hobbes", secret.PASSES["hobbes"])
44
49
  ft_client.setCookie("calvin", "lang", "en")
45
50
  # return working client
46
51
  yield ft_client
52
+ # cleanup
53
+ ft_client.setPass("calvin", None)
54
+ ft_client.setPass("hobbes", None)
47
55
 
48
56
  def test_app_admin(app): # GET /admin
49
57
  app.get("/admin", login=None, status=401)
@@ -65,15 +73,15 @@ def test_app_who_am_i(app): # GET /who-am-i
65
73
  def api(ft_client):
66
74
  # set a default
67
75
  ft_client._default_login = "calvin"
68
- # bad password / token
76
+ # bad password and token
69
77
  ft_client.setPass("moe", None)
70
78
  ft_client.setToken("moe", None)
71
79
  ft_client.setPass("moe", "bad password")
72
80
  ft_client.setToken("moe", "bad token")
73
- # cookies
81
+ # set language cookies
74
82
  ft_client.setCookie("calvin", "lang", "en")
75
83
  ft_client.setCookie("hobbes", "lang", "fr")
76
- # get valid tokens using password authn
84
+ # get valid tokens using password authn on /login
77
85
  res = ft_client.get("/login", login="calvin", auth="basic", status=200)
78
86
  assert res.json["user"] == "calvin"
79
87
  ft_client._auth.setToken("calvin", res.json["token"])
@@ -100,7 +108,7 @@ def api(ft_client):
100
108
  assert res.json["user"] == "calvin"
101
109
  res = ft_client.get("/who-am-i", status=200)
102
110
  assert res.json["user"] == "calvin"
103
- # add a bad password
111
+ # ready for testing routes
104
112
  yield ft_client
105
113
 
106
114
  def test_admin(api):
@@ -343,6 +351,7 @@ def test_client_fixture():
343
351
  def test_classes(api):
344
352
 
345
353
  def thing_eq(ta, tb):
354
+ ta = model.Thing1(**ta) if isinstance(ta, dict) else ta
346
355
  tb = model.Thing1(**tb) if isinstance(tb, dict) else tb
347
356
  return ta.tid == tb.tid and ta.name == tb.name and ta.owner == tb.owner
348
357
 
@@ -356,17 +365,15 @@ def test_classes(api):
356
365
  # check all combinations
357
366
  for path in ["/t0", "/t1", "/t2", "/t3"]:
358
367
  for param in [t0, t1, t2, t3]:
359
- for tclass in [model.Thing1, model.Thing2, model.Thing3]:
368
+ for tclass in [dict, model.Thing1, model.Thing2, model.Thing3]:
360
369
  for method in ["GET", "POST"]:
361
370
  for mode in ["data", "json"]:
362
371
  n +=1
363
372
  parameter = {mode: {"t": param}}
364
373
  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
374
+ assert res.is_json and isinstance(res.json, dict)
375
+ assert thing_eq(tclass(**res.json), param)
376
+ assert n == 256
370
377
 
371
378
  # simple types translation
372
379
  assert api.get("/t0", 200, json={"t": None}).json is None
@@ -26,3 +26,12 @@ def test_something(app):
26
26
  app.get("/open", 200, "Hello", login="calvin", auth="none")
27
27
  app.get("/open", 200, "Bonjour", login="hobbes", auth="none")
28
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