From f7355781b4b7d24e9bfea96dadb538967cc22aeb Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Wed, 6 Apr 2022 20:29:15 +0900 Subject: [PATCH] Fix tests, restructure tests --- .github/workflows/python.yml | 3 +- requirements-test.txt | 5 --- tests/{core/test_jose => clients}/__init__.py | 0 .../utils.py => clients/asgi_helper.py} | 34 ------------------ tests/clients/keys/jwks_private.json | 6 ++++ tests/clients/keys/jwks_public.json | 6 ++++ tests/clients/keys/rsa_private.pem | 27 ++++++++++++++ .../test_django}/__init__.py | 0 tests/clients/test_django/settings.py | 36 +++++++++++++++++++ .../test_django}/test_oauth_client.py | 6 ++-- .../test_flask}/__init__.py | 0 .../test_flask}/test_oauth_client.py | 4 +-- .../test_flask}/test_user_mixin.py | 7 ++-- .../test_httpx}/__init__.py | 0 .../test_httpx}/test_assertion_client.py | 4 +-- .../test_async_assertion_client.py | 2 +- .../test_httpx}/test_async_oauth1_client.py | 2 +- .../test_httpx}/test_async_oauth2_client.py | 5 ++- .../test_httpx}/test_oauth1_client.py | 9 +---- .../test_httpx}/test_oauth2_client.py | 2 +- .../test_requests}/__init__.py | 0 .../test_requests}/test_assertion_session.py | 0 .../test_requests}/test_oauth1_session.py | 5 ++- .../test_requests}/test_oauth2_session.py | 14 ++++++-- .../test_starlette}/__init__.py | 0 .../test_starlette}/test_oauth_client.py | 4 +-- .../test_starlette}/test_user_mixin.py | 9 +++-- tests/{client_base.py => clients/util.py} | 19 ++++++---- tests/clients/wsgi_helper.py | 35 ++++++++++++++++++ tests/django/settings.py | 11 +----- tests/django/test_oauth1/oauth1_server.py | 2 +- tests/django/test_oauth2/oauth2_server.py | 2 +- tests/{django/base.py => django_helper.py} | 0 .../test_client_registration_endpoint.py | 4 +-- .../test_httpx_client => jose}/__init__.py | 0 tests/{core/test_jose => jose}/test_jwe.py | 0 tests/{core/test_jose => jose}/test_jwk.py | 0 tests/{core/test_jose => jose}/test_jws.py | 0 tests/{core/test_jose => jose}/test_jwt.py | 0 tests/requirements-base.txt | 3 ++ tests/requirements-clients.txt | 9 +++++ tests/requirements-django.txt | 2 ++ tests/requirements-flask.txt | 2 ++ tox.ini | 25 ++++++------- 44 files changed, 191 insertions(+), 113 deletions(-) delete mode 100644 requirements-test.txt rename tests/{core/test_jose => clients}/__init__.py (100%) rename tests/{starlette/utils.py => clients/asgi_helper.py} (64%) create mode 100644 tests/clients/keys/jwks_private.json create mode 100644 tests/clients/keys/jwks_public.json create mode 100644 tests/clients/keys/rsa_private.pem rename tests/{core/test_requests_client => clients/test_django}/__init__.py (100%) create mode 100644 tests/clients/test_django/settings.py rename tests/{django/test_client => clients/test_django}/test_oauth_client.py (99%) rename tests/{django/test_client => clients/test_flask}/__init__.py (100%) rename tests/{flask/test_client => clients/test_flask}/test_oauth_client.py (99%) rename tests/{flask/test_client => clients/test_flask}/test_user_mixin.py (96%) rename tests/{flask/test_client => clients/test_httpx}/__init__.py (100%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_assertion_client.py (95%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_async_assertion_client.py (97%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_async_oauth1_client.py (99%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_async_oauth2_client.py (99%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_oauth1_client.py (96%) rename tests/{starlette/test_httpx_client => clients/test_httpx}/test_oauth2_client.py (99%) rename tests/{starlette => clients/test_requests}/__init__.py (100%) rename tests/{core/test_requests_client => clients/test_requests}/test_assertion_session.py (100%) rename tests/{core/test_requests_client => clients/test_requests}/test_oauth1_session.py (98%) rename tests/{core/test_requests_client => clients/test_requests}/test_oauth2_session.py (98%) rename tests/{starlette/test_client => clients/test_starlette}/__init__.py (100%) rename tests/{starlette/test_client => clients/test_starlette}/test_oauth_client.py (98%) rename tests/{starlette/test_client => clients/test_starlette}/test_user_mixin.py (93%) rename tests/{client_base.py => clients/util.py} (74%) create mode 100644 tests/clients/wsgi_helper.py rename tests/{django/base.py => django_helper.py} (100%) rename tests/{starlette/test_httpx_client => jose}/__init__.py (100%) rename tests/{core/test_jose => jose}/test_jwe.py (100%) rename tests/{core/test_jose => jose}/test_jwk.py (100%) rename tests/{core/test_jose => jose}/test_jws.py (100%) rename tests/{core/test_jose => jose}/test_jwt.py (100%) create mode 100644 tests/requirements-base.txt create mode 100644 tests/requirements-clients.txt create mode 100644 tests/requirements-django.txt create mode 100644 tests/requirements-flask.txt diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index b9d5bea5..a04873f3 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -37,11 +37,10 @@ jobs: run: | python -m pip install --upgrade pip pip install tox - pip install -r requirements-test.txt - name: Test with tox ${{ matrix.python.toxenv }} env: - TOXENV: py,flask,django,starlette + TOXENV: py,jose,clients,flask,django run: tox - name: Report coverage diff --git a/requirements-test.txt b/requirements-test.txt deleted file mode 100644 index 80369f33..00000000 --- a/requirements-test.txt +++ /dev/null @@ -1,5 +0,0 @@ -cryptography -pycryptodomex>=3.10,<4 -requests -pytest -coverage diff --git a/tests/core/test_jose/__init__.py b/tests/clients/__init__.py similarity index 100% rename from tests/core/test_jose/__init__.py rename to tests/clients/__init__.py diff --git a/tests/starlette/utils.py b/tests/clients/asgi_helper.py similarity index 64% rename from tests/starlette/utils.py rename to tests/clients/asgi_helper.py index 274b8bb7..5b8660c1 100644 --- a/tests/starlette/utils.py +++ b/tests/clients/asgi_helper.py @@ -1,8 +1,6 @@ import json from starlette.requests import Request as ASGIRequest from starlette.responses import Response as ASGIResponse -from werkzeug.wrappers import Request as WSGIRequest -from werkzeug.wrappers import Response as WSGIResponse class AsyncMockDispatch: @@ -62,35 +60,3 @@ async def __call__(self, scope, receive, send): headers=headers, ) await response(scope, receive, send) - - -class MockDispatch: - def __init__(self, body=b'', status_code=200, headers=None, - assert_func=None): - if headers is None: - headers = {} - if isinstance(body, dict): - body = json.dumps(body).encode() - headers['Content-Type'] = 'application/json' - else: - if isinstance(body, str): - body = body.encode() - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - self.body = body - self.status_code = status_code - self.headers = headers - self.assert_func = assert_func - - def __call__(self, environ, start_response): - request = WSGIRequest(environ) - - if self.assert_func: - self.assert_func(request) - - response = WSGIResponse( - status=self.status_code, - response=self.body, - headers=self.headers, - ) - return response(environ, start_response) diff --git a/tests/clients/keys/jwks_private.json b/tests/clients/keys/jwks_private.json new file mode 100644 index 00000000..2b2149f8 --- /dev/null +++ b/tests/clients/keys/jwks_private.json @@ -0,0 +1,6 @@ +{ + "keys": [ + {"kty": "RSA", "kid": "abc", "n": "pF1JaMSN8TEsh4N4O_5SpEAVLivJyLH-Cgl3OQBPGgJkt8cg49oasl-5iJS-VdrILxWM9_JCJyURpUuslX4Eb4eUBtQ0x5BaPa8-S2NLdGTaL7nBOO8o8n0C5FEUU-qlEip79KE8aqOj-OC44VsIquSmOvWIQD26n3fCVlgwoRBD1gzzsDOeaSyzpKrZR851Kh6rEmF2qjJ8jt6EkxMsRNACmBomzgA4M1TTsisSUO87444pe35Z4_n5c735o2fZMrGgMwiJNh7rT8SYxtIkxngioiGnwkxGQxQ4NzPAHg-XSY0J04pNm7KqTkgtxyrqOANJLIjXlR-U9SQ90NjHVQ", "e": "AQAB", "d": "G4E84ppZwm3fLMI0YZ26iJ_sq3BKcRpQD6_r0o8ZrZmO7y4Uc-ywoP7h1lhFzaox66cokuloZpKOdGHIfK-84EkI3WeveWHPqBjmTMlN_ClQVcI48mUbLhD7Zeenhi9y9ipD2fkNWi8OJny8k4GfXrGqm50w8schrsPksnxJjvocGMT6KZNfDURKF2HlM5X1uY8VCofokXOjBEeHIfYM8e7IcmPpyXwXKonDmVVbMbefo-u-TttgeyOYaO6s3flSy6Y0CnpWi43JQ_VEARxQl6Brj1oizr8UnQQ0nNCOWwDNVtOV4eSl7PZoiiT7CxYkYnhJXECMAM5YBpm4Qk9zdQ", "p": "1g4ZGrXOuo75p9_MRIepXGpBWxip4V7B9XmO9WzPCv8nMorJntWBmsYV1I01aITxadHatO4Gl2xLniNkDyrEQzJ7w38RQgsVK-CqbnC0K9N77QPbHeC1YQd9RCNyUohOimKvb7jyv798FBU1GO5QI2eNgfnnfteSVXhD2iOoTOs", "q": "xJJ-8toxJdnLa0uUsAbql6zeNXGbUBMzu3FomKlyuWuq841jS2kIalaO_TRj5hbnE45jmCjeLgTVO6Ach3Wfk4zrqajqfFJ0zUg_Wexp49lC3RWiV4icBb85Q6bzeJD9Dn9vhjpfWVkczf_NeA1fGH_pcgfkT6Dm706GFFttLL8", "dp": "Zfx3l5NR-O8QIhzuHSSp279Afl_E6P0V2phdNa_vAaVKDrmzkHrXcl-4nPnenXrh7vIuiw_xkgnmCWWBUfylYALYlu-e0GGpZ6t2aIJIRa1QmT_CEX0zzhQcae-dk5cgHK0iO0_aUOOyAXuNPeClzAiVknz4ACZDsXdIlNFyaZs", "dq": "Z9DG4xOBKXBhEoWUPXMpqnlN0gPx9tRtWe2HRDkZsfu_CWn-qvEJ1L9qPSfSKs6ls5pb1xyeWseKpjblWlUwtgiS3cOsM4SI03H4o1FMi11PBtxKJNitLgvT_nrJ0z8fpux-xfFGMjXyFImoxmKpepLzg5nPZo6f6HscLNwsSJk", "qi": "Sk20wFvilpRKHq79xxFWiDUPHi0x0pp82dYIEntGQkKUWkbSlhgf3MAi5NEQTDmXdnB-rVeWIvEi-BXfdnNgdn8eC4zSdtF4sIAhYr5VWZo0WVWDhT7u2ccvZBFymiz8lo3gN57wGUCi9pbZqzV1-ZppX6YTNDdDCE0q-KO3Cec"}, + {"kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB", "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ", "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k", "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc", "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik", "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8", "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4"} + ] +} diff --git a/tests/clients/keys/jwks_public.json b/tests/clients/keys/jwks_public.json new file mode 100644 index 00000000..e29644a6 --- /dev/null +++ b/tests/clients/keys/jwks_public.json @@ -0,0 +1,6 @@ +{ + "keys": [ + {"kty": "RSA", "kid": "abc", "n": "pF1JaMSN8TEsh4N4O_5SpEAVLivJyLH-Cgl3OQBPGgJkt8cg49oasl-5iJS-VdrILxWM9_JCJyURpUuslX4Eb4eUBtQ0x5BaPa8-S2NLdGTaL7nBOO8o8n0C5FEUU-qlEip79KE8aqOj-OC44VsIquSmOvWIQD26n3fCVlgwoRBD1gzzsDOeaSyzpKrZR851Kh6rEmF2qjJ8jt6EkxMsRNACmBomzgA4M1TTsisSUO87444pe35Z4_n5c735o2fZMrGgMwiJNh7rT8SYxtIkxngioiGnwkxGQxQ4NzPAHg-XSY0J04pNm7KqTkgtxyrqOANJLIjXlR-U9SQ90NjHVQ", "e": "AQAB"}, + {"kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB"} + ] +} diff --git a/tests/clients/keys/rsa_private.pem b/tests/clients/keys/rsa_private.pem new file mode 100644 index 00000000..e8df4105 --- /dev/null +++ b/tests/clients/keys/rsa_private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEApF1JaMSN8TEsh4N4O/5SpEAVLivJyLH+Cgl3OQBPGgJkt8cg +49oasl+5iJS+VdrILxWM9/JCJyURpUuslX4Eb4eUBtQ0x5BaPa8+S2NLdGTaL7nB +OO8o8n0C5FEUU+qlEip79KE8aqOj+OC44VsIquSmOvWIQD26n3fCVlgwoRBD1gzz +sDOeaSyzpKrZR851Kh6rEmF2qjJ8jt6EkxMsRNACmBomzgA4M1TTsisSUO87444p +e35Z4/n5c735o2fZMrGgMwiJNh7rT8SYxtIkxngioiGnwkxGQxQ4NzPAHg+XSY0J +04pNm7KqTkgtxyrqOANJLIjXlR+U9SQ90NjHVQIDAQABAoIBABuBPOKaWcJt3yzC +NGGduoif7KtwSnEaUA+v69KPGa2Zju8uFHPssKD+4dZYRc2qMeunKJLpaGaSjnRh +yHyvvOBJCN1nr3lhz6gY5kzJTfwpUFXCOPJlGy4Q+2Xnp4YvcvYqQ9n5DVovDiZ8 +vJOBn16xqpudMPLHIa7D5LJ8SY76HBjE+imTXw1EShdh5TOV9bmPFQqH6JFzowRH +hyH2DPHuyHJj6cl8FyqJw5lVWzG3n6Prvk7bYHsjmGjurN35UsumNAp6VouNyUP1 +RAEcUJega49aIs6/FJ0ENJzQjlsAzVbTleHkpez2aIok+wsWJGJ4SVxAjADOWAaZ +uEJPc3UCgYEA1g4ZGrXOuo75p9/MRIepXGpBWxip4V7B9XmO9WzPCv8nMorJntWB +msYV1I01aITxadHatO4Gl2xLniNkDyrEQzJ7w38RQgsVK+CqbnC0K9N77QPbHeC1 +YQd9RCNyUohOimKvb7jyv798FBU1GO5QI2eNgfnnfteSVXhD2iOoTOsCgYEAxJJ+ +8toxJdnLa0uUsAbql6zeNXGbUBMzu3FomKlyuWuq841jS2kIalaO/TRj5hbnE45j +mCjeLgTVO6Ach3Wfk4zrqajqfFJ0zUg/Wexp49lC3RWiV4icBb85Q6bzeJD9Dn9v +hjpfWVkczf/NeA1fGH/pcgfkT6Dm706GFFttLL8CgYBl/HeXk1H47xAiHO4dJKnb +v0B+X8To/RXamF01r+8BpUoOubOQetdyX7ic+d6deuHu8i6LD/GSCeYJZYFR/KVg +AtiW757QYalnq3ZogkhFrVCZP8IRfTPOFBxp752TlyAcrSI7T9pQ47IBe4094KXM +CJWSfPgAJkOxd0iU0XJpmwKBgGfQxuMTgSlwYRKFlD1zKap5TdID8fbUbVnth0Q5 +GbH7vwlp/qrxCdS/aj0n0irOpbOaW9ccnlrHiqY25VpVMLYIkt3DrDOEiNNx+KNR +TItdTwbcSiTYrS4L0/56ydM/H6bsfsXxRjI18hSJqMZiqXqS84OZz2aOn+h7HCzc +LEiZAoGASk20wFvilpRKHq79xxFWiDUPHi0x0pp82dYIEntGQkKUWkbSlhgf3MAi +5NEQTDmXdnB+rVeWIvEi+BXfdnNgdn8eC4zSdtF4sIAhYr5VWZo0WVWDhT7u2ccv +ZBFymiz8lo3gN57wGUCi9pbZqzV1+ZppX6YTNDdDCE0q+KO3Cec= +-----END RSA PRIVATE KEY----- diff --git a/tests/core/test_requests_client/__init__.py b/tests/clients/test_django/__init__.py similarity index 100% rename from tests/core/test_requests_client/__init__.py rename to tests/clients/test_django/__init__.py diff --git a/tests/clients/test_django/settings.py b/tests/clients/test_django/settings.py new file mode 100644 index 00000000..781ea49a --- /dev/null +++ b/tests/clients/test_django/settings.py @@ -0,0 +1,36 @@ +SECRET_KEY = 'django-secret' + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "example.sqlite", + } +} + +MIDDLEWARE = [ + 'django.contrib.sessions.middleware.SessionMiddleware' +] + +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' + +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'unique-snowflake', + } +} + +INSTALLED_APPS=[] + +AUTHLIB_OAUTH_CLIENTS = { + 'dev_overwrite': { + 'client_id': 'dev-client-id', + 'client_secret': 'dev-client-secret', + 'access_token_params': { + 'foo': 'foo-1', + 'bar': 'bar-2' + } + } +} + +USE_TZ = True diff --git a/tests/django/test_client/test_oauth_client.py b/tests/clients/test_django/test_oauth_client.py similarity index 99% rename from tests/django/test_client/test_oauth_client.py rename to tests/clients/test_django/test_oauth_client.py index 08cfbc57..8ec2e323 100644 --- a/tests/django/test_client/test_oauth_client.py +++ b/tests/clients/test_django/test_oauth_client.py @@ -1,11 +1,11 @@ from unittest import mock -from django.test import override_settings from authlib.jose import jwk from authlib.oidc.core.grants.util import generate_id_token from authlib.integrations.django_client import OAuth, OAuthError from authlib.common.urls import urlparse, url_decode -from tests.django.base import TestCase -from tests.client_base import ( +from django.test import override_settings +from tests.django_helper import TestCase +from ..util import ( mock_send_value, get_bearer_token ) diff --git a/tests/django/test_client/__init__.py b/tests/clients/test_flask/__init__.py similarity index 100% rename from tests/django/test_client/__init__.py rename to tests/clients/test_flask/__init__.py diff --git a/tests/flask/test_client/test_oauth_client.py b/tests/clients/test_flask/test_oauth_client.py similarity index 99% rename from tests/flask/test_client/test_oauth_client.py rename to tests/clients/test_flask/test_oauth_client.py index 4d07927f..07898220 100644 --- a/tests/flask/test_client/test_oauth_client.py +++ b/tests/clients/test_flask/test_oauth_client.py @@ -5,8 +5,8 @@ from authlib.integrations.flask_client import OAuth, OAuthError from authlib.integrations.flask_client import FlaskOAuth2App from authlib.common.urls import urlparse, url_decode -from tests.flask.cache import SimpleCache -from tests.client_base import ( +from cachelib import SimpleCache +from ..util import ( mock_send_value, get_bearer_token ) diff --git a/tests/flask/test_client/test_user_mixin.py b/tests/clients/test_flask/test_user_mixin.py similarity index 96% rename from tests/flask/test_client/test_user_mixin.py rename to tests/clients/test_flask/test_user_mixin.py index 6d496020..282f6cee 100644 --- a/tests/flask/test_client/test_user_mixin.py +++ b/tests/clients/test_flask/test_user_mixin.py @@ -4,8 +4,7 @@ from authlib.jose.errors import InvalidClaimError from authlib.integrations.flask_client import OAuth from authlib.oidc.core.grants.util import generate_id_token -from tests.util import read_file_path -from tests.client_base import get_bearer_token +from ..util import get_bearer_token, read_key_file class FlaskUserMixinTest(TestCase): @@ -122,7 +121,7 @@ def test_runtime_error_fetch_jwks_uri(self): self.assertRaises(RuntimeError, client.parse_id_token, token, 'n') def test_force_fetch_jwks_uri(self): - secret_keys = read_file_path('jwks_private.json') + secret_keys = read_key_file('jwks_private.json') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, secret_keys, @@ -145,7 +144,7 @@ def test_force_fetch_jwks_uri(self): def fake_send(sess, req, **kwargs): resp = mock.MagicMock() - resp.json = lambda: read_file_path('jwks_public.json') + resp.json = lambda: read_key_file('jwks_public.json') resp.status_code = 200 return resp diff --git a/tests/flask/test_client/__init__.py b/tests/clients/test_httpx/__init__.py similarity index 100% rename from tests/flask/test_client/__init__.py rename to tests/clients/test_httpx/__init__.py diff --git a/tests/starlette/test_httpx_client/test_assertion_client.py b/tests/clients/test_httpx/test_assertion_client.py similarity index 95% rename from tests/starlette/test_httpx_client/test_assertion_client.py rename to tests/clients/test_httpx/test_assertion_client.py index 5c8ef42d..1e267b82 100644 --- a/tests/starlette/test_httpx_client/test_assertion_client.py +++ b/tests/clients/test_httpx/test_assertion_client.py @@ -1,7 +1,7 @@ import time import pytest from authlib.integrations.httpx_client import AssertionClient -from ..utils import MockDispatch +from ..wsgi_helper import MockDispatch default_token = { @@ -13,7 +13,6 @@ } -@pytest.mark.asyncio def test_refresh_token(): def verifier(request): content = request.form @@ -50,7 +49,6 @@ def verifier(request): client.get('https://i.b') -@pytest.mark.asyncio def test_without_alg(): with AssertionClient( 'https://i.b/token', diff --git a/tests/starlette/test_httpx_client/test_async_assertion_client.py b/tests/clients/test_httpx/test_async_assertion_client.py similarity index 97% rename from tests/starlette/test_httpx_client/test_async_assertion_client.py rename to tests/clients/test_httpx/test_async_assertion_client.py index 67bfa7a5..9087b864 100644 --- a/tests/starlette/test_httpx_client/test_async_assertion_client.py +++ b/tests/clients/test_httpx/test_async_assertion_client.py @@ -1,7 +1,7 @@ import time import pytest from authlib.integrations.httpx_client import AsyncAssertionClient -from ..utils import AsyncMockDispatch +from ..asgi_helper import AsyncMockDispatch default_token = { diff --git a/tests/starlette/test_httpx_client/test_async_oauth1_client.py b/tests/clients/test_httpx/test_async_oauth1_client.py similarity index 99% rename from tests/starlette/test_httpx_client/test_async_oauth1_client.py rename to tests/clients/test_httpx/test_async_oauth1_client.py index c316148a..6500cd9e 100644 --- a/tests/starlette/test_httpx_client/test_async_oauth1_client.py +++ b/tests/clients/test_httpx/test_async_oauth1_client.py @@ -5,7 +5,7 @@ SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY, ) -from ..utils import AsyncMockDispatch +from ..asgi_helper import AsyncMockDispatch oauth_url = 'https://example.com/oauth' diff --git a/tests/starlette/test_httpx_client/test_async_oauth2_client.py b/tests/clients/test_httpx/test_async_oauth2_client.py similarity index 99% rename from tests/starlette/test_httpx_client/test_async_oauth2_client.py rename to tests/clients/test_httpx/test_async_oauth2_client.py index ddc3c790..eaa50bf1 100644 --- a/tests/starlette/test_httpx_client/test_async_oauth2_client.py +++ b/tests/clients/test_httpx/test_async_oauth2_client.py @@ -9,7 +9,7 @@ OAuthError, AsyncOAuth2Client, ) -from ..utils import AsyncMockDispatch +from ..asgi_helper import AsyncMockDispatch default_token = { @@ -21,17 +21,20 @@ } +@pytest.mark.asyncio async def assert_token_in_header(request): token = 'Bearer ' + default_token['access_token'] auth_header = request.headers.get('authorization') assert auth_header == token +@pytest.mark.asyncio async def assert_token_in_body(request): content = await request.body() assert default_token['access_token'] in content.decode() +@pytest.mark.asyncio async def assert_token_in_uri(request): assert default_token['access_token'] in str(request.url) diff --git a/tests/starlette/test_httpx_client/test_oauth1_client.py b/tests/clients/test_httpx/test_oauth1_client.py similarity index 96% rename from tests/starlette/test_httpx_client/test_oauth1_client.py rename to tests/clients/test_httpx/test_oauth1_client.py index a5b9998a..9fb6ecfd 100644 --- a/tests/starlette/test_httpx_client/test_oauth1_client.py +++ b/tests/clients/test_httpx/test_oauth1_client.py @@ -5,12 +5,11 @@ SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY, ) -from ..utils import MockDispatch +from ..wsgi_helper import MockDispatch oauth_url = 'https://example.com/oauth' -@pytest.mark.asyncio def test_fetch_request_token_via_header(): request_token = {'oauth_token': '1', 'oauth_token_secret': '2'} @@ -26,7 +25,6 @@ def assert_func(request): assert response == request_token -@pytest.mark.asyncio def test_fetch_request_token_via_body(): request_token = {'oauth_token': '1', 'oauth_token_secret': '2'} @@ -49,7 +47,6 @@ def assert_func(request): assert response == request_token -@pytest.mark.asyncio def test_fetch_request_token_via_query(): request_token = {'oauth_token': '1', 'oauth_token_secret': '2'} @@ -72,7 +69,6 @@ def assert_func(request): assert response == request_token -@pytest.mark.asyncio def test_fetch_access_token(): request_token = {'oauth_token': '1', 'oauth_token_secret': '2'} @@ -96,7 +92,6 @@ def assert_func(request): assert response == request_token -@pytest.mark.asyncio def test_get_via_header(): mock_response = MockDispatch(b'hello') with OAuth1Client( @@ -113,7 +108,6 @@ def test_get_via_header(): assert 'oauth_signature=' in auth_header -@pytest.mark.asyncio def test_get_via_body(): def assert_func(request): content = request.form @@ -136,7 +130,6 @@ def assert_func(request): assert auth_header is None -@pytest.mark.asyncio def test_get_via_query(): mock_response = MockDispatch(b'hello') with OAuth1Client( diff --git a/tests/starlette/test_httpx_client/test_oauth2_client.py b/tests/clients/test_httpx/test_oauth2_client.py similarity index 99% rename from tests/starlette/test_httpx_client/test_oauth2_client.py rename to tests/clients/test_httpx/test_oauth2_client.py index 3a5c2250..65883e92 100644 --- a/tests/starlette/test_httpx_client/test_oauth2_client.py +++ b/tests/clients/test_httpx/test_oauth2_client.py @@ -8,7 +8,7 @@ OAuthError, OAuth2Client, ) -from ..utils import MockDispatch +from ..wsgi_helper import MockDispatch default_token = { diff --git a/tests/starlette/__init__.py b/tests/clients/test_requests/__init__.py similarity index 100% rename from tests/starlette/__init__.py rename to tests/clients/test_requests/__init__.py diff --git a/tests/core/test_requests_client/test_assertion_session.py b/tests/clients/test_requests/test_assertion_session.py similarity index 100% rename from tests/core/test_requests_client/test_assertion_session.py rename to tests/clients/test_requests/test_assertion_session.py diff --git a/tests/core/test_requests_client/test_oauth1_session.py b/tests/clients/test_requests/test_oauth1_session.py similarity index 98% rename from tests/core/test_requests_client/test_oauth1_session.py rename to tests/clients/test_requests/test_oauth1_session.py index 7aca4127..b12295ea 100644 --- a/tests/core/test_requests_client/test_oauth1_session.py +++ b/tests/clients/test_requests/test_oauth1_session.py @@ -11,8 +11,7 @@ from authlib.oauth1.rfc5849.util import escape from authlib.common.encoding import to_unicode from authlib.integrations.requests_client import OAuth1Session, OAuthError -from tests.client_base import mock_text_response -from tests.util import read_file_path +from ..util import mock_text_response, read_key_file TEST_RSA_OAUTH_SIGNATURE = ( @@ -88,7 +87,7 @@ def test_signature_methods(self, generate_nonce, generate_timestamp): 'oauth_signature="{sig}"' ).format(sig=TEST_RSA_OAUTH_SIGNATURE) - rsa_key = read_file_path('rsa_private.pem') + rsa_key = read_key_file('rsa_private.pem') auth = OAuth1Session( 'foo', signature_method=SIGNATURE_RSA_SHA1, rsa_key=rsa_key) auth.send = self.verify_signature(signature) diff --git a/tests/core/test_requests_client/test_oauth2_session.py b/tests/clients/test_requests/test_oauth2_session.py similarity index 98% rename from tests/core/test_requests_client/test_oauth2_session.py rename to tests/clients/test_requests/test_oauth2_session.py index 6eefdd46..1cbe1709 100644 --- a/tests/core/test_requests_client/test_oauth2_session.py +++ b/tests/clients/test_requests/test_oauth2_session.py @@ -6,8 +6,16 @@ from authlib.integrations.requests_client import OAuth2Session, OAuthError from authlib.oauth2.rfc6749 import MismatchingStateException from authlib.oauth2.rfc7523 import ClientSecretJWT, PrivateKeyJWT -from tests.util import read_file_path -from tests.client_base import mock_json_response +from ..util import read_key_file + + +def mock_json_response(payload): + def fake_send(r, **kwargs): + resp = mock.MagicMock() + resp.json = lambda: payload + return resp + return fake_send + class OAuth2SessionTest(TestCase): @@ -439,7 +447,7 @@ def fake_send(r, **kwargs): self.assertEqual(token, self.token) def test_private_key_jwt(self): - client_secret = read_file_path('rsa_private.pem') + client_secret = read_key_file('rsa_private.pem') sess = OAuth2Session( 'id', client_secret, token_endpoint_auth_method='private_key_jwt' diff --git a/tests/starlette/test_client/__init__.py b/tests/clients/test_starlette/__init__.py similarity index 100% rename from tests/starlette/test_client/__init__.py rename to tests/clients/test_starlette/__init__.py diff --git a/tests/starlette/test_client/test_oauth_client.py b/tests/clients/test_starlette/test_oauth_client.py similarity index 98% rename from tests/starlette/test_client/test_oauth_client.py rename to tests/clients/test_starlette/test_oauth_client.py index 1559181f..6052eca7 100644 --- a/tests/starlette/test_client/test_oauth_client.py +++ b/tests/clients/test_starlette/test_oauth_client.py @@ -3,8 +3,8 @@ from starlette.requests import Request from authlib.common.urls import urlparse, url_decode from authlib.integrations.starlette_client import OAuth, OAuthError -from tests.client_base import get_bearer_token -from ..utils import AsyncPathMapDispatch +from ..asgi_helper import AsyncPathMapDispatch +from ..util import get_bearer_token def test_register_remote_app(): diff --git a/tests/starlette/test_client/test_user_mixin.py b/tests/clients/test_starlette/test_user_mixin.py similarity index 93% rename from tests/starlette/test_client/test_user_mixin.py rename to tests/clients/test_starlette/test_user_mixin.py index 0638e399..451d0b4c 100644 --- a/tests/starlette/test_client/test_user_mixin.py +++ b/tests/clients/test_starlette/test_user_mixin.py @@ -4,9 +4,8 @@ from authlib.jose import jwk from authlib.jose.errors import InvalidClaimError from authlib.oidc.core.grants.util import generate_id_token -from tests.util import read_file_path -from tests.client_base import get_bearer_token -from ..utils import AsyncPathMapDispatch +from ..util import get_bearer_token, read_key_file +from ..asgi_helper import AsyncPathMapDispatch async def run_fetch_userinfo(payload): @@ -102,7 +101,7 @@ async def test_runtime_error_fetch_jwks_uri(): @pytest.mark.asyncio async def test_force_fetch_jwks_uri(): - secret_keys = read_file_path('jwks_private.json') + secret_keys = read_key_file('jwks_private.json') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, secret_keys, @@ -112,7 +111,7 @@ async def test_force_fetch_jwks_uri(): token['id_token'] = id_token app = AsyncPathMapDispatch({ - '/jwks': {'body': read_file_path('jwks_public.json')} + '/jwks': {'body': read_key_file('jwks_public.json')} }) oauth = OAuth() diff --git a/tests/client_base.py b/tests/clients/util.py similarity index 74% rename from tests/client_base.py rename to tests/clients/util.py index 3893460b..8ae77456 100644 --- a/tests/client_base.py +++ b/tests/clients/util.py @@ -1,14 +1,19 @@ -from unittest import mock +import os import time +import json import requests +from unittest import mock -def mock_json_response(payload): - def fake_send(r, **kwargs): - resp = mock.MagicMock() - resp.json = lambda: payload - return resp - return fake_send +ROOT = os.path.abspath(os.path.dirname(__file__)) + + +def read_key_file(name): + file_path = os.path.join(ROOT, 'keys', name) + with open(file_path, 'r') as f: + if name.endswith('.json'): + return json.load(f) + return f.read() def mock_text_response(body, status_code=200): diff --git a/tests/clients/wsgi_helper.py b/tests/clients/wsgi_helper.py new file mode 100644 index 00000000..4651e655 --- /dev/null +++ b/tests/clients/wsgi_helper.py @@ -0,0 +1,35 @@ +import json +from werkzeug.wrappers import Request as WSGIRequest +from werkzeug.wrappers import Response as WSGIResponse + + +class MockDispatch: + def __init__(self, body=b'', status_code=200, headers=None, + assert_func=None): + if headers is None: + headers = {} + if isinstance(body, dict): + body = json.dumps(body).encode() + headers['Content-Type'] = 'application/json' + else: + if isinstance(body, str): + body = body.encode() + headers['Content-Type'] = 'application/x-www-form-urlencoded' + + self.body = body + self.status_code = status_code + self.headers = headers + self.assert_func = assert_func + + def __call__(self, environ, start_response): + request = WSGIRequest(environ) + + if self.assert_func: + self.assert_func(request) + + response = WSGIResponse( + status=self.status_code, + response=self.body, + headers=self.headers, + ) + return response(environ, start_response) diff --git a/tests/django/settings.py b/tests/django/settings.py index 92136d04..be038b29 100644 --- a/tests/django/settings.py +++ b/tests/django/settings.py @@ -27,13 +27,4 @@ 'tests.django.test_oauth2', ] -AUTHLIB_OAUTH_CLIENTS = { - 'dev_overwrite': { - 'client_id': 'dev-client-id', - 'client_secret': 'dev-client-secret', - 'access_token_params': { - 'foo': 'foo-1', - 'bar': 'bar-2' - } - } -} +USE_TZ = True diff --git a/tests/django/test_oauth1/oauth1_server.py b/tests/django/test_oauth1/oauth1_server.py index 2d2bc42f..775dbae8 100644 --- a/tests/django/test_oauth1/oauth1_server.py +++ b/tests/django/test_oauth1/oauth1_server.py @@ -2,8 +2,8 @@ from authlib.integrations.django_oauth1 import ( CacheAuthorizationServer, ) +from tests.django_helper import TestCase as _TestCase from .models import Client, TokenCredential -from ..base import TestCase as _TestCase class TestCase(_TestCase): diff --git a/tests/django/test_oauth2/oauth2_server.py b/tests/django/test_oauth2/oauth2_server.py index ee35c0c9..ff43908a 100644 --- a/tests/django/test_oauth2/oauth2_server.py +++ b/tests/django/test_oauth2/oauth2_server.py @@ -2,8 +2,8 @@ import base64 from authlib.common.encoding import to_bytes, to_unicode from authlib.integrations.django_oauth2 import AuthorizationServer +from tests.django_helper import TestCase as _TestCase from .models import Client, OAuth2Token -from ..base import TestCase as _TestCase class TestCase(_TestCase): diff --git a/tests/django/base.py b/tests/django_helper.py similarity index 100% rename from tests/django/base.py rename to tests/django_helper.py diff --git a/tests/flask/test_oauth2/test_client_registration_endpoint.py b/tests/flask/test_oauth2/test_client_registration_endpoint.py index 0351941f..eb6282dd 100644 --- a/tests/flask/test_oauth2/test_client_registration_endpoint.py +++ b/tests/flask/test_oauth2/test_client_registration_endpoint.py @@ -53,14 +53,14 @@ def create_client(): def test_access_denied(self): self.prepare_data() - rv = self.client.post('/create_client') + rv = self.client.post('/create_client', json={}) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'access_denied') def test_invalid_request(self): self.prepare_data() headers = {'Authorization': 'bearer abc'} - rv = self.client.post('/create_client', headers=headers) + rv = self.client.post('/create_client', json={}, headers=headers) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'invalid_request') diff --git a/tests/starlette/test_httpx_client/__init__.py b/tests/jose/__init__.py similarity index 100% rename from tests/starlette/test_httpx_client/__init__.py rename to tests/jose/__init__.py diff --git a/tests/core/test_jose/test_jwe.py b/tests/jose/test_jwe.py similarity index 100% rename from tests/core/test_jose/test_jwe.py rename to tests/jose/test_jwe.py diff --git a/tests/core/test_jose/test_jwk.py b/tests/jose/test_jwk.py similarity index 100% rename from tests/core/test_jose/test_jwk.py rename to tests/jose/test_jwk.py diff --git a/tests/core/test_jose/test_jws.py b/tests/jose/test_jws.py similarity index 100% rename from tests/core/test_jose/test_jws.py rename to tests/jose/test_jws.py diff --git a/tests/core/test_jose/test_jwt.py b/tests/jose/test_jwt.py similarity index 100% rename from tests/core/test_jose/test_jwt.py rename to tests/jose/test_jwt.py diff --git a/tests/requirements-base.txt b/tests/requirements-base.txt new file mode 100644 index 00000000..f31faea1 --- /dev/null +++ b/tests/requirements-base.txt @@ -0,0 +1,3 @@ +cryptography +pytest +coverage diff --git a/tests/requirements-clients.txt b/tests/requirements-clients.txt new file mode 100644 index 00000000..bd64a30c --- /dev/null +++ b/tests/requirements-clients.txt @@ -0,0 +1,9 @@ +requests +anyio +httpx +starlette +cachelib +werkzeug +flask +django +pytest-asyncio diff --git a/tests/requirements-django.txt b/tests/requirements-django.txt new file mode 100644 index 00000000..a5c251bb --- /dev/null +++ b/tests/requirements-django.txt @@ -0,0 +1,2 @@ +Django +pytest-django diff --git a/tests/requirements-flask.txt b/tests/requirements-flask.txt new file mode 100644 index 00000000..fb675a95 --- /dev/null +++ b/tests/requirements-flask.txt @@ -0,0 +1,2 @@ +Flask +Flask-SQLAlchemy diff --git a/tox.ini b/tox.ini index 35b86078..a2bdad91 100644 --- a/tox.ini +++ b/tox.ini @@ -2,33 +2,30 @@ isolated_build = True envlist = py{37,38,39,310} - py{37,38,39,310}-{flask,django,starlette} + py{37,38,39,310}-{clients,flask,django,jose} coverage [testenv] deps = - -rrequirements-test.txt - flask: Flask - flask: Flask-SQLAlchemy - flask: itsdangerous - flask: werkzeug - starlette: httpx - starlette: starlette - starlette: werkzeug - starlette: pytest-asyncio - django: Django - django: pytest-django + -r tests/requirements-base.txt + jose: pycryptodomex>=3.10,<4 + clients: -r tests/requirements-clients.txt + flask: -r tests/requirements-flask.txt + django: -r tests/requirements-django.txt setenv = TESTPATH=tests/core - starlette: TESTPATH=tests/starlette + jose: TESTPATH=tests/jose + clients: TESTPATH=tests/clients + clients: DJANGO_SETTINGS_MODULE=tests.clients.test_django.settings flask: TESTPATH=tests/flask django: TESTPATH=tests/django + django: DJANGO_SETTINGS_MODULE=tests.django.settings commands = coverage run --source=authlib -p -m pytest {env:TESTPATH} [pytest] -DJANGO_SETTINGS_MODULE=tests.django.settings +asyncio_mode = auto [testenv:coverage] skip_install = true