diff --git a/packages/pytest-simcore/src/pytest_simcore/services_api_mocks_for_aiohttp_clients.py b/packages/pytest-simcore/src/pytest_simcore/services_api_mocks_for_aiohttp_clients.py index 5b85a036d79..a2b46d06679 100644 --- a/packages/pytest-simcore/src/pytest_simcore/services_api_mocks_for_aiohttp_clients.py +++ b/packages/pytest-simcore/src/pytest_simcore/services_api_mocks_for_aiohttp_clients.py @@ -172,9 +172,6 @@ async def director_v2_service_mock( r"^http://[a-z\-_]*director-v2:[0-9]+/v2/computations/.*:stop$" ) delete_computation_pattern = get_computation_pattern - projects_networks_pattern = re.compile( - r"^http://[a-z\-_]*director-v2:[0-9]+/v2/dynamic_services/projects/.*/-/networks$" - ) get_services_pattern = re.compile( r"^http://[a-z\-_]*director-v2:[0-9]+/v2/dynamic_services.*$" @@ -202,7 +199,6 @@ async def director_v2_service_mock( repeat=True, ) aioresponses_mocker.delete(delete_computation_pattern, status=204, repeat=True) - aioresponses_mocker.patch(projects_networks_pattern, status=204, repeat=True) return aioresponses_mocker diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py index d941f889bd7..62cc42d91ba 100644 --- a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py @@ -93,3 +93,16 @@ async def stop_dynamic_service( timeout_s=timeout_s, ) assert result is None # nosec + + +@log_decorator(_logger, level=logging.DEBUG) +async def update_projects_networks( + rabbitmq_rpc_client: RabbitMQRPCClient, *, project_id: ProjectID +) -> None: + result = await rabbitmq_rpc_client.request( + DYNAMIC_SCHEDULER_RPC_NAMESPACE, + _RPC_METHOD_NAME_ADAPTER.validate_python("update_projects_networks"), + project_id=project_id, + timeout_s=_RPC_DEFAULT_TIMEOUT_S, + ) + assert result is None # nosec diff --git a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py index 8cd90ddb8f0..9bba60a19f5 100644 --- a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py +++ b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py @@ -56,3 +56,8 @@ async def stop_dynamic_service( return await scheduler_interface.stop_dynamic_service( app, dynamic_service_stop=dynamic_service_stop ) + + +@router.expose() +async def update_projects_networks(app: FastAPI, *, project_id: ProjectID) -> None: + await scheduler_interface.update_projects_networks(app, project_id=project_id) diff --git a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py index 4b49618d6df..52134837775 100644 --- a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py +++ b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py @@ -108,6 +108,9 @@ async def list_tracked_dynamic_services( ) return TypeAdapter(list[DynamicServiceGet]).validate_python(response.json()) + async def update_projects_networks(self, *, project_id: ProjectID) -> None: + await self.thin_client.patch_projects_networks(project_id=project_id) + def setup_director_v2(app: FastAPI) -> None: public_client = DirectorV2Client(app) diff --git a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py index bfb64e8839a..39fce3741f3 100644 --- a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py +++ b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py @@ -124,3 +124,10 @@ async def get_dynamic_services( "/dynamic_services", params=as_dict_exclude_unset(user_id=user_id, project_id=project_id), ) + + @retry_on_errors() + @expect_status(status.HTTP_204_NO_CONTENT) + async def patch_projects_networks(self, *, project_id: ProjectID) -> Response: + return await self.client.patch( + f"/dynamic_services/projects/{project_id}/-/networks" + ) diff --git a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py index 6f655b544e2..382030a0489 100644 --- a/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py +++ b/services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py @@ -73,3 +73,12 @@ async def stop_dynamic_service( ) await set_request_as_stopped(app, dynamic_service_stop) + + +async def update_projects_networks(app: FastAPI, *, project_id: ProjectID) -> None: + settings: ApplicationSettings = app.state.settings + if settings.DYNAMIC_SCHEDULER_USE_INTERNAL_SCHEDULER: + raise NotImplementedError + + director_v2_client = DirectorV2Client.get_from_app_state(app) + await director_v2_client.update_projects_networks(project_id=project_id) diff --git a/services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py b/services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py index 7c1665065ae..cb4dd981d19 100644 --- a/services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py +++ b/services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py @@ -490,3 +490,24 @@ async def test_stop_dynamic_service_serializes_generic_errors( ), timeout_s=5, ) + + +@pytest.fixture +def mock_director_v2_update_projects_networks(project_id: ProjectID) -> Iterator[None]: + with respx.mock( + base_url="http://director-v2:8000/v2", + assert_all_called=False, + assert_all_mocked=True, # IMPORTANT: KEEP always True! + ) as mock: + mock.patch(f"/dynamic_services/projects/{project_id}/-/networks").respond( + status.HTTP_204_NO_CONTENT + ) + yield None + + +async def test_update_projects_networks( + mock_director_v2_update_projects_networks: None, + rpc_client: RabbitMQRPCClient, + project_id: ProjectID, +): + await services.update_projects_networks(rpc_client, project_id=project_id) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py b/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py index bd4c03f31fe..3c0cac4120e 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py @@ -83,19 +83,6 @@ async def restart_dynamic_service(app: web.Application, node_uuid: str) -> None: ) -@log_decorator(logger=_log) -async def update_dynamic_service_networks_in_project( - app: web.Application, project_id: ProjectID -) -> None: - settings: DirectorV2Settings = get_plugin_settings(app) - backend_url = ( - URL(settings.base_url) / f"dynamic_services/projects/{project_id}/-/networks" - ) - await request_director_v2( - app, "PATCH", backend_url, expected_status=web.HTTPNoContent - ) - - @log_decorator(logger=_log) async def get_project_inactivity( app: web.Application, diff --git a/services/web/server/src/simcore_service_webserver/director_v2/api.py b/services/web/server/src/simcore_service_webserver/director_v2/api.py index f56de16b543..a4d9c351082 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/api.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/api.py @@ -21,7 +21,6 @@ request_retrieve_dyn_service, restart_dynamic_service, retrieve, - update_dynamic_service_networks_in_project, ) from ._core_utils import is_healthy from .exceptions import DirectorServiceError @@ -43,6 +42,5 @@ "retrieve", "set_project_run_policy", "stop_pipeline", - "update_dynamic_service_networks_in_project", ) # nopycln: file diff --git a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py index ef8f2b1f703..f34b90e1c2b 100644 --- a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py +++ b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py @@ -148,3 +148,11 @@ async def stop_dynamic_services_in_project( ] await logged_gather(*services_to_stop) + + +async def update_projects_networks( + app: web.Application, *, project_id: ProjectID +) -> None: + await services.update_projects_networks( + get_rabbitmq_rpc_client(app), project_id=project_id + ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index d26a63a9cf8..39dc3aec7c2 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -26,7 +26,8 @@ from ..application_settings import get_application_settings from ..catalog import client as catalog_client -from ..director_v2 import api +from ..director_v2 import api as director_v2_api +from ..dynamic_scheduler import api as dynamic_scheduler_api from ..folders import _folders_db as folders_db from ..storage.api import ( copy_data_folders_from_project, @@ -376,13 +377,13 @@ async def create_project( # pylint: disable=too-many-arguments,too-many-branche await db.set_hidden_flag(new_project["uuid"], hidden=False) # update the network information in director-v2 - await api.update_dynamic_service_networks_in_project( - request.app, ProjectID(new_project["uuid"]) + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=ProjectID(new_project["uuid"]) ) task_progress.update() # This is a new project and every new graph needs to be reflected in the pipeline tables - await api.create_or_update_pipeline( + await director_v2_api.create_or_update_pipeline( request.app, user_id, new_project["uuid"], product_name ) # get the latest state of the project (lastChangeDate for instance) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 4f696cff09b..88918b7106a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -813,8 +813,8 @@ async def add_project_node( await director_v2_api.create_or_update_pipeline( request.app, user_id, project["uuid"], product_name ) - await director_v2_api.update_dynamic_service_networks_in_project( - request.app, project["uuid"] + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=ProjectID(project["uuid"]) ) if _is_node_dynamic(service_key): @@ -936,8 +936,8 @@ async def delete_project_node( await director_v2_api.create_or_update_pipeline( request.app, user_id, project_uuid, product_name ) - await director_v2_api.update_dynamic_service_networks_in_project( - request.app, project_uuid + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=project_uuid ) @@ -1045,9 +1045,7 @@ async def patch_project_node( app, user_id, project_id, product_name=product_name ) if _node_patch_exclude_unset.get("label"): - await director_v2_api.update_dynamic_service_networks_in_project( - app, project_id - ) + await dynamic_scheduler_api.update_projects_networks(app, project_id=project_id) # 5. Updates project states for user, if inputs have been changed if "inputs" in _node_patch_exclude_unset: diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py index 3ee106028fd..3fc24965f3f 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py @@ -18,7 +18,7 @@ from servicelib.aiohttp.typing_extension import Handler from servicelib.logging_errors import create_troubleshotting_log_kwargs -from ..director_v2.api import update_dynamic_service_networks_in_project +from ..dynamic_scheduler import api as dynamic_scheduler_api from ..products.api import get_product_name from ..utils import compose_support_error_msg from ..utils_aiohttp import create_redirect_to_page_response @@ -252,7 +252,9 @@ async def get_redirection_to_viewer(request: web.Request): file_params.download_link, product_name=get_product_name(request), ) - await update_dynamic_service_networks_in_project(request.app, project_id) + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=project_id + ) response = _create_redirect_response_to_view_page( request.app, @@ -281,7 +283,9 @@ async def get_redirection_to_viewer(request: web.Request): service_info=_create_service_info_from(valid_service), product_name=get_product_name(request), ) - await update_dynamic_service_networks_in_project(request.app, project_id) + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=project_id + ) response = _create_redirect_response_to_view_page( request.app, @@ -317,7 +321,9 @@ async def get_redirection_to_viewer(request: web.Request): ).STUDIES_DEFAULT_FILE_THUMBNAIL, product_name=get_product_name(request), ) - await update_dynamic_service_networks_in_project(request.app, project_id) + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=project_id + ) response = _create_redirect_response_to_view_page( request.app, diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py index 85d47f0dba8..10f1d8b6674 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py @@ -28,9 +28,7 @@ from .._constants import INDEX_RESOURCE_NAME from ..director_v2._core_computations import create_or_update_pipeline -from ..director_v2._core_dynamic_services import ( - update_dynamic_service_networks_in_project, -) +from ..dynamic_scheduler import api as dynamic_scheduler_api from ..products.api import get_current_product, get_product_name from ..projects._groups_db import get_project_group from ..projects.api import check_user_project_permission @@ -214,7 +212,9 @@ async def copy_study_to_account( await create_or_update_pipeline( request.app, user["id"], project["uuid"], product_name ) - await update_dynamic_service_networks_in_project(request.app, project["uuid"]) + await dynamic_scheduler_api.update_projects_networks( + request.app, project_id=ProjectID(project["uuid"]) + ) return project_uuid diff --git a/services/web/server/tests/conftest.py b/services/web/server/tests/conftest.py index 0e1de456b78..c97f84ed5c0 100644 --- a/services/web/server/tests/conftest.py +++ b/services/web/server/tests/conftest.py @@ -21,6 +21,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID from models_library.projects_state import ProjectState +from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.dict_tools import ConfigDict from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict @@ -444,3 +445,15 @@ async def _creator( for client, project_uuid in zip(used_clients, created_project_uuids, strict=True): url = client.app.router["delete_project"].url_for(project_id=project_uuid) await client.delete(url.path) + + +@pytest.fixture +def mock_dynamic_scheduler(mocker: MockerFixture) -> None: + mocker.patch( + "simcore_service_webserver.dynamic_scheduler.api.stop_dynamic_services_in_project", + autospec=True, + ) + mocker.patch( + "simcore_service_webserver.dynamic_scheduler.api.update_projects_networks", + autospec=True, + ) diff --git a/services/web/server/tests/integration/01/test_garbage_collection.py b/services/web/server/tests/integration/01/test_garbage_collection.py index f373c302df4..0e683fb06ec 100644 --- a/services/web/server/tests/integration/01/test_garbage_collection.py +++ b/services/web/server/tests/integration/01/test_garbage_collection.py @@ -113,9 +113,6 @@ async def director_v2_service_mock( r"^http://[a-z\-_]*director-v2:[0-9]+/v2/computations/.*$" ) delete_computation_pattern = get_computation_pattern - projects_networks_pattern = re.compile( - r"^http://[a-z\-_]*director-v2:[0-9]+/v2/dynamic_services/projects/.*/-/networks$" - ) mocker.patch( "simcore_service_webserver.dynamic_scheduler.api.list_dynamic_services", @@ -134,7 +131,6 @@ async def director_v2_service_mock( repeat=True, ) mock.delete(delete_computation_pattern, status=204, repeat=True) - mock.patch(projects_networks_pattern, status=204, repeat=True) yield mock diff --git a/services/web/server/tests/unit/with_dbs/02/conftest.py b/services/web/server/tests/unit/with_dbs/02/conftest.py index 7e765694d3d..148d6315d8d 100644 --- a/services/web/server/tests/unit/with_dbs/02/conftest.py +++ b/services/web/server/tests/unit/with_dbs/02/conftest.py @@ -239,10 +239,8 @@ async def project_db_cleaner(client: TestClient): @pytest.fixture(autouse=True) -async def mocked_director_v2( - director_v2_service_mock: aioresponses, -) -> AsyncIterator[aioresponses]: - return director_v2_service_mock +async def mocked_director_v2(director_v2_service_mock: aioresponses) -> None: + pass @pytest.fixture() diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py index 92184c0d145..fe43672ebfc 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py @@ -180,6 +180,7 @@ async def test_copying_large_project_and_retrieving_copy_task( @pytest.mark.parametrize(*_standard_user_role_response()) async def test_creating_new_project_from_template_without_copying_data_creates_skeleton( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict[str, Any], primary_group: dict[str, str], @@ -230,6 +231,7 @@ async def test_creating_new_project_from_template_without_copying_data_creates_s @pytest.mark.parametrize(*_standard_user_role_response()) async def test_creating_new_project_as_template_without_copying_data_creates_skeleton( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict[str, Any], primary_group: dict[str, str], diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py index 95a2671739b..aa321cd378f 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py @@ -412,6 +412,7 @@ async def test_get_project( @pytest.mark.parametrize(*standard_role_response()) async def test_new_project( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, primary_group, @@ -427,6 +428,7 @@ async def test_new_project( @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_project_from_template( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, primary_group: dict[str, str], @@ -453,6 +455,7 @@ async def test_new_project_from_template( @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_project_from_other_study( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, primary_group: dict[str, str], @@ -482,6 +485,7 @@ async def test_new_project_from_other_study( @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_project_from_template_with_body( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, primary_group: dict[str, str], @@ -536,6 +540,7 @@ async def test_new_project_from_template_with_body( @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_template_from_project( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict[str, Any], primary_group: dict[str, str], diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py index 657b19e20d6..5945d290744 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py @@ -54,6 +54,7 @@ async def _request_clone_project(client: TestClient, url: URL) -> ProjectGet: @pytest.mark.parametrize(*standard_role_response(), ids=str) async def test_clone_project_user_permissions( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, @@ -85,6 +86,7 @@ async def test_clone_project_user_permissions( [UserRole.USER], ) async def test_clone_project( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py index 18ba745eaee..8ba05ea870c 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py @@ -3,8 +3,9 @@ # pylint: disable=unused-argument # pylint: disable=unused-variable +from collections.abc import Iterator from copy import deepcopy -from typing import Any, Iterator +from typing import Any import pytest import sqlalchemy as sa @@ -88,6 +89,7 @@ async def _request_clone_project(client: TestClient, url: URL) -> ProjectGet: [UserRole.USER], ) async def test_clone_project( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py index c4d5d1f26b0..80f7f4a7bd3 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py @@ -5,9 +5,8 @@ # pylint: disable=unused-variable -from collections.abc import Callable, Iterator +from collections.abc import Awaitable, Callable, Iterator from http import HTTPStatus -from typing import Awaitable from unittest import mock from unittest.mock import MagicMock, call diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py index a9f111e9c4a..f0147c7eb02 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py @@ -152,6 +152,7 @@ async def test_list_projects_with_invalid_pagination_parameters( @pytest.mark.parametrize("limit", [7, 20, 43]) @pytest.mark.parametrize(*standard_user_role()) async def test_list_projects_with_pagination( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict[str, Any], primary_group: dict[str, str], @@ -181,7 +182,7 @@ async def test_list_projects_with_pagination( next_link = None default_query_parameter = {"limit": limit} projects = [] - for i in range(NUMBER_OF_CALLS): + for _ in range(NUMBER_OF_CALLS): print( "calling in with query", next_link.query if next_link else default_query_parameter, @@ -189,9 +190,9 @@ async def test_list_projects_with_pagination( data, meta, links = await _list_projects( client, expected.ok, - query_parameters=next_link.query - if next_link - else default_query_parameter, + query_parameters=( + next_link.query if next_link else default_query_parameter + ), ) print("...received [", meta, "]") assert len(data) == meta["count"] diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py index 80c941eca23..ca193544302 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py @@ -114,6 +114,7 @@ async def _wait_until_deleted(): @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_project_with_parent_project_node( + mock_dynamic_scheduler: None, # for deletion mocked_dynamic_services_interface: dict[str, MagicMock], storage_subsystem_mock: MockedStorageSubsystem, @@ -191,6 +192,7 @@ async def test_new_project_with_parent_project_node( @pytest.mark.parametrize(*standard_user_role_response()) async def test_new_project_with_invalid_parent_project_node( + mock_dynamic_scheduler: None, # for deletion mocked_dynamic_services_interface: dict[str, MagicMock], storage_subsystem_mock: MockedStorageSubsystem, @@ -274,6 +276,7 @@ async def test_new_project_with_invalid_parent_project_node( @pytest.mark.parametrize(*standard_user_role_response()) async def test_set_project_parent_backward_compatibility( + mock_dynamic_scheduler: None, # for deletion mocked_dynamic_services_interface: dict[str, MagicMock], storage_subsystem_mock: MockedStorageSubsystem, @@ -393,6 +396,7 @@ async def test_update_project_metadata_backward_compatibility_with_same_project_ @pytest.mark.parametrize(*standard_user_role_response()) async def test_update_project_metadata_s4lacad_backward_compatibility_passing_nil_parent_node_id( + mock_dynamic_scheduler: None, # for deletion mocked_dynamic_services_interface: dict[str, MagicMock], storage_subsystem_mock: MockedStorageSubsystem, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py index 6fc5c13b194..c5fa6330978 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py @@ -318,6 +318,7 @@ async def test_create_node_returns_422_if_body_is_missing( ) @pytest.mark.parametrize(*standard_role_response(), ids=str) async def test_create_node( + mock_dynamic_scheduler: None, node_class: str, expect_run_service_call: bool, client: TestClient, @@ -376,6 +377,7 @@ def standard_user_role() -> tuple[str, tuple]: @pytest.mark.parametrize(*standard_user_role()) async def test_create_and_delete_many_nodes_in_parallel( + mock_dynamic_scheduler: None, disable_max_number_of_running_dynamic_nodes: dict[str, str], client: TestClient, user_project: ProjectDict, @@ -395,8 +397,8 @@ class _RunningServices: running_services_uuids: list[str] = field(default_factory=list) def num_services( - self, *args, **kwargs - ) -> list[DynamicServiceGet]: # noqa: ARG002 + self, *args, **kwargs # noqa: ARG002 + ) -> list[DynamicServiceGet]: return [ DynamicServiceGet.model_validate( DynamicServiceGet.model_config["json_schema_extra"]["examples"][1] @@ -470,6 +472,7 @@ def inc_running_services(self, *args, **kwargs): # noqa: ARG002 @pytest.mark.parametrize(*standard_user_role()) async def test_create_node_does_not_start_dynamic_node_if_there_are_already_too_many_running( + mock_dynamic_scheduler: None, client: TestClient, user_project_with_num_dynamic_services: Callable[[int], Awaitable[ProjectDict]], expected: ExpectedResponse, @@ -503,6 +506,7 @@ async def test_create_node_does_not_start_dynamic_node_if_there_are_already_too_ @pytest.mark.parametrize(*standard_user_role()) async def test_create_many_nodes_in_parallel_still_is_limited_to_the_defined_maximum( + mock_dynamic_scheduler: None, client: TestClient, user_project_with_num_dynamic_services: Callable[[int], Awaitable[ProjectDict]], expected: ExpectedResponse, @@ -524,8 +528,8 @@ class _RunninServices: running_services_uuids: list[str] = field(default_factory=list) async def num_services( - self, *args, **kwargs - ) -> list[dict[str, Any]]: # noqa: ARG002 + self, *args, **kwargs # noqa: ARG002 + ) -> list[dict[str, Any]]: return [ {"service_uuid": service_uuid} for service_uuid in self.running_services_uuids @@ -588,6 +592,7 @@ async def inc_running_services(self, *args, **kwargs): # noqa: ARG002 @pytest.mark.parametrize(*standard_user_role()) async def test_create_node_does_start_dynamic_node_if_max_num_set_to_0( + mock_dynamic_scheduler: None, disable_max_number_of_running_dynamic_nodes: dict[str, str], client: TestClient, user_project_with_num_dynamic_services: Callable[[int], Awaitable[ProjectDict]], @@ -625,6 +630,7 @@ async def test_create_node_does_start_dynamic_node_if_max_num_set_to_0( ) @pytest.mark.parametrize(*standard_role_response(), ids=str) async def test_creating_deprecated_node_returns_406_not_acceptable( + mock_dynamic_scheduler: None, client: TestClient, user_project: ProjectDict, expected: ExpectedResponse, @@ -664,6 +670,7 @@ async def test_creating_deprecated_node_returns_406_not_acceptable( ) @pytest.mark.parametrize(*standard_role_response(), ids=str) async def test_delete_node( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict, user_project: ProjectDict, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handlers__patch.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handlers__patch.py index 71101c5dc88..5f836e2b747 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handlers__patch.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handlers__patch.py @@ -65,6 +65,7 @@ def mock_catalog_rpc_check_for_service(mocker: MockerFixture): ], ) async def test_patch_project_node_entrypoint_access( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, @@ -86,6 +87,7 @@ async def test_patch_project_node_entrypoint_access( "user_role,expected", [(UserRole.USER, status.HTTP_204_NO_CONTENT)] ) async def test_patch_project_node( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py index 8a82df500b6..db4596f06ec 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py @@ -247,6 +247,7 @@ async def test_io_workflow( [UserRole.USER], ) async def test_clone_project_and_set_inputs( + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project: ProjectDict, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py index 15af4778e85..f3b91131b1a 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py @@ -258,6 +258,7 @@ async def _delete_project(client: TestClient, project: dict) -> ClientResponse: ids=str, ) async def test_share_project( + mock_dynamic_scheduler: None, client: TestClient, logged_user: dict, primary_group: dict[str, str], @@ -984,6 +985,7 @@ async def test_get_active_project( ], ) async def test_project_node_lifetime( # noqa: PLR0915 + mock_dynamic_scheduler: None, client: TestClient, logged_user: UserInfoDict, user_project, diff --git a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py index 862a0db06e8..2c0142c2c0b 100644 --- a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py +++ b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py @@ -77,6 +77,7 @@ async def test_iterators_workflow( context_with_logged_user: None, mocker: MockerFixture, faker: Faker, + mock_dynamic_scheduler: None, director_v2_service_mock: None, request_create_project: Callable[..., Awaitable[ProjectDict]], ): diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py index 705b0458188..fa05653b87c 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py @@ -37,6 +37,7 @@ async def test_workflow( user_project: ProjectDict, aiohttp_mocked_request: web.Request, request_update_project: Callable[[TestClient, UUID], Awaitable], + mock_dynamic_scheduler: None, director_v2_service_mock: None, ): vc_repo = VersionControlRepository.create_from_request(aiohttp_mocked_request) diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py index ab84b68a3e8..325eb63e353 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py @@ -50,6 +50,7 @@ async def test_workflow( client: TestClient, user_project: ProjectDict, request_update_project: Callable[[TestClient, UUID], Awaitable], + mock_dynamic_scheduler: None, director_v2_service_mock: None, ): # pylint: disable=too-many-statements diff --git a/services/web/server/tests/unit/with_dbs/04/garbage_collector/test_resource_manager.py b/services/web/server/tests/unit/with_dbs/04/garbage_collector/test_resource_manager.py index 962e8539d04..18cba797177 100644 --- a/services/web/server/tests/unit/with_dbs/04/garbage_collector/test_resource_manager.py +++ b/services/web/server/tests/unit/with_dbs/04/garbage_collector/test_resource_manager.py @@ -232,11 +232,6 @@ async def empty_user_project2( print("<----- removed project", project["name"]) -@pytest.fixture(autouse=True) -async def director_v2_mock(director_v2_service_mock) -> aioresponses: - return director_v2_service_mock - - async def test_anonymous_websocket_connection( client_session_id_factory: Callable[[], str], socketio_url_factory: Callable, @@ -541,6 +536,7 @@ async def test_interactive_services_removed_after_logout( ], ) async def test_interactive_services_remain_after_websocket_reconnection_from_2_tabs( + director_v2_service_mock: aioresponses, client: TestClient, logged_user: UserInfoDict, empty_user_project, @@ -678,6 +674,7 @@ async def mocked_notification_system(mocker): ], ) async def test_interactive_services_removed_per_project( + director_v2_service_mock: aioresponses, client, logged_user, empty_user_project, @@ -799,6 +796,7 @@ async def test_interactive_services_removed_per_project( ], ) async def test_services_remain_after_closing_one_out_of_two_tabs( + director_v2_service_mock: aioresponses, client, logged_user, empty_user_project, @@ -854,6 +852,7 @@ async def test_services_remain_after_closing_one_out_of_two_tabs( ], ) async def test_websocket_disconnected_remove_or_maintain_files_based_on_role( + director_v2_service_mock: aioresponses, client, logged_user, empty_user_project, @@ -923,6 +922,7 @@ async def test_websocket_disconnected_remove_or_maintain_files_based_on_role( @pytest.mark.parametrize("user_role", [UserRole.USER, UserRole.TESTER, UserRole.GUEST]) async def test_regression_removing_unexisting_user( + director_v2_service_mock: aioresponses, client: TestClient, logged_user: dict[str, Any], empty_user_project: dict[str, Any], diff --git a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/conftest.py b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/conftest.py index 864dbbe7be0..bfa6fe9fece 100644 --- a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/conftest.py +++ b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/conftest.py @@ -7,7 +7,6 @@ import logging import pytest -from pytest_mock import MockerFixture from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict from simcore_service_webserver.log import setup_logging @@ -64,11 +63,3 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc print(plugin_settings.model_dump_json(indent=1)) return {**app_environment, **envs_plugins, **envs_studies_dispatcher} - - -@pytest.fixture -def mock_dynamic_scheduler(mocker: MockerFixture) -> None: - mocker.patch( - "simcore_service_webserver.dynamic_scheduler.api.stop_dynamic_services_in_project", - autospec=True, - ) diff --git a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_handlers.py b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_handlers.py index 6f8853337ee..86ed849075f 100644 --- a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_handlers.py +++ b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_handlers.py @@ -402,8 +402,8 @@ async def test_dispatch_study_anonymously( "simcore_service_webserver.director_v2.api.create_or_update_pipeline", return_value=None, ) - mock_client_director_v2_project_networks = mocker.patch( - "simcore_service_webserver.studies_dispatcher._redirects_handlers.update_dynamic_service_networks_in_project", + mock_dynamic_scheduler_update_project_networks = mocker.patch( + "simcore_service_webserver.studies_dispatcher._redirects_handlers.dynamic_scheduler_api.update_projects_networks", return_value=None, ) @@ -443,7 +443,7 @@ async def test_dispatch_study_anonymously( assert guest_project["prjOwner"] == data["login"] assert mock_client_director_v2_func.called - assert mock_client_director_v2_project_networks.called + assert mock_dynamic_scheduler_update_project_networks.called @pytest.mark.parametrize( @@ -468,8 +468,8 @@ async def test_dispatch_logged_in_user( "simcore_service_webserver.director_v2.api.create_or_update_pipeline", return_value=None, ) - mock_client_director_v2_project_networks = mocker.patch( - "simcore_service_webserver.studies_dispatcher._redirects_handlers.update_dynamic_service_networks_in_project", + mock_dynamic_scheduler_update_project_networks = mocker.patch( + "simcore_service_webserver.studies_dispatcher._redirects_handlers.dynamic_scheduler_api.update_projects_networks", return_value=None, ) @@ -501,7 +501,7 @@ async def test_dispatch_logged_in_user( assert created_project["prjOwner"] == data["login"] assert mock_client_director_v2_pipline_update.called - assert mock_client_director_v2_project_networks.called + assert mock_dynamic_scheduler_update_project_networks.called # delete before exiting url = client.app.router["delete_project"].url_for(project_id=expected_project_id) diff --git a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_studies_access.py b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_studies_access.py index cff892d7f00..04b8e50f7ea 100644 --- a/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_studies_access.py +++ b/services/web/server/tests/unit/with_dbs/04/studies_dispatcher/test_studies_dispatcher_studies_access.py @@ -259,6 +259,7 @@ async def test_access_study_anonymously( published_project: ProjectDict, storage_subsystem_mock_override: None, catalog_subsystem_mock: Callable[[list[ProjectDict]], None], + mock_dynamic_scheduler: None, director_v2_service_mock: AioResponsesMock, mocks_on_projects_api: None, # needed to cleanup the locks between parametrizations @@ -309,6 +310,7 @@ async def test_access_study_by_logged_user( published_project: ProjectDict, storage_subsystem_mock_override: None, catalog_subsystem_mock: Callable[[list[ProjectDict]], None], + mock_dynamic_scheduler: None, director_v2_service_mock: AioResponsesMock, mocks_on_projects_api: None, auto_delete_projects: None, @@ -416,6 +418,7 @@ async def test_guest_user_is_not_garbage_collected( published_project: ProjectDict, storage_subsystem_mock_override: None, catalog_subsystem_mock: Callable[[list[ProjectDict]], None], + mock_dynamic_scheduler: None, director_v2_service_mock: AioResponsesMock, mocks_on_projects_api: None, # needed to cleanup the locks between parametrizations