1313from datetime import UTC , datetime , timedelta
1414from decimal import Decimal
1515from http import HTTPStatus
16- from typing import Any
16+ from typing import Any , TypedDict
1717from unittest import mock
1818from unittest .mock import call
1919
@@ -157,24 +157,26 @@ async def _replace_project(
157157 return data
158158
159159
160+ class _SocketHandlers (TypedDict ):
161+ SOCKET_IO_PROJECT_UPDATED_EVENT : mock .Mock
162+
163+
160164@pytest .fixture
161165async def create_socketio_connection (
162166 socketio_client_factory : Callable ,
163167 mocker : MockerFixture ,
164168) -> AsyncIterator [
165- Callable [
166- [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , dict [str , mock .Mock ]]]
167- ]
169+ Callable [[TestClient , str ], Awaitable [tuple [socketio .AsyncClient , _SocketHandlers ]]]
168170]:
169171 connected_sockets = []
170172
171173 async def _ (
172174 client : TestClient , client_id : str
173- ) -> tuple [socketio .AsyncClient , dict [ str , mock . Mock ] ]:
175+ ) -> tuple [socketio .AsyncClient , _SocketHandlers ]:
174176 sio = await socketio_client_factory (client_id , client )
175177 assert sio .sid
176178
177- event_handlers = { SOCKET_IO_PROJECT_UPDATED_EVENT : mocker .Mock ()}
179+ event_handlers = _SocketHandlers ( SOCKET_IO_PROJECT_UPDATED_EVENT = mocker .Mock ())
178180
179181 for event , handler in event_handlers .items ():
180182 sio .on (event , handler = handler )
@@ -184,7 +186,9 @@ async def _(
184186 yield _
185187
186188 # cleanup
187- await asyncio .gather (* (socket .disconnect () for socket in connected_sockets ))
189+ with log_context (logging .INFO , "disconnecting sockets" ):
190+ await asyncio .gather (* (socket .disconnect () for socket in connected_sockets ))
191+ await asyncio .sleep (0.1 ) # give time to properly disconnect
188192
189193
190194async def _open_project (
@@ -1379,20 +1383,49 @@ async def test_open_shared_project_multiple_users(
13791383 shared_project : dict ,
13801384 client_session_id_factory : Callable [[], str ],
13811385 expected : ExpectedResponse ,
1382- mocker : MockerFixture ,
13831386 create_socketio_connection : Callable [
1384- [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , dict [ str , mock . Mock ] ]]
1387+ [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , _SocketHandlers ]]
13851388 ],
1389+ mocked_dynamic_services_interface : dict [str , mock .Mock ],
1390+ mock_catalog_api : dict [str , mock .Mock ],
13861391):
13871392 base_client = client
13881393 base_client_tab_id = client_session_id_factory ()
1389- # 1. user 1 opens project
1390- sio , socket_handlers = await create_socketio_connection (
1394+ sio_base , sio_base_handlers = await create_socketio_connection (
13911395 base_client ,
13921396 base_client_tab_id ,
13931397 )
1398+
1399+ # current state is closed and unlocked
1400+ expected_project_state = ProjectStateOutputSchema (
1401+ share_state = ProjectShareStateOutputSchema (
1402+ locked = False , status = ProjectStatus .CLOSED , current_user_groupids = []
1403+ ),
1404+ state = ProjectRunningState (value = RunningState .NOT_STARTED ),
1405+ )
1406+ await _state_project (
1407+ base_client , shared_project , expected .ok , expected_project_state
1408+ )
1409+
1410+ # now user 1 opens the shared project
1411+ await _open_project (base_client , base_client_tab_id , shared_project , expected .ok )
1412+
1413+ expected_project_state = expected_project_state .model_copy (
1414+ update = {
1415+ "share_state" : ProjectShareStateOutputSchema (
1416+ locked = False ,
1417+ status = ProjectStatus .OPENED ,
1418+ current_user_groupids = [logged_user ["primary_gid" ]],
1419+ ),
1420+ }
1421+ )
13941422 await _assert_project_state_updated (
1395- socket_handlers [SOCKET_IO_PROJECT_UPDATED_EVENT ], shared_project , []
1423+ sio_base_handlers [SOCKET_IO_PROJECT_UPDATED_EVENT ],
1424+ shared_project ,
1425+ [expected_project_state ] * 2 ,
1426+ )
1427+ await _state_project (
1428+ base_client , shared_project , expected .ok , expected_project_state
13961429 )
13971430
13981431
@@ -1413,7 +1446,7 @@ async def test_open_shared_project_2_users_locked_remove_once_rtc_collaboration_
14131446 mocked_notifications_plugin : dict [str , mock .Mock ],
14141447 exit_stack : contextlib .AsyncExitStack ,
14151448 create_socketio_connection : Callable [
1416- [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , dict [ str , mock . Mock ] ]]
1449+ [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , _SocketHandlers ]]
14171450 ],
14181451):
14191452 # Use-case: user 1 opens a shared project, user 2 tries to open it as well
@@ -1649,7 +1682,7 @@ async def test_open_shared_project_at_same_time(
16491682 mocked_notifications_plugin : dict [str , mock .Mock ],
16501683 exit_stack : contextlib .AsyncExitStack ,
16511684 create_socketio_connection : Callable [
1652- [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , dict [ str , mock . Mock ] ]]
1685+ [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , _SocketHandlers ]]
16531686 ],
16541687):
16551688 NUMBER_OF_ADDITIONAL_CLIENTS = 10
@@ -1732,7 +1765,7 @@ async def test_opened_project_can_still_be_opened_after_refreshing_tab(
17321765 clean_redis_table ,
17331766 mocked_notifications_plugin : dict [str , mock .Mock ],
17341767 create_socketio_connection : Callable [
1735- [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , dict [ str , mock . Mock ] ]]
1768+ [TestClient , str ], Awaitable [tuple [socketio .AsyncClient , _SocketHandlers ]]
17361769 ],
17371770):
17381771 """Simulating a refresh goes as follows:
0 commit comments