Skip to content

Commit 2f05c0c

Browse files
author
Andrei Neagu
committed
speedup tests
1 parent 11fbf6a commit 2f05c0c

File tree

2 files changed

+79
-61
lines changed

2 files changed

+79
-61
lines changed

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/_common/base_display_model.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections.abc import Callable
22
from typing import Any, Self, TypeAlias
33

4-
from pydantic import BaseModel, PrivateAttr
4+
from pydantic import BaseModel, NonNegativeInt, PrivateAttr
55

66
CompleteModelDict: TypeAlias = dict[str, Any]
77

@@ -27,20 +27,30 @@ def _get_on_change_callbacks_to_run(self, update_obj: Self) -> list[Callable]:
2727

2828
return callbaks_to_run
2929

30-
def update(self, update_obj: Self) -> None:
30+
def update(self, update_obj: Self) -> NonNegativeInt:
31+
"""
32+
updates the model with the values from update_obj
33+
returns the number of callbacks that were run
34+
"""
3135
callbacks_to_run = self._get_on_change_callbacks_to_run(update_obj)
3236

3337
for attribute_name, update_value in update_obj.__dict__.items():
3438
current_value = getattr(self, attribute_name)
3539
if current_value != update_value:
3640
if isinstance(update_value, BaseUpdatableDisplayModel):
37-
# do not replace existing object, update it's content
38-
current_value.update(update_value)
41+
if type(current_value) is type(update_value):
42+
# when the same type update the existing object
43+
current_value.update(update_value)
44+
else:
45+
setattr(self, attribute_name, update_value)
46+
3947
setattr(self, attribute_name, update_value)
4048

4149
for callback in callbacks_to_run:
4250
callback()
4351

52+
return len(callbacks_to_run)
53+
4454
def _raise_if_attribute_not_declared_in_model(self, attribute: str) -> None:
4555
if attribute not in self.__class__.model_fields:
4656
msg = f"Attribute '{attribute}' is not part of the model fields"

services/dynamic-scheduler/tests/unit/api_frontend/_common/test_updatable_component.py

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,71 @@
22
# pylint:disable=unused-argument
33

44
from functools import cached_property
5+
from unittest.mock import Mock
56

67
import nicegui
78
import pytest
89
from fastapi import FastAPI
910
from helpers import assert_contains_text
1011
from nicegui import APIRouter, ui
1112
from playwright.async_api import Page
13+
from pydantic import NonNegativeInt
14+
from pytest_simcore.helpers.typing_env import EnvVarsDict
1215
from simcore_service_dynamic_scheduler.api.frontend._common.base_display_model import (
1316
BaseUpdatableDisplayModel,
1417
)
1518
from simcore_service_dynamic_scheduler.api.frontend._common.updatable_component import (
1619
BaseUpdatableComponent,
1720
)
18-
from simcore_service_dynamic_scheduler.api.frontend._utils import (
19-
get_settings,
20-
set_parent_app,
21-
)
22-
from simcore_service_dynamic_scheduler.core.settings import ApplicationSettings
21+
from simcore_service_dynamic_scheduler.api.frontend._utils import set_parent_app
22+
2323

24-
pytest_simcore_core_services_selection = [
25-
"postgres",
26-
"rabbit",
27-
"redis",
28-
]
24+
@pytest.fixture
25+
def app_environment() -> EnvVarsDict:
26+
return {}
2927

