FlaskTester 3.0__tar.gz → 3.2__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-3.0 → FlaskTester-3.2}/FlaskTester.egg-info/PKG-INFO +26 -9
- {FlaskTester-3.0 → FlaskTester-3.2}/FlaskTester.py +70 -72
- {FlaskTester-3.0 → FlaskTester-3.2}/PKG-INFO +26 -9
- {FlaskTester-3.0 → FlaskTester-3.2}/README.md +25 -8
- {FlaskTester-3.0 → FlaskTester-3.2}/pyproject.toml +1 -1
- {FlaskTester-3.0 → FlaskTester-3.2}/tests/test.py +53 -6
- {FlaskTester-3.0 → FlaskTester-3.2}/FlaskTester.egg-info/SOURCES.txt +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/FlaskTester.egg-info/dependency_links.txt +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/FlaskTester.egg-info/requires.txt +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/FlaskTester.egg-info/top_level.txt +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/LICENSE +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/MANIFEST.in +0 -0
- {FlaskTester-3.0 → FlaskTester-3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: FlaskTester
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2
|
|
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
|
|
@@ -46,13 +46,13 @@ Requires-Dist: myst_parser; extra == "doc"
|
|
|
46
46
|
This package allows to run authenticated tests against a Flask application,
|
|
47
47
|
either with internal Flask tests (aka `test_client`) or external tests (with
|
|
48
48
|
`requests` which performs actual HTTP requests), including password and token
|
|
49
|
-
authentication.
|
|
49
|
+
authentication and per-user cookies.
|
|
50
50
|
|
|
51
51
|
Only one set of tests is needed, switching from internal to external is
|
|
52
52
|
achieved through environment variables.
|
|
53
53
|
|
|
54
54
|

|
|
55
|
-

|
|
56
56
|

|
|
57
57
|

|
|
58
58
|

|
|
@@ -87,8 +87,8 @@ def app(ft_client):
|
|
|
87
87
|
assert res.is_json
|
|
88
88
|
ft_client.setToken("hobbes", res.json["token"])
|
|
89
89
|
# also set a cookie
|
|
90
|
-
ft_client.setCookie("hobbes", "lang", "
|
|
91
|
-
ft_client.setCookie("calvin", "lang", "
|
|
90
|
+
ft_client.setCookie("hobbes", "lang", "fr")
|
|
91
|
+
ft_client.setCookie("calvin", "lang", "en")
|
|
92
92
|
# return working client
|
|
93
93
|
yield ft_client
|
|
94
94
|
|
|
@@ -109,7 +109,7 @@ def test_app_admin(app):
|
|
|
109
109
|
This can be run against a (local) server:
|
|
110
110
|
|
|
111
111
|
```shell
|
|
112
|
-
export TEST_SEED="some-random-data" # shared seed
|
|
112
|
+
export TEST_SEED="some-random-data" # shared test seed
|
|
113
113
|
flask --app app:app run & # start flask app
|
|
114
114
|
pid=$! # keep pid
|
|
115
115
|
export FLASK_TESTER_URL="http://localhost:5000" # set app local url
|
|
@@ -128,6 +128,11 @@ The above test runs with [`tests/app.py`](tests/app.py)
|
|
|
128
128
|
[Flask](https://flask.palletsprojects.com/)
|
|
129
129
|
REST application back-end with password and token authentication based on
|
|
130
130
|
[FlaskSimpleAuth](https://pypi.org/project/FlaskSimpleAuth/).
|
|
131
|
+
The code uses _25_ lines of Python for implementing
|
|
132
|
+
password (basic and parameters) and token authentications,
|
|
133
|
+
admin group authorization, and routes for
|
|
134
|
+
token generation (2), identity tests (2) and an incredible open cookie-based
|
|
135
|
+
translation service.
|
|
131
136
|
|
|
132
137
|
## Fixtures
|
|
133
138
|
|
|
@@ -155,11 +160,12 @@ The package provides two fixtures:
|
|
|
155
160
|
- `FLASK_TESTER_LOG_LEVEL` log level for module,
|
|
156
161
|
default is `NOTSET`.
|
|
157
162
|
|
|
158
|
-
The fixture has
|
|
163
|
+
The fixture has 4 main methods:
|
|
159
164
|
- `setPass` to associate a password to a user, set to _None_ to remove credential.
|
|
160
165
|
- `setToken` to associate a token to a user, set to _None_ to remove credential.
|
|
161
166
|
- `setCookie` to add a cookie to a user, set value to _None_ to remove cookie.
|
|
162
|
-
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
167
|
+
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
168
|
+
This method is called automatically for adding credentials to a request.
|
|
163
169
|
|
|
164
170
|
- `ft_client` for app testing, which depends on the previous fixture, plus
|
|
165
171
|
environment variables which allow to find the application, at least one must
|
|
@@ -178,7 +184,7 @@ The package provides two fixtures:
|
|
|
178
184
|
|
|
179
185
|
The fixture then provides test methods to issue test requests against a Flask application:
|
|
180
186
|
- `request` generic request with `login`, `auth`, `status` end `content` extensions.
|
|
181
|
-
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
187
|
+
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
182
188
|
Moreover, `setPass`, `setToken` and `setCookie` are forwarded to the internal authenticator.
|
|
183
189
|
|
|
184
190
|
Authenticator environment variables can be set from the pytest Python test file by
|
|
@@ -242,6 +248,17 @@ Packages are distributed from [PyPI](https://pypi.org/project/FlaskTester/),
|
|
|
242
248
|
see also the [documentation](https://zx80.github.io/flask-tester/),
|
|
243
249
|
please report any [issues](https://github.com/zx80/flask-tester/issues).
|
|
244
250
|
|
|
251
|
+
### 3.2 on 2024-03-24
|
|
252
|
+
|
|
253
|
+
Improved documentation.
|
|
254
|
+
Simpler code and API documentation.
|
|
255
|
+
|
|
256
|
+
### 3.1 on 2024-03-24
|
|
257
|
+
|
|
258
|
+
More consistent test and demo code.
|
|
259
|
+
Reach actual full coverage, without _any_ pragma.
|
|
260
|
+
Fix default allowed authenticator schemes.
|
|
261
|
+
|
|
245
262
|
### 3.0 on 2024-03-23
|
|
246
263
|
|
|
247
264
|
Add support for `none` authentication, with only cookies.
|
|
@@ -293,7 +293,8 @@ class Client:
|
|
|
293
293
|
"""Run a request and return response."""
|
|
294
294
|
raise NotImplementedError()
|
|
295
295
|
|
|
296
|
-
def request(self, method: str, path: str, status: int|None = None,
|
|
296
|
+
def request(self, method: str, path: str, status: int|None = None, content: str|None = None,
|
|
297
|
+
auth: str|None = None, **kwargs):
|
|
297
298
|
"""Run a possibly authenticated HTTP request.
|
|
298
299
|
|
|
299
300
|
Mandatory parameters:
|
|
@@ -304,6 +305,7 @@ class Client:
|
|
|
304
305
|
Optional parameters:
|
|
305
306
|
|
|
306
307
|
- ``status``: expected HTTP status, *None* to skip status check
|
|
308
|
+
- ``content``: regular expression for response body, *None* to skip content check
|
|
307
309
|
- ``login``: authenticated user, use **explicit** *None* to skip
|
|
308
310
|
- ``auth``: authentication scheme to use instead of default behavior
|
|
309
311
|
- ``**kwargs``: more request parameters (headers, data, json…)
|
|
@@ -323,11 +325,18 @@ class Client:
|
|
|
323
325
|
self._auth.setAuth(login, kwargs, cookies, auth=auth)
|
|
324
326
|
res = self._request(method, path, cookies, **kwargs) # type: ignore
|
|
325
327
|
|
|
328
|
+
# check status
|
|
326
329
|
if status is not None:
|
|
327
330
|
if res.status_code != status: # show error before aborting
|
|
328
331
|
log.error(f"bad {status} result: {res.status_code} {res.text[:512]}")
|
|
329
332
|
assert res.status_code == status, f"unexpected status {res.status_code}, expecting {status}"
|
|
330
333
|
|
|
334
|
+
# check content
|
|
335
|
+
if content is not None:
|
|
336
|
+
if not re.search(content, res.text, re.DOTALL):
|
|
337
|
+
log.error(f"cannot find {content} in {res.text}")
|
|
338
|
+
assert False, f"expected content {content} not found in {res.text}"
|
|
339
|
+
|
|
331
340
|
return res
|
|
332
341
|
|
|
333
342
|
def get(self, path, **kwargs):
|
|
@@ -350,32 +359,13 @@ class Client:
|
|
|
350
359
|
"""HTTP DELETE request."""
|
|
351
360
|
return self.request("DELETE", path, **kwargs)
|
|
352
361
|
|
|
353
|
-
def check(self, method: str, path: str, status: int,
|
|
354
|
-
"""Run a query and check the response.
|
|
355
|
-
|
|
356
|
-
Mandatory parameters:
|
|
362
|
+
def check(self, method: str, path: str, status: int, **kwargs):
|
|
363
|
+
"""Run a query and check the response status.
|
|
357
364
|
|
|
358
|
-
|
|
359
|
-
- ``path``: local path under the base URL
|
|
360
|
-
- ``status``: expected HTTP status
|
|
361
|
-
|
|
362
|
-
Optional parameters:
|
|
363
|
-
|
|
364
|
-
- ``content``: regular expression in response body
|
|
365
|
-
- ``login``: authenticated user, use explicit *None* to skip
|
|
366
|
-
- ``**kwargs``: more request parameters (headers, data, json…)
|
|
365
|
+
Same as ``request``, but ``status`` is mandatory.
|
|
367
366
|
"""
|
|
368
367
|
|
|
369
|
-
|
|
370
|
-
res = self.request(method, path, status=status, **kwargs)
|
|
371
|
-
|
|
372
|
-
# check contents
|
|
373
|
-
if content is not None:
|
|
374
|
-
if not re.search(content, res.text, re.DOTALL):
|
|
375
|
-
log.error(f"cannot find {content} in {res.text}")
|
|
376
|
-
assert False, f"expected content {content} not found in {res.text}"
|
|
377
|
-
|
|
378
|
-
return res
|
|
368
|
+
return self.request(method, path, status=status, **kwargs)
|
|
379
369
|
|
|
380
370
|
|
|
381
371
|
class RequestClient(Client):
|
|
@@ -455,36 +445,8 @@ class FlaskClient(Client):
|
|
|
455
445
|
|
|
456
446
|
return self._client.open(method=method, path=path, **kwargs)
|
|
457
447
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
def ft_authenticator():
|
|
461
|
-
"""Pytest Fixture: ft_authenticator.
|
|
462
|
-
|
|
463
|
-
Environment variables:
|
|
464
|
-
|
|
465
|
-
- ``FLASK_TESTER_LOG_LEVEL``: package log level in
|
|
466
|
-
``DEBUG INFO WARNING ERROR CRITICAL NOSET``.
|
|
467
|
-
Default is ``NOTSET``.
|
|
468
|
-
- ``FLASK_TESTER_ALLOW``: allowed space-separated authentication schemes, in
|
|
469
|
-
``basic param bearer header cookie tparam fake none``.
|
|
470
|
-
Default is ``bearer basic param``.
|
|
471
|
-
- ``FLASK_TESTER_USER``: user login parameter for ``param`` authentication.
|
|
472
|
-
Default is ``USER``.
|
|
473
|
-
- ``FLASK_TESTER_PASS``: user password parameter for ``param`` authentication.
|
|
474
|
-
Default is ``PASS``.
|
|
475
|
-
- ``FLASK_TESTER_LOGIN``: user login parameter for ``fake`` authentication.
|
|
476
|
-
Default is ``LOGIN``.
|
|
477
|
-
- ``FLASK_TESTER_BEARER``: bearer name for *token* authentication.
|
|
478
|
-
Default is ``Bearer``.
|
|
479
|
-
- ``FLASK_TESTER_HEADER``: header name for *token* authentication.
|
|
480
|
-
Default is ``Auth``.
|
|
481
|
-
- ``FLASK_TESTER_COOKIE``: cookie name for *token* authentication.
|
|
482
|
-
Default is ``auth``.
|
|
483
|
-
- ``FLASK_TESTER_TPARAM``: parameter for *token* authentication.
|
|
484
|
-
Default is ``AUTH``.
|
|
485
|
-
- ``FLASK_TESTER_AUTH``: initial comma-separated list of *login:password*.
|
|
486
|
-
Default is not set.
|
|
487
|
-
"""
|
|
448
|
+
def _ft_authenticator():
|
|
449
|
+
"""Fixture implementation separated for testing."""
|
|
488
450
|
|
|
489
451
|
level = os.environ.get("FLASK_TESTER_LOG_LEVEL", "NOTSET")
|
|
490
452
|
log.setLevel(logging.DEBUG if level == "DEBUG" else
|
|
@@ -494,7 +456,7 @@ def ft_authenticator():
|
|
|
494
456
|
logging.CRITICAL if level == "CRITICAL" else
|
|
495
457
|
logging.NOTSET)
|
|
496
458
|
|
|
497
|
-
allow = os.environ.get("FLASK_TESTER_ALLOW", "bearer basic param").split(" ")
|
|
459
|
+
allow = os.environ.get("FLASK_TESTER_ALLOW", "bearer basic param none").split(" ")
|
|
498
460
|
|
|
499
461
|
# per-scheme parameters, must be consistent with FSA configuration
|
|
500
462
|
user = os.environ.get("FLASK_TESTER_USER", "USER")
|
|
@@ -511,31 +473,50 @@ def ft_authenticator():
|
|
|
511
473
|
if "FLASK_TESTER_AUTH" in os.environ:
|
|
512
474
|
auth.setPasses(os.environ["FLASK_TESTER_AUTH"].split(","))
|
|
513
475
|
|
|
514
|
-
|
|
515
|
-
|
|
476
|
+
return auth
|
|
516
477
|
|
|
517
478
|
@pytest.fixture
|
|
518
|
-
def
|
|
519
|
-
"""Pytest Fixture:
|
|
479
|
+
def ft_authenticator():
|
|
480
|
+
"""Pytest Fixture: ft_authenticator.
|
|
520
481
|
|
|
521
|
-
|
|
482
|
+
Environment variables:
|
|
522
483
|
|
|
523
|
-
- ``
|
|
524
|
-
|
|
484
|
+
- ``FLASK_TESTER_LOG_LEVEL``: package log level in
|
|
485
|
+
``DEBUG INFO WARNING ERROR CRITICAL NOSET``.
|
|
486
|
+
Default is ``NOTSET``.
|
|
487
|
+
- ``FLASK_TESTER_ALLOW``: allowed space-separated authentication schemes, in
|
|
488
|
+
``basic param bearer header cookie tparam fake none``.
|
|
489
|
+
Default is ``bearer basic param none``.
|
|
490
|
+
- ``FLASK_TESTER_USER``: user login parameter for ``param`` authentication.
|
|
491
|
+
Default is ``USER``.
|
|
492
|
+
- ``FLASK_TESTER_PASS``: user password parameter for ``param`` authentication.
|
|
493
|
+
Default is ``PASS``.
|
|
494
|
+
- ``FLASK_TESTER_LOGIN``: user login parameter for ``fake`` authentication.
|
|
495
|
+
Default is ``LOGIN``.
|
|
496
|
+
- ``FLASK_TESTER_BEARER``: bearer name for *token* authentication.
|
|
497
|
+
Default is ``Bearer``.
|
|
498
|
+
- ``FLASK_TESTER_HEADER``: header name for *token* authentication.
|
|
499
|
+
Default is ``Auth``.
|
|
500
|
+
- ``FLASK_TESTER_COOKIE``: cookie name for *token* authentication.
|
|
501
|
+
Default is ``auth``.
|
|
502
|
+
- ``FLASK_TESTER_TPARAM``: parameter for *token* authentication.
|
|
503
|
+
Default is ``AUTH``.
|
|
504
|
+
- ``FLASK_TESTER_AUTH``: initial comma-separated list of *login:password*.
|
|
505
|
+
Default is not set.
|
|
506
|
+
"""
|
|
525
507
|
|
|
526
|
-
|
|
508
|
+
yield _ft_authenticator()
|
|
527
509
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
"""
|
|
510
|
+
def _ft_client(authenticator):
|
|
511
|
+
"""Fixture implementation separated for testing."""
|
|
531
512
|
|
|
532
513
|
default_login = os.environ.get("FLASK_TESTER_DEFAULT", None)
|
|
533
514
|
client: Client
|
|
534
515
|
|
|
535
|
-
if "FLASK_TESTER_URL" in os.environ:
|
|
516
|
+
if "FLASK_TESTER_URL" in os.environ:
|
|
536
517
|
|
|
537
518
|
app_url = os.environ["FLASK_TESTER_URL"]
|
|
538
|
-
client = RequestClient(
|
|
519
|
+
client = RequestClient(authenticator, app_url, default_login)
|
|
539
520
|
|
|
540
521
|
elif "FLASK_TESTER_APP" in os.environ:
|
|
541
522
|
|
|
@@ -553,12 +534,29 @@ def ft_client(ft_authenticator):
|
|
|
553
534
|
if callable(app) and not hasattr(app, "test_client"):
|
|
554
535
|
app = app()
|
|
555
536
|
break
|
|
556
|
-
if not app:
|
|
537
|
+
if not app:
|
|
557
538
|
raise FlaskTesterError(f"cannot find Flask app in {pkg_name}")
|
|
558
|
-
client = FlaskClient(
|
|
539
|
+
client = FlaskClient(authenticator, app.test_client(), default_login)
|
|
559
540
|
|
|
560
|
-
else:
|
|
541
|
+
else:
|
|
561
542
|
|
|
562
543
|
raise FlaskTesterError("no Flask application to test")
|
|
563
544
|
|
|
564
|
-
|
|
545
|
+
return client
|
|
546
|
+
|
|
547
|
+
@pytest.fixture
|
|
548
|
+
def ft_client(ft_authenticator):
|
|
549
|
+
"""Pytest Fixture: ft_client.
|
|
550
|
+
|
|
551
|
+
Target environment variable, one **must** be defined:
|
|
552
|
+
|
|
553
|
+
- ``FLASK_TESTER_URL``: application HTTP base URL.
|
|
554
|
+
- ``FLASK_TESTER_APP``: Flask application, eg ``app:create_app``.
|
|
555
|
+
|
|
556
|
+
Other environment variable:
|
|
557
|
+
|
|
558
|
+
- ``FLASK_TESTER_DEFAULT``: Default client login, default is *None* for no
|
|
559
|
+
default.
|
|
560
|
+
"""
|
|
561
|
+
|
|
562
|
+
yield _ft_client(ft_authenticator)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: FlaskTester
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2
|
|
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
|
|
@@ -46,13 +46,13 @@ Requires-Dist: myst_parser; extra == "doc"
|
|
|
46
46
|
This package allows to run authenticated tests against a Flask application,
|
|
47
47
|
either with internal Flask tests (aka `test_client`) or external tests (with
|
|
48
48
|
`requests` which performs actual HTTP requests), including password and token
|
|
49
|
-
authentication.
|
|
49
|
+
authentication and per-user cookies.
|
|
50
50
|
|
|
51
51
|
Only one set of tests is needed, switching from internal to external is
|
|
52
52
|
achieved through environment variables.
|
|
53
53
|
|
|
54
54
|

|
|
55
|
-

|
|
56
56
|

|
|
57
57
|

|
|
58
58
|

|
|
@@ -87,8 +87,8 @@ def app(ft_client):
|
|
|
87
87
|
assert res.is_json
|
|
88
88
|
ft_client.setToken("hobbes", res.json["token"])
|
|
89
89
|
# also set a cookie
|
|
90
|
-
ft_client.setCookie("hobbes", "lang", "
|
|
91
|
-
ft_client.setCookie("calvin", "lang", "
|
|
90
|
+
ft_client.setCookie("hobbes", "lang", "fr")
|
|
91
|
+
ft_client.setCookie("calvin", "lang", "en")
|
|
92
92
|
# return working client
|
|
93
93
|
yield ft_client
|
|
94
94
|
|
|
@@ -109,7 +109,7 @@ def test_app_admin(app):
|
|
|
109
109
|
This can be run against a (local) server:
|
|
110
110
|
|
|
111
111
|
```shell
|
|
112
|
-
export TEST_SEED="some-random-data" # shared seed
|
|
112
|
+
export TEST_SEED="some-random-data" # shared test seed
|
|
113
113
|
flask --app app:app run & # start flask app
|
|
114
114
|
pid=$! # keep pid
|
|
115
115
|
export FLASK_TESTER_URL="http://localhost:5000" # set app local url
|
|
@@ -128,6 +128,11 @@ The above test runs with [`tests/app.py`](tests/app.py)
|
|
|
128
128
|
[Flask](https://flask.palletsprojects.com/)
|
|
129
129
|
REST application back-end with password and token authentication based on
|
|
130
130
|
[FlaskSimpleAuth](https://pypi.org/project/FlaskSimpleAuth/).
|
|
131
|
+
The code uses _25_ lines of Python for implementing
|
|
132
|
+
password (basic and parameters) and token authentications,
|
|
133
|
+
admin group authorization, and routes for
|
|
134
|
+
token generation (2), identity tests (2) and an incredible open cookie-based
|
|
135
|
+
translation service.
|
|
131
136
|
|
|
132
137
|
## Fixtures
|
|
133
138
|
|
|
@@ -155,11 +160,12 @@ The package provides two fixtures:
|
|
|
155
160
|
- `FLASK_TESTER_LOG_LEVEL` log level for module,
|
|
156
161
|
default is `NOTSET`.
|
|
157
162
|
|
|
158
|
-
The fixture has
|
|
163
|
+
The fixture has 4 main methods:
|
|
159
164
|
- `setPass` to associate a password to a user, set to _None_ to remove credential.
|
|
160
165
|
- `setToken` to associate a token to a user, set to _None_ to remove credential.
|
|
161
166
|
- `setCookie` to add a cookie to a user, set value to _None_ to remove cookie.
|
|
162
|
-
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
167
|
+
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
168
|
+
This method is called automatically for adding credentials to a request.
|
|
163
169
|
|
|
164
170
|
- `ft_client` for app testing, which depends on the previous fixture, plus
|
|
165
171
|
environment variables which allow to find the application, at least one must
|
|
@@ -178,7 +184,7 @@ The package provides two fixtures:
|
|
|
178
184
|
|
|
179
185
|
The fixture then provides test methods to issue test requests against a Flask application:
|
|
180
186
|
- `request` generic request with `login`, `auth`, `status` end `content` extensions.
|
|
181
|
-
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
187
|
+
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
182
188
|
Moreover, `setPass`, `setToken` and `setCookie` are forwarded to the internal authenticator.
|
|
183
189
|
|
|
184
190
|
Authenticator environment variables can be set from the pytest Python test file by
|
|
@@ -242,6 +248,17 @@ Packages are distributed from [PyPI](https://pypi.org/project/FlaskTester/),
|
|
|
242
248
|
see also the [documentation](https://zx80.github.io/flask-tester/),
|
|
243
249
|
please report any [issues](https://github.com/zx80/flask-tester/issues).
|
|
244
250
|
|
|
251
|
+
### 3.2 on 2024-03-24
|
|
252
|
+
|
|
253
|
+
Improved documentation.
|
|
254
|
+
Simpler code and API documentation.
|
|
255
|
+
|
|
256
|
+
### 3.1 on 2024-03-24
|
|
257
|
+
|
|
258
|
+
More consistent test and demo code.
|
|
259
|
+
Reach actual full coverage, without _any_ pragma.
|
|
260
|
+
Fix default allowed authenticator schemes.
|
|
261
|
+
|
|
245
262
|
### 3.0 on 2024-03-23
|
|
246
263
|
|
|
247
264
|
Add support for `none` authentication, with only cookies.
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
This package allows to run authenticated tests against a Flask application,
|
|
4
4
|
either with internal Flask tests (aka `test_client`) or external tests (with
|
|
5
5
|
`requests` which performs actual HTTP requests), including password and token
|
|
6
|
-
authentication.
|
|
6
|
+
authentication and per-user cookies.
|
|
7
7
|
|
|
8
8
|
Only one set of tests is needed, switching from internal to external is
|
|
9
9
|
achieved through environment variables.
|
|
10
10
|
|
|
11
11
|

|
|
12
|
-

|
|
13
13
|

|
|
14
14
|

|
|
15
15
|

|
|
@@ -44,8 +44,8 @@ def app(ft_client):
|
|
|
44
44
|
assert res.is_json
|
|
45
45
|
ft_client.setToken("hobbes", res.json["token"])
|
|
46
46
|
# also set a cookie
|
|
47
|
-
ft_client.setCookie("hobbes", "lang", "
|
|
48
|
-
ft_client.setCookie("calvin", "lang", "
|
|
47
|
+
ft_client.setCookie("hobbes", "lang", "fr")
|
|
48
|
+
ft_client.setCookie("calvin", "lang", "en")
|
|
49
49
|
# return working client
|
|
50
50
|
yield ft_client
|
|
51
51
|
|
|
@@ -66,7 +66,7 @@ def test_app_admin(app):
|
|
|
66
66
|
This can be run against a (local) server:
|
|
67
67
|
|
|
68
68
|
```shell
|
|
69
|
-
export TEST_SEED="some-random-data" # shared seed
|
|
69
|
+
export TEST_SEED="some-random-data" # shared test seed
|
|
70
70
|
flask --app app:app run & # start flask app
|
|
71
71
|
pid=$! # keep pid
|
|
72
72
|
export FLASK_TESTER_URL="http://localhost:5000" # set app local url
|
|
@@ -85,6 +85,11 @@ The above test runs with [`tests/app.py`](tests/app.py)
|
|
|
85
85
|
[Flask](https://flask.palletsprojects.com/)
|
|
86
86
|
REST application back-end with password and token authentication based on
|
|
87
87
|
[FlaskSimpleAuth](https://pypi.org/project/FlaskSimpleAuth/).
|
|
88
|
+
The code uses _25_ lines of Python for implementing
|
|
89
|
+
password (basic and parameters) and token authentications,
|
|
90
|
+
admin group authorization, and routes for
|
|
91
|
+
token generation (2), identity tests (2) and an incredible open cookie-based
|
|
92
|
+
translation service.
|
|
88
93
|
|
|
89
94
|
## Fixtures
|
|
90
95
|
|
|
@@ -112,11 +117,12 @@ The package provides two fixtures:
|
|
|
112
117
|
- `FLASK_TESTER_LOG_LEVEL` log level for module,
|
|
113
118
|
default is `NOTSET`.
|
|
114
119
|
|
|
115
|
-
The fixture has
|
|
120
|
+
The fixture has 4 main methods:
|
|
116
121
|
- `setPass` to associate a password to a user, set to _None_ to remove credential.
|
|
117
122
|
- `setToken` to associate a token to a user, set to _None_ to remove credential.
|
|
118
123
|
- `setCookie` to add a cookie to a user, set value to _None_ to remove cookie.
|
|
119
|
-
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
124
|
+
- `setAuth` to add authentication data to a request `kwargs` and `cookies`.
|
|
125
|
+
This method is called automatically for adding credentials to a request.
|
|
120
126
|
|
|
121
127
|
- `ft_client` for app testing, which depends on the previous fixture, plus
|
|
122
128
|
environment variables which allow to find the application, at least one must
|
|
@@ -135,7 +141,7 @@ The package provides two fixtures:
|
|
|
135
141
|
|
|
136
142
|
The fixture then provides test methods to issue test requests against a Flask application:
|
|
137
143
|
- `request` generic request with `login`, `auth`, `status` end `content` extensions.
|
|
138
|
-
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
144
|
+
- `get post put patch delete` methods with `login`, `auth` and `status` extensions.
|
|
139
145
|
Moreover, `setPass`, `setToken` and `setCookie` are forwarded to the internal authenticator.
|
|
140
146
|
|
|
141
147
|
Authenticator environment variables can be set from the pytest Python test file by
|
|
@@ -199,6 +205,17 @@ Packages are distributed from [PyPI](https://pypi.org/project/FlaskTester/),
|
|
|
199
205
|
see also the [documentation](https://zx80.github.io/flask-tester/),
|
|
200
206
|
please report any [issues](https://github.com/zx80/flask-tester/issues).
|
|
201
207
|
|
|
208
|
+
### 3.2 on 2024-03-24
|
|
209
|
+
|
|
210
|
+
Improved documentation.
|
|
211
|
+
Simpler code and API documentation.
|
|
212
|
+
|
|
213
|
+
### 3.1 on 2024-03-24
|
|
214
|
+
|
|
215
|
+
More consistent test and demo code.
|
|
216
|
+
Reach actual full coverage, without _any_ pragma.
|
|
217
|
+
Fix default allowed authenticator schemes.
|
|
218
|
+
|
|
202
219
|
### 3.0 on 2024-03-23
|
|
203
220
|
|
|
204
221
|
Add support for `none` authentication, with only cookies.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "FlaskTester"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.2"
|
|
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"
|
|
@@ -38,8 +38,8 @@ def app(ft_client):
|
|
|
38
38
|
assert res.is_json
|
|
39
39
|
ft_client.setToken("hobbes", res.json["token"])
|
|
40
40
|
# also set a cookie
|
|
41
|
-
ft_client.setCookie("hobbes", "lang", "
|
|
42
|
-
ft_client.setCookie("calvin", "lang", "
|
|
41
|
+
ft_client.setCookie("hobbes", "lang", "fr")
|
|
42
|
+
ft_client.setCookie("calvin", "lang", "en")
|
|
43
43
|
# return working client
|
|
44
44
|
yield ft_client
|
|
45
45
|
|
|
@@ -136,16 +136,16 @@ def test_methods(api):
|
|
|
136
136
|
|
|
137
137
|
def test_hello(api):
|
|
138
138
|
res = api.get("/hello", login="calvin", auth="none", status=200)
|
|
139
|
-
assert res.json["hello"] == "Hi"
|
|
139
|
+
assert res.json["lang"] == "en" and res.json["hello"] == "Hi"
|
|
140
140
|
assert res.headers["FSA-User"] == "None (None)"
|
|
141
141
|
res = api.get("/hello", login="hobbes", auth="none", status=200)
|
|
142
|
-
assert res.json["hello"] == "Salut"
|
|
142
|
+
assert res.json["lang"] == "fr" and res.json["hello"] == "Salut"
|
|
143
143
|
assert res.headers["FSA-User"] == "None (None)"
|
|
144
144
|
res = api.get("/hello", login="susie", auth="none", status=200, cookies={"lang": "it"})
|
|
145
|
-
assert res.json["hello"] == "Ciao"
|
|
145
|
+
assert res.json["lang"] == "it" and res.json["hello"] == "Ciao"
|
|
146
146
|
assert res.headers["FSA-User"] == "None (None)"
|
|
147
147
|
res = api.get("/hello", login="moe", auth="none", status=200)
|
|
148
|
-
assert res.json["hello"] == "Hi"
|
|
148
|
+
assert res.json["lang"] == "en" and res.json["hello"] == "Hi"
|
|
149
149
|
assert res.headers["FSA-User"] == "None (None)"
|
|
150
150
|
|
|
151
151
|
def test_authenticator_token():
|
|
@@ -269,6 +269,7 @@ def test_request_flask_response():
|
|
|
269
269
|
assert not xres.is_json and xres.json is None
|
|
270
270
|
|
|
271
271
|
def test_client():
|
|
272
|
+
# abstract class for coverage
|
|
272
273
|
client = ft.Client(ft.Authenticator())
|
|
273
274
|
try:
|
|
274
275
|
client._request("GET", "/", {})
|
|
@@ -277,6 +278,7 @@ def test_client():
|
|
|
277
278
|
assert True, "expected error raised"
|
|
278
279
|
|
|
279
280
|
def test_request_client():
|
|
281
|
+
# start a tmp server on port 8888 for URL client coverage
|
|
280
282
|
httpd = htsv.HTTPServer(("", 8888), htsv.SimpleHTTPRequestHandler)
|
|
281
283
|
thread = threading.Thread(target = lambda: httpd.serve_forever())
|
|
282
284
|
thread.start()
|
|
@@ -290,3 +292,48 @@ def test_request_client():
|
|
|
290
292
|
client.post("/", status=501, data={"hello": "world!"})
|
|
291
293
|
finally:
|
|
292
294
|
httpd.shutdown()
|
|
295
|
+
|
|
296
|
+
def test_client_fixture():
|
|
297
|
+
# ft_client coverage
|
|
298
|
+
auth = ft._ft_authenticator()
|
|
299
|
+
# check and save env
|
|
300
|
+
# assert "FLASK_TESTER_URL" not in os.environ
|
|
301
|
+
url = None
|
|
302
|
+
if "FLASK_TESTER_URL" in os.environ: # pragma: no cover
|
|
303
|
+
url = os.environ["FLASK_TESTER_URL"]
|
|
304
|
+
del os.environ["FLASK_TESTER_URL"]
|
|
305
|
+
app = None
|
|
306
|
+
if "FLASK_TESTER_APP" in os.environ:
|
|
307
|
+
app = os.environ["FLASK_TESTER_APP"]
|
|
308
|
+
del os.environ["FLASK_TESTER_APP"]
|
|
309
|
+
# no url nor app
|
|
310
|
+
try:
|
|
311
|
+
init = ft._ft_client(auth)
|
|
312
|
+
pytest.fail("must fail without environment variables") # pragma: no cover
|
|
313
|
+
except ft.FlaskTesterError:
|
|
314
|
+
assert True, "expected error raised"
|
|
315
|
+
# url
|
|
316
|
+
os.environ["FLASK_TESTER_URL"] = "http://localhost:5000"
|
|
317
|
+
init = ft._ft_client(auth)
|
|
318
|
+
assert isinstance(init, ft.RequestClient)
|
|
319
|
+
del os.environ["FLASK_TESTER_URL"]
|
|
320
|
+
# bad package
|
|
321
|
+
os.environ["FLASK_TESTER_APP"] = "no_such_package"
|
|
322
|
+
try:
|
|
323
|
+
init = ft._ft_client(auth)
|
|
324
|
+
pytest.fail("must fail on missing package") # pragma: no cover
|
|
325
|
+
except ModuleNotFoundError:
|
|
326
|
+
assert True, "expected error raised"
|
|
327
|
+
# bad name
|
|
328
|
+
os.environ["FLASK_TESTER_APP"] = "app:no_such_app"
|
|
329
|
+
try:
|
|
330
|
+
init = ft._ft_client(auth)
|
|
331
|
+
pytest.fail("must fail on missing package") # pragma: no cover
|
|
332
|
+
except ft.FlaskTesterError:
|
|
333
|
+
assert True, "expected error raised"
|
|
334
|
+
del os.environ["FLASK_TESTER_APP"]
|
|
335
|
+
# reset env
|
|
336
|
+
if url: # pragma: no cover
|
|
337
|
+
os.environ["FLASK_TESTER_URL"] = url
|
|
338
|
+
if app:
|
|
339
|
+
os.environ["FLASK_TESTER_APP"] = app
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|