From a832ac2b82fd1cb8396070040c3961898abc5065 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Mon, 10 Aug 2026 15:12:22 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=85(backend)=20correctly=20reload=20urls?= =?UTF-8?q?=20in=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After removing most of the usage of S3 in the tests, these ones are faster and make some flakyness more relevant. For example, in tests related to the external api we have to reload the urls based on the settings. We now have some race conditions where tests collapsed and urls are not correctly reloaded. --- src/backend/core/tests/conftest.py | 21 +++++++++- .../test_external_api_documents.py | 8 +++- src/backend/core/tests/utils/urls.py | 41 ++++++++++++++++--- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/backend/core/tests/conftest.py b/src/backend/core/tests/conftest.py index b39af92a7..45e0a4dbb 100644 --- a/src/backend/core/tests/conftest.py +++ b/src/backend/core/tests/conftest.py @@ -10,7 +10,7 @@ import responses from core import factories from core.services.yhub_services import YHubService -from core.tests.utils.urls import reload_urls +from core.tests.utils.urls import reload_urls, restore_urls USER = "user" TEAM = "team" @@ -23,6 +23,25 @@ def clear_cache(): cache.clear() +@pytest.fixture(autouse=True) +def restore_urlconf(): + """ + Put the URLs back after a test that reloaded them. + + Reloading is how a test makes the resource server routes appear or checks + that they are absent, but the URLconf belongs to the process: without this, + a test asserting a 404 on `/external_api/` and one asserting a 401 pass or + fail depending on which ran first in their worker. + + Autouse and asking for nothing, so it is set up before the `settings` + fixture and torn down after it: the reload then sees the settings of the + project, not the ones of the test. + """ + yield + + restore_urls() + + @pytest.fixture def mock_user_teams(): """Mock for the "teams" property on the User model.""" diff --git a/src/backend/core/tests/external_api/test_external_api_documents.py b/src/backend/core/tests/external_api/test_external_api_documents.py index b4bb8f1eb..183b092ed 100644 --- a/src/backend/core/tests/external_api/test_external_api_documents.py +++ b/src/backend/core/tests/external_api/test_external_api_documents.py @@ -581,11 +581,17 @@ def test_external_api_documents_trashbin_not_allowed( assert response.status_code == 403 -def test_external_api_documents_create_for_owner_not_allowed(): +def test_external_api_documents_create_for_owner_not_allowed( + resource_server_backend_conf, +): """ Authenticated users SHOULD NOT be allowed to call create documents on behalf of other users. This API endpoint is reserved for server-to-server calls. + + The route only exists when the resource server is enabled, hence the + fixture: the endpoint answering 401 is what this asserts, not the + `/external_api/` prefix being routed at all. """ user = factories.UserFactory() diff --git a/src/backend/core/tests/utils/urls.py b/src/backend/core/tests/utils/urls.py index 78455de1e..2da23f7ee 100644 --- a/src/backend/core/tests/utils/urls.py +++ b/src/backend/core/tests/utils/urls.py @@ -5,16 +5,47 @@ import importlib from django.urls import clear_url_caches +class _URLConf: + """ + Whether a test reloaded the URLs of this process. + + The URLconf is module-level state: a reload outlives the test that did it + and every test running after it in the same worker sees its routes — which + ones share a worker changes from one run to the next. `restore_urls` puts + the default back, and this flag keeps it to the tests that need it. + """ + + reloaded = False + + +def _reload(): + """Reload the URL modules and drop the resolver caches.""" + import core.urls # pylint:disable=import-outside-toplevel # noqa: PLC0415 + + import impress.urls # pylint:disable=import-outside-toplevel # noqa: PLC0415 + + importlib.reload(core.urls) + importlib.reload(impress.urls) + clear_url_caches() + + def reload_urls(): """ Reload the URLs. Since the URLs are loaded based on a settings value, we need to reload them to make the URL settings based condition effective. """ - import core.urls # pylint:disable=import-outside-toplevel # noqa: PLC0415 + _URLConf.reloaded = True + _reload() - import impress.urls # pylint:disable=import-outside-toplevel # noqa: PLC0415 - importlib.reload(core.urls) - importlib.reload(impress.urls) - clear_url_caches() +def restore_urls(): + """ + Reload the URLs of a test that changed them, so the next one starts clean. + + Called once the settings of the test are restored, so the routes are the + ones the settings of the project declare. + """ + if _URLConf.reloaded: + _URLConf.reloaded = False + _reload()