30-
pytest_simcore_ops_services_selection = [
31-
"redis-commander",
32-
]
28+
29+
@pytest.fixture
30+
def mount_path() -> str:
31+
return "/dynamic-scheduler/"
32+
33+
34+
@pytest.fixture
35+
def use_internal_scheduler() -> bool:
36+
return True
37+
38+
39+
@pytest.fixture
40+
def router(person: "Person") -> APIRouter:
41+
router = APIRouter()
42+
43+
@ui.page("/", api_router=router)
44+
async def index():
45+
_index_page_ui(person)
46+
47+
return router
48+
49+
50+
@pytest.fixture
51+
def not_initialized_app(
52+
reset_nicegui_app: None,
53+
app_environment: EnvVarsDict,
54+
router: APIRouter,
55+
mount_path: str,
56+
) -> FastAPI:
57+
minimal_app = FastAPI()
58+
59+
mock_settings = Mock()
60+
mock_settings.DYNAMIC_SCHEDULER_UI_MOUNT_PATH = mount_path
61+
minimal_app.state.settings = mock_settings
62+
63+
nicegui.app.include_router(router)
64+
65+
nicegui.ui.run_with(
66+
minimal_app, mount_path=mount_path, storage_secret="test-secret"
67+
)
68+
set_parent_app(minimal_app)
69+
return minimal_app
3370

3471

3572
class Pet(BaseUpdatableDisplayModel):
@@ -105,40 +142,6 @@ def _index_page_ui(person: Person) -> None:
105142
ui.label("AFTER_LABEL")
106143

107144

108-
@pytest.fixture
109-
def use_internal_scheduler() -> bool:
110-
return True
111-
112-
113-
@pytest.fixture
114-
def router(person: Person) -> APIRouter:
115-
router = APIRouter()
116-
117-
@ui.page("/", api_router=router)
118-
async def index():
119-
_index_page_ui(person)
120-
121-
return router
122-
123-
124-
@pytest.fixture
125-
def not_initialized_app(not_initialized_app: FastAPI, router: APIRouter) -> FastAPI:
126-
minimal_app = FastAPI()
127-
128-
settings = ApplicationSettings.create_from_envs()
129-
minimal_app.state.settings = settings
130-
131-
nicegui.app.include_router(router)
132-
133-
nicegui.ui.run_with(
134-
minimal_app,
135-
mount_path=settings.DYNAMIC_SCHEDULER_UI_MOUNT_PATH,
136-
storage_secret=settings.DYNAMIC_SCHEDULER_UI_STORAGE_SECRET.get_secret_value(),
137-
)
138-
set_parent_app(minimal_app)
139-
return minimal_app
140-
141-
142145
async def _ensure_before_label(async_page: Page) -> None:
143146
await assert_contains_text(async_page, "BEFORE_LABEL")
144147

@@ -176,33 +179,38 @@ async def _ensure_index_page(async_page: Page, person: Person) -> None:
176179

177180

178181
@pytest.mark.parametrize(
179-
"person, person_update",
182+
"person, person_update, expected_callbacks_count",
180183
[
181184
pytest.param(
182185
Person(name="Alice", age=30, companion=Pet(name="Fluffy", species="cat")),
183186
Person(name="Alice", age=30, companion=Pet(name="Buddy", species="dog")),
184-
id="change-pet",
185-
)
187+
0,
188+
id="update-pet-via-attribute-biding-no-rerender",
189+
),
190+
pytest.param(
191+
Person(name="Alice", age=30, companion=Pet(name="Fluffy", species="cat")),
192+
Person(name="Alice", age=30, companion=Friend(name="Marta", age=30)),
193+
1,
194+
id="update-pet-ui-rerednder",
195+
),
186196
],
187197
)
188198
async def test_updatable_component(
189199
app_runner: None,
190200
async_page: Page,
201+
mount_path: str,
191202
server_host_port: str,
192203
person: Person,
193204
person_update: Person,
205+
expected_callbacks_count: NonNegativeInt,
194206
):
195-
await async_page.goto(
196-
f"{server_host_port}{get_settings().DYNAMIC_SCHEDULER_UI_MOUNT_PATH}"
197-
)
207+
await async_page.goto(f"{server_host_port}{mount_path}")
198208

199209
# check initial page layout
200210
await _ensure_index_page(async_page, person)
201211

202-
person.update(person_update)
212+
callbacks_count = person.update(person_update)
213+
assert callbacks_count == expected_callbacks_count
203214

204215
# change layout after update
205216
await _ensure_index_page(async_page, person_update)
206-
207-
208-
# TODO: make tests go faster since we are running differently

0 commit comments

Comments
 (0)