Skip to content

Commit 1eeee40

Browse files
committed
test is there
1 parent 83e5da4 commit 1eeee40

File tree

2 files changed

+135
-15
lines changed

2 files changed

+135
-15
lines changed

services/web/server/tests/integration/01/test_computation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,9 @@ async def test_running_computation_sends_progress_updates_via_socketio(
605605
mocker: MockerFixture,
606606
):
607607
assert client.app
608-
socket_io_conn, client_id = await create_socketio_connection(None, client)
609-
mock_progress_handler = mocker.MagicMock()
610-
socket_io_conn.on(SOCKET_IO_NODE_UPDATED_EVENT, handler=mock_progress_handler)
608+
socket_io_conn, _ = await create_socketio_connection(None, client)
609+
mock_node_updated_handler = mocker.MagicMock()
610+
socket_io_conn.on(SOCKET_IO_NODE_UPDATED_EVENT, handler=mock_node_updated_handler)
611611

612612
project_id = user_project["uuid"]
613613

@@ -640,9 +640,9 @@ async def test_running_computation_sends_progress_updates_via_socketio(
640640
)
641641

642642
# check that the progress updates were sent
643-
assert mock_progress_handler.call_count > 0, (
643+
assert mock_node_updated_handler.call_count > 0, (
644644
"expected progress updates to be sent via socketio, "
645-
f"but got {mock_progress_handler.call_count} calls"
645+
f"but got {mock_node_updated_handler.call_count} calls"
646646
)
647647

648648
# Get all computational nodes from the workbench (exclude file-picker nodes)
@@ -654,7 +654,7 @@ async def test_running_computation_sends_progress_updates_via_socketio(
654654

655655
# Collect all node IDs that received progress updates
656656
received_progress_node_ids = set()
657-
for call_args in mock_progress_handler.call_args_list:
657+
for call_args in mock_node_updated_handler.call_args_list:
658658
assert len(call_args[0]) == 1, (
659659
"expected the progress handler to be called with a single argument, "
660660
f"but got {len(call_args[0])} arguments"

services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from uuid import uuid4
1818

1919
import pytest
20+
import socketio
2021
import sqlalchemy as sa
2122
from aiohttp.test_utils import TestClient
2223
from aioresponses import aioresponses
@@ -29,7 +30,9 @@
2930
FileMetaDataGet,
3031
PresignedLink,
3132
)
33+
from models_library.api_schemas_webserver.projects_nodes import NodeGetIdle
3234
from models_library.generics import Envelope
35+
from models_library.projects_nodes import Node, NodeShareStatus
3336
from models_library.projects_nodes_io import NodeID
3437
from models_library.services_resources import (
3538
DEFAULT_SINGLE_SERVICE_NAME,
@@ -38,6 +41,7 @@
3841
)
3942
from models_library.utils.fastapi_encoders import jsonable_encoder
4043
from pydantic import NonNegativeFloat, NonNegativeInt, TypeAdapter
44+
from pytest_mock import MockerFixture
4145
from pytest_simcore.helpers.assert_checks import assert_status
4246
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
4347
from pytest_simcore.helpers.webserver_parametrizations import (
@@ -54,6 +58,7 @@
5458
_ProjectNodePreview,
5559
)
5660
from simcore_service_webserver.projects.models import ProjectDict
61+
from simcore_service_webserver.socketio.messages import SOCKET_IO_NODE_UPDATED_EVENT
5762
from tenacity import (
5863
AsyncRetrying,
5964
RetryError,
@@ -412,7 +417,9 @@ class _RunningServices:
412417
running_services_uuids: list[str] = field(default_factory=list)
413418

414419
def num_services(
415-
self, *args, **kwargs # noqa: ARG002
420+
self,
421+
*args,
422+
**kwargs, # noqa: ARG002
416423
) -> list[DynamicServiceGet]:
417424
return [
418425
DynamicServiceGet.model_validate(
@@ -512,7 +519,7 @@ async def test_create_node_does_not_start_dynamic_node_if_there_are_already_too_
512519
"service_version": faker.numerify("%.#.#"),
513520
"service_id": None,
514521
}
515-
response = await client.post(f"{ url}", json=body)
522+
response = await client.post(f"{url}", json=body)
516523
await assert_status(response, expected.created)
517524
mocked_dynamic_services_interface[
518525
"dynamic_scheduler.api.run_dynamic_service"
@@ -543,7 +550,9 @@ class _RunninServices:
543550
running_services_uuids: list[str] = field(default_factory=list)
544551

545552
async def num_services(
546-
self, *args, **kwargs # noqa: ARG002
553+
self,
554+
*args,
555+
**kwargs, # noqa: ARG002
547556
) -> list[dict[str, Any]]:
548557
return [
549558
{"service_uuid": service_uuid}
@@ -632,7 +641,7 @@ async def test_create_node_does_start_dynamic_node_if_max_num_set_to_0(
632641
"service_version": faker.numerify("%.#.#"),
633642
"service_id": None,
634643
}
635-
response = await client.post(f"{ url}", json=body)
644+
response = await client.post(f"{url}", json=body)
636645
await assert_status(response, expected.created)
637646
mocked_dynamic_services_interface[
638647
"dynamic_scheduler.api.run_dynamic_service"
@@ -765,6 +774,21 @@ async def test_delete_node(
765774
assert node_id not in workbench
766775

767776

777+
@pytest.fixture
778+
async def socket_io_node_updated_mock(
779+
mocker: MockerFixture,
780+
client: TestClient,
781+
logged_user,
782+
create_socketio_connection: Callable[
783+
[str | None, TestClient | None], Awaitable[tuple[socketio.AsyncClient, str]]
784+
],
785+
) -> mock.Mock:
786+
socket_io_conn, _ = await create_socketio_connection(None, client)
787+
mock_node_updated_handler = mocker.MagicMock()
788+
socket_io_conn.on(SOCKET_IO_NODE_UPDATED_EVENT, handler=mock_node_updated_handler)
789+
return mock_node_updated_handler
790+
791+
768792
@pytest.mark.parametrize(*standard_role_response(), ids=str)
769793
async def test_start_node(
770794
client: TestClient,
@@ -783,7 +807,8 @@ async def test_start_node(
783807
all_service_uuids = list(project["workbench"])
784808
# start the node, shall work as expected
785809
url = client.app.router["start_node"].url_for(
786-
project_id=project["uuid"], node_id=choice(all_service_uuids) # noqa: S311
810+
project_id=project["uuid"],
811+
node_id=choice(all_service_uuids), # noqa: S311
787812
)
788813
response = await client.post(f"{url}")
789814
data, error = await assert_status(
@@ -804,6 +829,97 @@ async def test_start_node(
804829
].assert_not_called()
805830

806831

832+
@pytest.mark.parametrize(*standard_user_role())
833+
async def test_start_stop_node_sends_node_updated_socketio_event(
834+
client: TestClient,
835+
logged_user: dict[str, Any],
836+
socket_io_node_updated_mock: mock.Mock,
837+
user_project_with_num_dynamic_services: Callable[[int], Awaitable[ProjectDict]],
838+
expected: ExpectedResponse,
839+
mocked_dynamic_services_interface: dict[str, mock.MagicMock],
840+
mock_catalog_api: dict[str, mock.Mock],
841+
faker: Faker,
842+
max_amount_of_auto_started_dyn_services: int,
843+
mocker: MockerFixture,
844+
):
845+
assert client.app
846+
project = await user_project_with_num_dynamic_services(
847+
max_amount_of_auto_started_dyn_services or faker.pyint(min_value=3)
848+
)
849+
all_service_uuids = list(project["workbench"])
850+
# start the node, shall work as expected
851+
chosen_node_id = choice(all_service_uuids) # noqa: S311
852+
url = client.app.router["start_node"].url_for(
853+
project_id=project["uuid"], node_id=chosen_node_id
854+
)
855+
856+
# simulate that the dynamic service is running
857+
mocked_dynamic_services_interface[
858+
"dynamic_scheduler.api.get_dynamic_service"
859+
].return_value = DynamicServiceGet.model_validate(
860+
DynamicServiceGet.model_json_schema()["examples"][0]
861+
)
862+
863+
response = await client.post(f"{url}")
864+
await assert_status(response, expected.no_content)
865+
mocked_dynamic_services_interface[
866+
"dynamic_scheduler.api.run_dynamic_service"
867+
].assert_called_once()
868+
869+
socket_io_node_updated_mock.assert_called_once()
870+
message = socket_io_node_updated_mock.call_args[0][0]
871+
assert "data" in message
872+
assert "project_id" in message
873+
assert "node_id" in message
874+
assert message["project_id"] == project["uuid"]
875+
assert message["node_id"] == chosen_node_id
876+
received_node = Node.model_validate(message["data"])
877+
assert received_node.state
878+
assert received_node.state.lock_state
879+
assert received_node.state.lock_state.locked is True
880+
assert received_node.state.lock_state.current_user_groupids == [
881+
logged_user["primary_gid"]
882+
]
883+
assert received_node.state.lock_state.status is NodeShareStatus.OPENED
884+
socket_io_node_updated_mock.reset_mock()
885+
886+
# now stop the node
887+
url = client.app.router["stop_node"].url_for(
888+
project_id=project["uuid"], node_id=chosen_node_id
889+
)
890+
# simulate that the dynamic service is idle
891+
mocked_dynamic_services_interface[
892+
"dynamic_scheduler.api.get_dynamic_service"
893+
].return_value = NodeGetIdle.model_validate(
894+
NodeGetIdle.model_json_schema()["examples"][0]
895+
)
896+
response = await client.post(f"{url}")
897+
await assert_status(response, expected.accepted)
898+
async for attempt in AsyncRetrying(
899+
retry=retry_if_exception_type(AssertionError),
900+
stop=stop_after_delay(5),
901+
wait=wait_fixed(0.1),
902+
reraise=True,
903+
):
904+
with attempt:
905+
mocked_dynamic_services_interface[
906+
"dynamic_scheduler.api.stop_dynamic_service"
907+
].assert_called_once()
908+
socket_io_node_updated_mock.assert_called_once()
909+
message = socket_io_node_updated_mock.call_args[0][0]
910+
assert "data" in message
911+
assert "project_id" in message
912+
assert "node_id" in message
913+
assert message["project_id"] == project["uuid"]
914+
assert message["node_id"] == chosen_node_id
915+
received_node = Node.model_validate(message["data"])
916+
assert received_node.state
917+
assert received_node.state.lock_state
918+
assert received_node.state.lock_state.locked is False
919+
assert received_node.state.lock_state.current_user_groupids is None
920+
assert received_node.state.lock_state.status is None
921+
922+
807923
@pytest.mark.parametrize(*standard_user_role())
808924
async def test_start_node_raises_if_dynamic_services_limit_attained(
809925
client: TestClient,
@@ -827,7 +943,8 @@ async def test_start_node_raises_if_dynamic_services_limit_attained(
827943
]
828944
# start the node, shall work as expected
829945
url = client.app.router["start_node"].url_for(
830-
project_id=project["uuid"], node_id=choice(all_service_uuids) # noqa: S311
946+
project_id=project["uuid"],
947+
node_id=choice(all_service_uuids), # noqa: S311
831948
)
832949
response = await client.post(f"{url}")
833950
data, error = await assert_status(
@@ -862,7 +979,8 @@ async def test_start_node_starts_dynamic_service_if_max_number_of_services_set_t
862979
]
863980
# start the node, shall work as expected
864981
url = client.app.router["start_node"].url_for(
865-
project_id=project["uuid"], node_id=choice(all_service_uuids) # noqa: S311
982+
project_id=project["uuid"],
983+
node_id=choice(all_service_uuids), # noqa: S311
866984
)
867985
response = await client.post(f"{url}")
868986
data, error = await assert_status(
@@ -895,7 +1013,8 @@ async def test_start_node_raises_if_called_with_wrong_data(
8951013

8961014
# start the node, with wrong project
8971015
url = client.app.router["start_node"].url_for(
898-
project_id=faker.uuid4(), node_id=choice(all_service_uuids) # noqa: S311
1016+
project_id=faker.uuid4(),
1017+
node_id=choice(all_service_uuids), # noqa: S311
8991018
)
9001019
response = await client.post(f"{url}")
9011020
data, error = await assert_status(
@@ -943,7 +1062,8 @@ async def test_stop_node(
9431062
all_service_uuids = list(project["workbench"])
9441063
# start the node, shall work as expected
9451064
url = client.app.router["stop_node"].url_for(
946-
project_id=project["uuid"], node_id=choice(all_service_uuids) # noqa: S311
1065+
project_id=project["uuid"],
1066+
node_id=choice(all_service_uuids), # noqa: S311
9471067
)
9481068
response = await client.post(f"{url}")
9491069
_, error = await assert_status(

0 commit comments

Comments
 (0)