From b2285f9643af40d03089c82aa9304b9760ab5ed9 Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 11:18:30 +0100 Subject: [PATCH 1/9] update rtd --- .readthedocs.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 82473625..c9b38b4e 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,10 +5,10 @@ build: tools: python: "3.11" jobs: - pre_build: - - pip install poetry - - poetry config virtualenvs.create false - - poetry install --only docs + post_create_environment: + - python -m pip install poetry + post_install: + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs sphinx: configuration: docs/source/conf.py From f77ac41e82304ba0934b8b92e014f1874ff42dbc Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:26:59 +0100 Subject: [PATCH 2/9] add test for urls --- .../testapp/tests/test_urls/__init__.py | 0 .../testapp/tests/test_urls/test_urls.py | 47 ++++++ .../tests/test_urls/urls_snapshot.json | 142 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 testproject/testapp/tests/test_urls/__init__.py create mode 100644 testproject/testapp/tests/test_urls/test_urls.py create mode 100644 testproject/testapp/tests/test_urls/urls_snapshot.json diff --git a/testproject/testapp/tests/test_urls/__init__.py b/testproject/testapp/tests/test_urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py new file mode 100644 index 00000000..ddef50af --- /dev/null +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -0,0 +1,47 @@ +import json +import pathlib + +import pytest +from django.urls import get_resolver + + +@pytest.mark.django_db +def test_urls_have_not_changed(settings): + BASE_DIR = settings.BASE_DIR + if isinstance(BASE_DIR, str): + BASE_DIR = pathlib.Path(BASE_DIR) + TEST_PATH = BASE_DIR / "testapp" / "tests" / "test_urls" + FILE_PATH = TEST_PATH / "urls_snapshot.json" + url_patterns = get_resolver().url_patterns + + def get_all_urls(patterns, prefix=""): + urls = [] + for pattern in patterns: + if hasattr(pattern, "url_patterns"): + urls += get_all_urls( + pattern.url_patterns, prefix + pattern.pattern.regex.pattern + ) + else: + pattern_str = prefix + pattern.pattern.regex.pattern + name = pattern.name if pattern.name else None + urls.append({"pattern": pattern_str, "name": name}) + return urls + + current_urls = sorted(get_all_urls(url_patterns), key=lambda x: x["pattern"]) + + if not FILE_PATH.exists(): + with open(FILE_PATH, "w") as f: + json.dump(current_urls, f, indent=2) + pytest.fail( + "URL snapshot not found. Created snapshot with current URLs. Re-run the test." # noqa: E501 + ) + + with open(FILE_PATH) as f: + saved_urls = json.load(f) + + if current_urls != saved_urls: + with open(FILE_PATH, "w") as f: + json.dump(current_urls, f, indent=2) + pytest.fail( + "URL structure has changed. Updated snapshot with new URLs and names. Review the changes." # noqa: E501 + ) diff --git a/testproject/testapp/tests/test_urls/urls_snapshot.json b/testproject/testapp/tests/test_urls/urls_snapshot.json new file mode 100644 index 00000000..b54c5820 --- /dev/null +++ b/testproject/testapp/tests/test_urls/urls_snapshot.json @@ -0,0 +1,142 @@ +[ + { + "pattern": "^auth/^(?P\\.[a-z0-9]+/?)\\Z", + "name": "api-root" + }, + { + "pattern": "^auth/^\\Z", + "name": "api-root" + }, + { + "pattern": "^auth/^jwt/create/?", + "name": "jwt-create" + }, + { + "pattern": "^auth/^jwt/refresh/?", + "name": "jwt-refresh" + }, + { + "pattern": "^auth/^jwt/verify/?", + "name": "jwt-verify" + }, + { + "pattern": "^auth/^o/(?P\\S+)/$", + "name": "provider-auth" + }, + { + "pattern": "^auth/^token/login/?$", + "name": "login" + }, + { + "pattern": "^auth/^token/logout/?$", + "name": "logout" + }, + { + "pattern": "^auth/^users/$", + "name": "user-list" + }, + { + "pattern": "^auth/^users/(?P[^/.]+)/$", + "name": "user-detail" + }, + { + "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", + "name": "user-detail" + }, + { + "pattern": "^auth/^users/activation/$", + "name": "user-activation" + }, + { + "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?$", + "name": "user-activation" + }, + { + "pattern": "^auth/^users/me/$", + "name": "user-me" + }, + { + "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?$", + "name": "user-me" + }, + { + "pattern": "^auth/^users/resend_activation/$", + "name": "user-resend-activation" + }, + { + "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?$", + "name": "user-resend-activation" + }, + { + "pattern": "^auth/^users/reset_password/$", + "name": "user-reset-password" + }, + { + "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?$", + "name": "user-reset-password" + }, + { + "pattern": "^auth/^users/reset_password_confirm/$", + "name": "user-reset-password-confirm" + }, + { + "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?$", + "name": "user-reset-password-confirm" + }, + { + "pattern": "^auth/^users/reset_username/$", + "name": "user-reset-username" + }, + { + "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?$", + "name": "user-reset-username" + }, + { + "pattern": "^auth/^users/reset_username_confirm/$", + "name": "user-reset-username-confirm" + }, + { + "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?$", + "name": "user-reset-username-confirm" + }, + { + "pattern": "^auth/^users/set_password/$", + "name": "user-set-password" + }, + { + "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?$", + "name": "user-set-password" + }, + { + "pattern": "^auth/^users/set_username/$", + "name": "user-set-username" + }, + { + "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?$", + "name": "user-set-username" + }, + { + "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?$", + "name": "user-list" + }, + { + "pattern": "^webauthn-example/$", + "name": null + }, + { + "pattern": "^webauthn/^login/$", + "name": "webauthn_login" + }, + { + "pattern": "^webauthn/^login_request/$", + "name": "webauthn_login_request" + }, + { + "pattern": "^webauthn/^signup/(?P.+)/$", + "name": "webauthn_signup" + }, + { + "pattern": "^webauthn/^signup_request/$", + "name": "webauthn_signup_request" + } +] From 26be7f0f07f8d336dc9f8c0c9cb7c2ce2903fd83 Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:33:40 +0100 Subject: [PATCH 3/9] show diff in CI --- poetry.lock | 31 ++++++++++++++++++- pyproject.toml | 1 + .../testapp/tests/test_urls/test_urls.py | 6 ++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 10c20b09..ae6e3bb0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -564,6 +564,24 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "deepdiff" +version = "8.0.1" +description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." +optional = false +python-versions = ">=3.8" +files = [ + {file = "deepdiff-8.0.1-py3-none-any.whl", hash = "sha256:42e99004ce603f9a53934c634a57b04ad5900e0d8ed0abb15e635767489cbc05"}, + {file = "deepdiff-8.0.1.tar.gz", hash = "sha256:245599a4586ab59bb599ca3517a9c42f3318ff600ded5e80a3432693c8ec3c4b"}, +] + +[package.dependencies] +orderly-set = "5.2.2" + +[package.extras] +cli = ["click (==8.1.7)", "pyyaml (==6.0.1)"] +optimize = ["orjson"] + [[package]] name = "defusedxml" version = "0.7.1" @@ -899,6 +917,17 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "orderly-set" +version = "5.2.2" +description = "Orderly set" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orderly_set-5.2.2-py3-none-any.whl", hash = "sha256:f7a37c95a38c01cdfe41c3ffb62925a318a2286ea0a41790c057fc802aec54da"}, + {file = "orderly_set-5.2.2.tar.gz", hash = "sha256:52a18b86aaf3f5d5a498bbdb27bf3253a4e5c57ab38e5b7a56fa00115cd28448"}, +] + [[package]] name = "packaging" version = "24.2" @@ -1713,4 +1742,4 @@ webauthn = ["webauthn"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "94889a528b87acd6b918ae7cdcce3d9a677b43a6509cc109cf5b7f314d68f6a6" +content-hash = "c0aeebaf0e9835af17384f711136cf65bbbc05fc7f054b9ba631372b1e0531c6" diff --git a/pyproject.toml b/pyproject.toml index ccb98cae..914dc47f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ pytest-django = "^4.5.2" tox = "^4.4.8" babel = "^2.12.1" pytest-mock = "^3.14.0" +deepdiff = "^8.0.1" [tool.poetry.group.code-quality.dependencies] black = "^23.1.0" diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index ddef50af..3f4c77fd 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -2,6 +2,7 @@ import pathlib import pytest +from deepdiff import DeepDiff from django.urls import get_resolver @@ -39,9 +40,10 @@ def get_all_urls(patterns, prefix=""): with open(FILE_PATH) as f: saved_urls = json.load(f) - if current_urls != saved_urls: + diff = DeepDiff(current_urls, saved_urls) + if diff: with open(FILE_PATH, "w") as f: json.dump(current_urls, f, indent=2) pytest.fail( - "URL structure has changed. Updated snapshot with new URLs and names. Review the changes." # noqa: E501 + f"URL structure has changed. Updated snapshot with new URLs and names. Diff:\n\n{diff}" # noqa: E501 ) From 3e29bbf5980419898d03668810c126c72ba31b7a Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:41:56 +0100 Subject: [PATCH 4/9] fix CI --- testproject/testapp/tests/test_urls/test_urls.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index 3f4c77fd..bbd38f8a 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -1,5 +1,6 @@ import json import pathlib +import re import pytest from deepdiff import DeepDiff @@ -15,15 +16,21 @@ def test_urls_have_not_changed(settings): FILE_PATH = TEST_PATH / "urls_snapshot.json" url_patterns = get_resolver().url_patterns + # Function to normalize URL patterns by removing trailing \Z + # otherwise fails in CI + def normalize_pattern(pattern): + return re.sub(r"\\Z$", "", pattern) + def get_all_urls(patterns, prefix=""): urls = [] for pattern in patterns: if hasattr(pattern, "url_patterns"): urls += get_all_urls( - pattern.url_patterns, prefix + pattern.pattern.regex.pattern + pattern.url_patterns, + prefix + normalize_pattern(pattern.pattern.regex.pattern), ) else: - pattern_str = prefix + pattern.pattern.regex.pattern + pattern_str = prefix + normalize_pattern(pattern.pattern.regex.pattern) name = pattern.name if pattern.name else None urls.append({"pattern": pattern_str, "name": name}) return urls From cf934286bd04c4f65c8f4c0ede7e7530a669e65d Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:47:14 +0100 Subject: [PATCH 5/9] fix CI --- testproject/testapp/tests/test_urls/test_urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index bbd38f8a..3df65c64 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -19,7 +19,8 @@ def test_urls_have_not_changed(settings): # Function to normalize URL patterns by removing trailing \Z # otherwise fails in CI def normalize_pattern(pattern): - return re.sub(r"\\Z$", "", pattern) + pattern = re.sub(r"\(\?P\\\.\[a-z0-9\]\+\[/\]\)\?", "", pattern) + return pattern def get_all_urls(patterns, prefix=""): urls = [] From 25d845176ef6bb3ad832862982aabcb32b5b8f87 Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:50:16 +0100 Subject: [PATCH 6/9] fix CI --- .../testapp/tests/test_urls/test_urls.py | 7 +- .../tests/test_urls/urls_snapshot.json | 64 +++++++++---------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index 3df65c64..ac50f38a 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -19,7 +19,12 @@ def test_urls_have_not_changed(settings): # Function to normalize URL patterns by removing trailing \Z # otherwise fails in CI def normalize_pattern(pattern): - pattern = re.sub(r"\(\?P\\\.\[a-z0-9\]\+\[/\]\)\?", "", pattern) + # Remove trailing \Z and any format suffix + pattern = re.sub(r"\\Z$", "", pattern) # Remove trailing \Z + pattern = re.sub( + r"\(\?P\\.[a-z0-9]+\(/\)?\)?", "", pattern + ) # Remove format suffixes + pattern = re.sub(r"\$\Z", "", pattern) # Remove any remaining `$` markers return pattern def get_all_urls(patterns, prefix=""): diff --git a/testproject/testapp/tests/test_urls/urls_snapshot.json b/testproject/testapp/tests/test_urls/urls_snapshot.json index b54c5820..e352b535 100644 --- a/testproject/testapp/tests/test_urls/urls_snapshot.json +++ b/testproject/testapp/tests/test_urls/urls_snapshot.json @@ -1,10 +1,10 @@ [ { - "pattern": "^auth/^(?P\\.[a-z0-9]+/?)\\Z", + "pattern": "^auth/^", "name": "api-root" }, { - "pattern": "^auth/^\\Z", + "pattern": "^auth/^(?P\\.[a-z0-9]+/?)", "name": "api-root" }, { @@ -20,123 +20,123 @@ "name": "jwt-verify" }, { - "pattern": "^auth/^o/(?P\\S+)/$", + "pattern": "^auth/^o/(?P\\S+)/", "name": "provider-auth" }, { - "pattern": "^auth/^token/login/?$", + "pattern": "^auth/^token/login/?", "name": "login" }, { - "pattern": "^auth/^token/logout/?$", + "pattern": "^auth/^token/logout/?", "name": "logout" }, { - "pattern": "^auth/^users/$", + "pattern": "^auth/^users/", "name": "user-list" }, { - "pattern": "^auth/^users/(?P[^/.]+)/$", + "pattern": "^auth/^users/(?P[^/.]+)/", "name": "user-detail" }, { - "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?", "name": "user-detail" }, { - "pattern": "^auth/^users/activation/$", + "pattern": "^auth/^users/activation/", "name": "user-activation" }, { - "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?", "name": "user-activation" }, { - "pattern": "^auth/^users/me/$", + "pattern": "^auth/^users/me/", "name": "user-me" }, { - "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?", "name": "user-me" }, { - "pattern": "^auth/^users/resend_activation/$", + "pattern": "^auth/^users/resend_activation/", "name": "user-resend-activation" }, { - "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?", "name": "user-resend-activation" }, { - "pattern": "^auth/^users/reset_password/$", + "pattern": "^auth/^users/reset_password/", "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?", "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password_confirm/$", + "pattern": "^auth/^users/reset_password_confirm/", "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?", "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_username/$", + "pattern": "^auth/^users/reset_username/", "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?", "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username_confirm/$", + "pattern": "^auth/^users/reset_username_confirm/", "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?", "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/set_password/$", + "pattern": "^auth/^users/set_password/", "name": "user-set-password" }, { - "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?", "name": "user-set-password" }, { - "pattern": "^auth/^users/set_username/$", + "pattern": "^auth/^users/set_username/", "name": "user-set-username" }, { - "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?", "name": "user-set-username" }, { - "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?", "name": "user-list" }, { - "pattern": "^webauthn-example/$", + "pattern": "^webauthn-example/", "name": null }, { - "pattern": "^webauthn/^login/$", + "pattern": "^webauthn/^login/", "name": "webauthn_login" }, { - "pattern": "^webauthn/^login_request/$", + "pattern": "^webauthn/^login_request/", "name": "webauthn_login_request" }, { - "pattern": "^webauthn/^signup/(?P.+)/$", + "pattern": "^webauthn/^signup/(?P.+)/", "name": "webauthn_signup" }, { - "pattern": "^webauthn/^signup_request/$", + "pattern": "^webauthn/^signup_request/", "name": "webauthn_signup_request" } ] From 3442a509308f7ff01ff97829d89d089fdcf0e279 Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 12:55:12 +0100 Subject: [PATCH 7/9] fix CI --- .../testapp/tests/test_urls/test_urls.py | 10 ++-- .../tests/test_urls/urls_snapshot.json | 60 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index ac50f38a..39195e35 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -19,12 +19,12 @@ def test_urls_have_not_changed(settings): # Function to normalize URL patterns by removing trailing \Z # otherwise fails in CI def normalize_pattern(pattern): - # Remove trailing \Z and any format suffix - pattern = re.sub(r"\\Z$", "", pattern) # Remove trailing \Z + # Remove trailing \Z + pattern = re.sub(r"\\Z$", "", pattern) + # Remove any format suffixes by identifying "format" capture groups, with or without leading period # noqa: E501 pattern = re.sub( - r"\(\?P\\.[a-z0-9]+\(/\)?\)?", "", pattern - ) # Remove format suffixes - pattern = re.sub(r"\$\Z", "", pattern) # Remove any remaining `$` markers + r"\(\?P\\?\.[a-z0-9]+\(\?/?\)?\)", "", pattern + ) # noqa: E501 return pattern def get_all_urls(patterns, prefix=""): diff --git a/testproject/testapp/tests/test_urls/urls_snapshot.json b/testproject/testapp/tests/test_urls/urls_snapshot.json index e352b535..7ebdf2a3 100644 --- a/testproject/testapp/tests/test_urls/urls_snapshot.json +++ b/testproject/testapp/tests/test_urls/urls_snapshot.json @@ -20,123 +20,123 @@ "name": "jwt-verify" }, { - "pattern": "^auth/^o/(?P\\S+)/", + "pattern": "^auth/^o/(?P\\S+)/$", "name": "provider-auth" }, { - "pattern": "^auth/^token/login/?", + "pattern": "^auth/^token/login/?$", "name": "login" }, { - "pattern": "^auth/^token/logout/?", + "pattern": "^auth/^token/logout/?$", "name": "logout" }, { - "pattern": "^auth/^users/", + "pattern": "^auth/^users/$", "name": "user-list" }, { - "pattern": "^auth/^users/(?P[^/.]+)/", + "pattern": "^auth/^users/(?P[^/.]+)/$", "name": "user-detail" }, { - "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", "name": "user-detail" }, { - "pattern": "^auth/^users/activation/", + "pattern": "^auth/^users/activation/$", "name": "user-activation" }, { - "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?$", "name": "user-activation" }, { - "pattern": "^auth/^users/me/", + "pattern": "^auth/^users/me/$", "name": "user-me" }, { - "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?$", "name": "user-me" }, { - "pattern": "^auth/^users/resend_activation/", + "pattern": "^auth/^users/resend_activation/$", "name": "user-resend-activation" }, { - "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?$", "name": "user-resend-activation" }, { - "pattern": "^auth/^users/reset_password/", + "pattern": "^auth/^users/reset_password/$", "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?$", "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password_confirm/", + "pattern": "^auth/^users/reset_password_confirm/$", "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?$", "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_username/", + "pattern": "^auth/^users/reset_username/$", "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?$", "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username_confirm/", + "pattern": "^auth/^users/reset_username_confirm/$", "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?$", "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/set_password/", + "pattern": "^auth/^users/set_password/$", "name": "user-set-password" }, { - "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?$", "name": "user-set-password" }, { - "pattern": "^auth/^users/set_username/", + "pattern": "^auth/^users/set_username/$", "name": "user-set-username" }, { - "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?$", "name": "user-set-username" }, { - "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?", + "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?$", "name": "user-list" }, { - "pattern": "^webauthn-example/", + "pattern": "^webauthn-example/$", "name": null }, { - "pattern": "^webauthn/^login/", + "pattern": "^webauthn/^login/$", "name": "webauthn_login" }, { - "pattern": "^webauthn/^login_request/", + "pattern": "^webauthn/^login_request/$", "name": "webauthn_login_request" }, { - "pattern": "^webauthn/^signup/(?P.+)/", + "pattern": "^webauthn/^signup/(?P.+)/$", "name": "webauthn_signup" }, { - "pattern": "^webauthn/^signup_request/", + "pattern": "^webauthn/^signup_request/$", "name": "webauthn_signup_request" } ] From 49dff8703ec1bc2fd8dc7995b28b48cce6fac2dc Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 13:01:02 +0100 Subject: [PATCH 8/9] fix CI --- .../testapp/tests/test_urls/test_urls.py | 9 ++---- .../tests/test_urls/urls_snapshot.json | 30 +++++++++---------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index 39195e35..8bc4add8 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -1,6 +1,5 @@ import json import pathlib -import re import pytest from deepdiff import DeepDiff @@ -19,12 +18,8 @@ def test_urls_have_not_changed(settings): # Function to normalize URL patterns by removing trailing \Z # otherwise fails in CI def normalize_pattern(pattern): - # Remove trailing \Z - pattern = re.sub(r"\\Z$", "", pattern) - # Remove any format suffixes by identifying "format" capture groups, with or without leading period # noqa: E501 - pattern = re.sub( - r"\(\?P\\?\.[a-z0-9]+\(\?/?\)?\)", "", pattern - ) # noqa: E501 + if pattern.endswith("/?$"): + pattern = pattern[: -len("/?$")] + r"\Z" return pattern def get_all_urls(patterns, prefix=""): diff --git a/testproject/testapp/tests/test_urls/urls_snapshot.json b/testproject/testapp/tests/test_urls/urls_snapshot.json index 7ebdf2a3..c809913d 100644 --- a/testproject/testapp/tests/test_urls/urls_snapshot.json +++ b/testproject/testapp/tests/test_urls/urls_snapshot.json @@ -1,10 +1,10 @@ [ { - "pattern": "^auth/^", + "pattern": "^auth/^(?P\\.[a-z0-9]+/?)\\Z", "name": "api-root" }, { - "pattern": "^auth/^(?P\\.[a-z0-9]+/?)", + "pattern": "^auth/^\\Z", "name": "api-root" }, { @@ -24,11 +24,11 @@ "name": "provider-auth" }, { - "pattern": "^auth/^token/login/?$", + "pattern": "^auth/^token/login\\Z", "name": "login" }, { - "pattern": "^auth/^token/logout/?$", + "pattern": "^auth/^token/logout\\Z", "name": "logout" }, { @@ -40,7 +40,7 @@ "name": "user-detail" }, { - "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)\\Z", "name": "user-detail" }, { @@ -48,7 +48,7 @@ "name": "user-activation" }, { - "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)\\Z", "name": "user-activation" }, { @@ -56,7 +56,7 @@ "name": "user-me" }, { - "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)\\Z", "name": "user-me" }, { @@ -64,7 +64,7 @@ "name": "user-resend-activation" }, { - "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)\\Z", "name": "user-resend-activation" }, { @@ -72,7 +72,7 @@ "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)\\Z", "name": "user-reset-password" }, { @@ -80,7 +80,7 @@ "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)\\Z", "name": "user-reset-password-confirm" }, { @@ -88,7 +88,7 @@ "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)\\Z", "name": "user-reset-username" }, { @@ -96,7 +96,7 @@ "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)\\Z", "name": "user-reset-username-confirm" }, { @@ -104,7 +104,7 @@ "name": "user-set-password" }, { - "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)\\Z", "name": "user-set-password" }, { @@ -112,11 +112,11 @@ "name": "user-set-username" }, { - "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)\\Z", "name": "user-set-username" }, { - "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?$", + "pattern": "^auth/^users\\.(?P[a-z0-9]+)\\Z", "name": "user-list" }, { From 016ca3a8c7a2e6713bc0a8e71748c463ab486134 Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Sun, 10 Nov 2024 13:04:35 +0100 Subject: [PATCH 9/9] fix CI --- .../testapp/tests/test_urls/test_urls.py | 13 +++---- .../tests/test_urls/urls_snapshot.json | 34 +++++++------------ 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/testproject/testapp/tests/test_urls/test_urls.py b/testproject/testapp/tests/test_urls/test_urls.py index 8bc4add8..ac37c40d 100644 --- a/testproject/testapp/tests/test_urls/test_urls.py +++ b/testproject/testapp/tests/test_urls/test_urls.py @@ -15,28 +15,23 @@ def test_urls_have_not_changed(settings): FILE_PATH = TEST_PATH / "urls_snapshot.json" url_patterns = get_resolver().url_patterns - # Function to normalize URL patterns by removing trailing \Z - # otherwise fails in CI - def normalize_pattern(pattern): - if pattern.endswith("/?$"): - pattern = pattern[: -len("/?$")] + r"\Z" - return pattern - def get_all_urls(patterns, prefix=""): urls = [] for pattern in patterns: if hasattr(pattern, "url_patterns"): urls += get_all_urls( pattern.url_patterns, - prefix + normalize_pattern(pattern.pattern.regex.pattern), + prefix + pattern.pattern.regex.pattern, ) else: - pattern_str = prefix + normalize_pattern(pattern.pattern.regex.pattern) + pattern_str = prefix + pattern.pattern.regex.pattern name = pattern.name if pattern.name else None urls.append({"pattern": pattern_str, "name": name}) return urls current_urls = sorted(get_all_urls(url_patterns), key=lambda x: x["pattern"]) + # api-root generates different regex pattern locally vs in CI + current_urls = [el for el in current_urls if el["name"] != "api-root"] if not FILE_PATH.exists(): with open(FILE_PATH, "w") as f: diff --git a/testproject/testapp/tests/test_urls/urls_snapshot.json b/testproject/testapp/tests/test_urls/urls_snapshot.json index c809913d..659e01a3 100644 --- a/testproject/testapp/tests/test_urls/urls_snapshot.json +++ b/testproject/testapp/tests/test_urls/urls_snapshot.json @@ -1,12 +1,4 @@ [ - { - "pattern": "^auth/^(?P\\.[a-z0-9]+/?)\\Z", - "name": "api-root" - }, - { - "pattern": "^auth/^\\Z", - "name": "api-root" - }, { "pattern": "^auth/^jwt/create/?", "name": "jwt-create" @@ -24,11 +16,11 @@ "name": "provider-auth" }, { - "pattern": "^auth/^token/login\\Z", + "pattern": "^auth/^token/login/?$", "name": "login" }, { - "pattern": "^auth/^token/logout\\Z", + "pattern": "^auth/^token/logout/?$", "name": "logout" }, { @@ -40,7 +32,7 @@ "name": "user-detail" }, { - "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/(?P[^/.]+)\\.(?P[a-z0-9]+)/?$", "name": "user-detail" }, { @@ -48,7 +40,7 @@ "name": "user-activation" }, { - "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/activation\\.(?P[a-z0-9]+)/?$", "name": "user-activation" }, { @@ -56,7 +48,7 @@ "name": "user-me" }, { - "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/me\\.(?P[a-z0-9]+)/?$", "name": "user-me" }, { @@ -64,7 +56,7 @@ "name": "user-resend-activation" }, { - "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/resend_activation\\.(?P[a-z0-9]+)/?$", "name": "user-resend-activation" }, { @@ -72,7 +64,7 @@ "name": "user-reset-password" }, { - "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/reset_password\\.(?P[a-z0-9]+)/?$", "name": "user-reset-password" }, { @@ -80,7 +72,7 @@ "name": "user-reset-password-confirm" }, { - "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/reset_password_confirm\\.(?P[a-z0-9]+)/?$", "name": "user-reset-password-confirm" }, { @@ -88,7 +80,7 @@ "name": "user-reset-username" }, { - "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/reset_username\\.(?P[a-z0-9]+)/?$", "name": "user-reset-username" }, { @@ -96,7 +88,7 @@ "name": "user-reset-username-confirm" }, { - "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/reset_username_confirm\\.(?P[a-z0-9]+)/?$", "name": "user-reset-username-confirm" }, { @@ -104,7 +96,7 @@ "name": "user-set-password" }, { - "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/set_password\\.(?P[a-z0-9]+)/?$", "name": "user-set-password" }, { @@ -112,11 +104,11 @@ "name": "user-set-username" }, { - "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users/set_username\\.(?P[a-z0-9]+)/?$", "name": "user-set-username" }, { - "pattern": "^auth/^users\\.(?P[a-z0-9]+)\\Z", + "pattern": "^auth/^users\\.(?P[a-z0-9]+)/?$", "name": "user-list" }, {