diff --git a/backend/.dockerignore b/backend/.dockerignore index 566d89485a..d1136a4a87 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -12,6 +12,7 @@ sonar-project.properties # tests **/tests* +!tests/factories* # celery broker/* diff --git a/backend/tests/plugins.py b/backend/tests/plugins.py index c7ef30b5ba..3f992b5a55 100644 --- a/backend/tests/plugins.py +++ b/backend/tests/plugins.py @@ -1,70 +1,13 @@ from __future__ import annotations -import contextlib -import datetime -from typing import Any -from unittest.mock import patch - import pytest @pytest.hookimpl(tryfirst=True) def pytest_load_initial_conftests(early_config: pytest.Config, parser: pytest.Parser, args: list[str]) -> None: - setup_now_tt() - - -def setup_now_tt(): - """ - Setup NowTT for tests so that when freezegun is used, - it will also setup the offset in the database for NowTT. - """ - from freezegun.api import _freeze_time # noqa: PLC2701 - - freezes: dict[int, int] = {} - - class MockFreezeTime(_freeze_time): - def start(self) -> Any: - """Called when 'freeze_time' is started.""" - from utils.db import NowTT - - # Calculate offset for time travel. - delta = self.time_to_freeze - datetime.datetime.now() - offset = int(delta.total_seconds()) - - # Set offset for to database, but ignore errors if test doesn't have database access. - with contextlib.suppress(RuntimeError): - NowTT.set_offset(seconds=offset) + from utils.utils import setup_now_tt - # Save offset in case we make multiple calls to 'freeze_time'. - freezes[id(self)] = offset - - return super().start() - - def stop(self) -> None: - """Called when 'freeze_time' is stopped.""" - from utils.db import NowTT - - # Remove the saved offset. - del freezes[id(self)] - - # If there are no more freezes, reset the offset to 0. - # Otherwise, use the last set offset. - if freezes: - key, value = freezes.popitem() - freezes[key] = value - else: - value = 0 - - # Set offset for to database, but ignore errors if test doesn't have database access. - with contextlib.suppress(RuntimeError): - NowTT.set_offset(seconds=value) - - return super().stop() - - # Just apply the patch for the whole duration of the test run. - mocked_freeze_factory = patch("freezegun.api._freeze_time", MockFreezeTime) - mocked_freeze_factory.start() - return mocked_freeze_factory + setup_now_tt() @pytest.hookimpl() diff --git a/backend/tilavarauspalvelu/api/frontend_testing_api/helpers.py b/backend/tilavarauspalvelu/api/frontend_testing_api/helpers.py index 3ab5ee59f0..ac8d57e1a9 100644 --- a/backend/tilavarauspalvelu/api/frontend_testing_api/helpers.py +++ b/backend/tilavarauspalvelu/api/frontend_testing_api/helpers.py @@ -5,7 +5,7 @@ from freezegun import freeze_time -from tests.plugins import setup_now_tt +from utils.utils import setup_now_tt if TYPE_CHECKING: import datetime diff --git a/backend/utils/utils.py b/backend/utils/utils.py index e08c98898d..0df13cb791 100644 --- a/backend/utils/utils.py +++ b/backend/utils/utils.py @@ -1,6 +1,7 @@ from __future__ import annotations import base64 +import contextlib import datetime import hashlib import hmac @@ -9,6 +10,7 @@ import re import urllib.parse from typing import TYPE_CHECKING, Any, Generic, TypeVar +from unittest.mock import patch from django.conf import settings from django.core.cache import cache @@ -28,6 +30,7 @@ __all__ = [ "comma_sep_str", "get_text_search_language", + "setup_now_tt", "update_query_params", "with_indices", ] @@ -262,3 +265,57 @@ def get_jwt_payload(json_web_token: str) -> dict[str, Any]: payload_part += "=" * divmod(len(payload_part), 4)[1] # Add padding to the payload if needed payload: str = base64.urlsafe_b64decode(payload_part).decode() # Decode the payload return json.loads(payload) # Return the payload as a dict + + +def setup_now_tt() -> Any: + """ + Setup NowTT for tests so that when freezegun is used, + it will also setup the offset in the database for NowTT. + """ + from freezegun.api import _freeze_time # noqa: PLC2701 + + freezes: dict[int, int] = {} + + class MockFreezeTime(_freeze_time): + def start(self) -> Any: + """Called when 'freeze_time' is started.""" + from utils.db import NowTT + + # Calculate offset for time travel. + delta = self.time_to_freeze - local_datetime() + offset = int(delta.total_seconds()) + + # Set offset for to database, but ignore errors if test doesn't have database access. + with contextlib.suppress(RuntimeError): + NowTT.set_offset(seconds=offset) + + # Save offset in case we make multiple calls to 'freeze_time'. + freezes[id(self)] = offset + + return super().start() + + def stop(self) -> None: + """Called when 'freeze_time' is stopped.""" + from utils.db import NowTT + + # Remove the saved offset. + del freezes[id(self)] + + # If there are no more freezes, reset the offset to 0. + # Otherwise, use the last set offset. + if freezes: + key, value = freezes.popitem() + freezes[key] = value + else: + value = 0 + + # Set offset for to database, but ignore errors if test doesn't have database access. + with contextlib.suppress(RuntimeError): + NowTT.set_offset(seconds=value) + + return super().stop() + + # Just apply the patch for the whole duration of the test run. + mocked_freeze_factory = patch("freezegun.api._freeze_time", MockFreezeTime) + mocked_freeze_factory.start() + return mocked_freeze_factory diff --git a/docker-compose.yml b/docker-compose.yml index 48855a5d42..83e184dd56 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,6 +62,8 @@ services: build: context: backend/ dockerfile: docker/Dockerfile + args: + DEPS: main,admin,celery,test,lint env_file: - backend/.env environment: