Skip to content

Commit 9320613

Browse files
committed
fixing tests
1 parent f1585fb commit 9320613

File tree

4 files changed

+88
-50
lines changed

4 files changed

+88
-50
lines changed

services/web/server/src/simcore_service_webserver/collaboration/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class RealTimeCollaborationSettings(BaseCustomSettings):
1414
RTC_MAX_NUMBER_OF_USERS: Annotated[
1515
PositiveInt | None,
1616
Field(
17-
description="Maximum number of users allowed in a real-time collaboration session",
17+
description="Maximum number of user sessions allowed on a single project at once. (null disables the limit)",
1818
),
1919
]
2020

services/web/server/src/simcore_service_webserver/projects/_controller/projects_states_rest.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ async def open_project(request: web.Request) -> web.Response:
102102
client_session_id=client_session_id,
103103
app=request.app,
104104
max_number_of_opened_projects_per_user=product.max_open_studies_per_user,
105+
allow_multiple_sessions=app_settings.WEBSERVER_REALTIME_COLLABORATION
106+
is not None,
105107
max_number_of_user_sessions_per_project=(
106-
1
107-
if not app_settings.WEBSERVER_REALTIME_COLLABORATION
108-
else app_settings.WEBSERVER_REALTIME_COLLABORATION.RTC_MAX_NUMBER_OF_USERS
108+
app_settings.WEBSERVER_REALTIME_COLLABORATION.RTC_MAX_NUMBER_OF_USERS
109+
if app_settings.WEBSERVER_REALTIME_COLLABORATION
110+
else None
109111
),
110112
):
111113
raise HTTPLockedError(text="Project is locked, try later")

services/web/server/src/simcore_service_webserver/projects/_projects_service.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,9 @@ async def try_open_project_for_user(
13681368
project_uuid: ProjectID,
13691369
client_session_id: str,
13701370
app: web.Application,
1371+
*,
13711372
max_number_of_opened_projects_per_user: int | None,
1373+
allow_multiple_sessions: bool,
13721374
max_number_of_user_sessions_per_project: PositiveInt | None,
13731375
) -> bool:
13741376
"""
@@ -1426,18 +1428,22 @@ async def _open_project() -> bool:
14261428
project_uuid=project_uuid,
14271429
client_session_id=client_session_id,
14281430
)
1429-
if (
1430-
max_number_of_user_sessions_per_project is None
1431-
or not sessions_with_project
1432-
) or (
1433-
len(sessions_with_project) < max_number_of_user_sessions_per_project
1431+
if not sessions_with_project or (
1432+
allow_multiple_sessions
1433+
and (
1434+
max_number_of_user_sessions_per_project is None
1435+
or (
1436+
len(sessions_with_project)
1437+
< max_number_of_user_sessions_per_project
1438+
)
1439+
)
14341440
):
14351441
# if there are no sessions with this project, or the number of sessions is less than the maximum allowed
14361442
await user_session.add(PROJECT_ID_KEY, f"{project_uuid}")
14371443
return True
14381444

14391445
# NOTE: Special case for backwards compatibility, allow to close a tab and open the project again in a new tab
1440-
if max_number_of_user_sessions_per_project == 1:
1446+
if not allow_multiple_sessions:
14411447
current_session = user_session.get_id()
14421448
user_ids: set[int] = {s.user_id for s in sessions_with_project}
14431449
if user_ids.issubset({user_id}):

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

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
)
3838
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
3939
from models_library.projects import ProjectID
40-
from models_library.projects_access import Owner
4140
from models_library.projects_state import (
4241
ProjectRunningState,
4342
ProjectStatus,
@@ -51,6 +50,7 @@
5150
from models_library.utils.fastapi_encoders import jsonable_encoder
5251
from pytest_mock import MockerFixture
5352
from pytest_simcore.helpers.assert_checks import assert_status
53+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
5454
from pytest_simcore.helpers.typing_env import EnvVarsDict
5555
from pytest_simcore.helpers.webserver_login import log_client_in
5656
from pytest_simcore.helpers.webserver_parametrizations import (
@@ -80,12 +80,26 @@ def app_environment(
8080
app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch
8181
) -> EnvVarsDict:
8282
# disable the garbage collector
83-
monkeypatch.setenv("WEBSERVER_GARBAGE_COLLECTOR", "null")
84-
monkeypatch.setenv("WEBSERVER_DEV_FEATURES_ENABLED", "1")
85-
return app_environment | {
86-
"WEBSERVER_GARBAGE_COLLECTOR": "null",
87-
"WEBSERVER_DEV_FEATURES_ENABLED": "1",
88-
}
83+
return app_environment | setenvs_from_dict(
84+
monkeypatch,
85+
{
86+
"WEBSERVER_GARBAGE_COLLECTOR": "null",
87+
"WEBSERVER_DEV_FEATURES_ENABLED": "1",
88+
},
89+
)
90+
91+
92+
@pytest.fixture
93+
def with_disabled_rtx_collaboration(
94+
app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch
95+
) -> EnvVarsDict:
96+
# disable the real-time collaboration settings
97+
return app_environment | setenvs_from_dict(
98+
monkeypatch,
99+
{
100+
"WEBSERVER_REALTIME_COLLABORATION": "null",
101+
},
102+
)
89103

90104

91105
def assert_replaced(current_project, update_data):
@@ -723,7 +737,8 @@ async def test_open_project_with_small_amount_of_dynamic_services_starts_them_au
723737

724738

725739
@pytest.mark.parametrize(*standard_user_role_response())
726-
async def test_open_project_with_disable_service_auto_start_set_overrides_behavior(
740+
async def test_open_project_with_disable_service_auto_start_set_overrides_behavior_collaboration_disabled(
741+
with_disabled_rtx_collaboration: EnvVarsDict,
727742
client: TestClient,
728743
logged_user: UserInfoDict,
729744
user_project_with_num_dynamic_services: Callable[[int], Awaitable[ProjectDict]],
@@ -1038,7 +1053,8 @@ async def test_close_project(
10381053
(UserRole.TESTER, status.HTTP_200_OK),
10391054
],
10401055
)
1041-
async def test_get_active_project(
1056+
async def test_get_active_project_disabled_collaboration(
1057+
with_disabled_rtx_collaboration: EnvVarsDict,
10421058
client: TestClient,
10431059
logged_user: UserInfoDict,
10441060
user_project: ProjectDict,
@@ -1334,6 +1350,7 @@ def clean_redis_table(redis_client) -> None:
13341350

13351351
@pytest.mark.parametrize(*standard_user_role_response())
13361352
async def test_open_shared_project_2_users_locked(
1353+
with_disabled_rtx_collaboration: EnvVarsDict,
13371354
client: TestClient,
13381355
client_on_running_server_factory: Callable[[], TestClient],
13391356
logged_user: dict,
@@ -1388,16 +1405,18 @@ async def test_open_shared_project_2_users_locked(
13881405
expected.ok if user_role != UserRole.GUEST else status.HTTP_200_OK,
13891406
)
13901407
# now the expected result is that the project is locked and opened by client 1
1391-
owner1 = Owner(
1392-
user_id=logged_user["id"],
1393-
first_name=logged_user.get("first_name"),
1394-
last_name=logged_user.get("last_name"),
1408+
expected_project_state_client_1 = expected_project_state_client_1.model_copy(
1409+
update={
1410+
"share_state": ProjectShareStateOutputSchema(
1411+
locked=True,
1412+
status=ProjectStatus.OPENED,
1413+
current_user_groupids=[
1414+
logged_user["primary_gid"]
1415+
], # this should be the group of that user
1416+
),
1417+
}
13951418
)
1396-
expected_project_state_client_1.share_state.locked = True
1397-
expected_project_state_client_1.share_state.status = ProjectStatus.OPENED
1398-
expected_project_state_client_1.share_state.current_user_groupids = logged_user[
1399-
"primary_gid"
1400-
]
1419+
14011420
# NOTE: there are 2 calls since we are part of the primary group and the all group
14021421
await _assert_project_state_updated(
14031422
mock_project_state_updated_handler,
@@ -1432,8 +1451,15 @@ async def test_open_shared_project_2_users_locked(
14321451
shared_project,
14331452
expected.locked if user_role != UserRole.GUEST else status.HTTP_423_LOCKED,
14341453
)
1435-
expected_project_state_client_2 = deepcopy(expected_project_state_client_1)
1436-
expected_project_state_client_2.share_state.status = ProjectStatus.OPENED
1454+
expected_project_state_client_2 = expected_project_state_client_1.model_copy(
1455+
update={
1456+
"share_state": ProjectShareStateOutputSchema(
1457+
locked=expected_project_state_client_1.share_state.locked,
1458+
status=ProjectStatus.OPENED,
1459+
current_user_groupids=expected_project_state_client_1.share_state.current_user_groupids,
1460+
),
1461+
}
1462+
)
14371463

14381464
await _state_project(
14391465
client_2,
@@ -1465,9 +1491,7 @@ async def test_open_shared_project_2_users_locked(
14651491
"share_state": ProjectShareStateOutputSchema(
14661492
locked=True,
14671493
status=ProjectStatus.CLOSING,
1468-
current_user_groupids=[
1469-
owner1.user_id
1470-
], # this should be the group of that user
1494+
current_user_groupids=[logged_user["primary_gid"]],
14711495
)
14721496
}
14731497
)
@@ -1499,21 +1523,25 @@ async def test_open_shared_project_2_users_locked(
14991523
expected.ok if user_role != UserRole.GUEST else status.HTTP_423_LOCKED,
15001524
)
15011525
if not any(user_role == role for role in [UserRole.ANONYMOUS, UserRole.GUEST]):
1502-
expected_project_state_client_2.share_state.locked = True
1503-
expected_project_state_client_2.share_state.status = ProjectStatus.OPENED
1504-
owner2 = Owner(
1505-
user_id=user_2["id"],
1506-
first_name=user_2.get("first_name", None),
1507-
last_name=user_2.get("last_name", None),
1526+
expected_project_state_client_2 = expected_project_state_client_1.model_copy(
1527+
update={
1528+
"share_state": ProjectShareStateOutputSchema(
1529+
locked=True,
1530+
status=ProjectStatus.OPENED,
1531+
current_user_groupids=[int(user_2["primary_gid"])],
1532+
),
1533+
}
15081534
)
1509-
expected_project_state_client_2.share_state.current_user_groupids = [
1510-
owner2.user_id
1511-
] # this should be the group of that user
1512-
expected_project_state_client_1.share_state.locked = True
1513-
expected_project_state_client_1.share_state.status = ProjectStatus.OPENED
1514-
expected_project_state_client_1.share_state.current_user_groupids = [
1515-
owner2.user_id
1516-
] # this should be the group of that user
1535+
expected_project_state_client_1 = expected_project_state_client_1.model_copy(
1536+
update={
1537+
"share_state": ProjectShareStateOutputSchema(
1538+
locked=True,
1539+
status=ProjectStatus.OPENED,
1540+
current_user_groupids=[int(user_2["primary_gid"])],
1541+
),
1542+
}
1543+
)
1544+
15171545
# NOTE: there are 3 calls since we are part of the primary group and the all group
15181546
await _assert_project_state_updated(
15191547
mock_project_state_updated_handler,
@@ -1535,6 +1563,7 @@ async def test_open_shared_project_2_users_locked(
15351563

15361564
@pytest.mark.parametrize(*standard_user_role_response())
15371565
async def test_open_shared_project_at_same_time(
1566+
with_disabled_rtx_collaboration: EnvVarsDict,
15381567
client: TestClient,
15391568
client_on_running_server_factory: Callable[[], TestClient],
15401569
logged_user: dict,
@@ -1619,9 +1648,10 @@ async def test_open_shared_project_at_same_time(
16191648
assert data == {k: shared_project[k] for k in data}
16201649
assert project_status.share_state.locked
16211650
assert project_status.share_state.current_user_groupids
1622-
assert project_status.share_state.current_user_groupids in [
1623-
c["user"]["first_name"] for c in clients
1624-
] # TODO: this will fail
1651+
assert len(project_status.share_state.current_user_groupids) == 1
1652+
assert project_status.share_state.current_user_groupids[0] in [
1653+
c["user"]["primary_gid"] for c in clients
1654+
]
16251655

16261656
assert num_assertions == NUMBER_OF_ADDITIONAL_CLIENTS
16271657

0 commit comments

Comments
 (0)