Skip to content

Commit 9507c0d

Browse files
committed
adds tests
1 parent 3cdbdbc commit 9507c0d

File tree

3 files changed

+69
-19
lines changed

3 files changed

+69
-19
lines changed

packages/postgres-database/src/simcore_postgres_database/utils_projects_nodes.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,50 +66,46 @@ class ProjectNodeCreate(BaseModel):
6666
parent: str | None = None
6767
boot_options: dict[str, Any] | None = None
6868

69+
model_config = ConfigDict(frozen=True)
70+
6971
@classmethod
7072
def get_field_names(cls, *, exclude: set[str]) -> set[str]:
7173
return cls.model_fields.keys() - exclude
7274

75+
def _get_node_exclude_fields(self) -> set[str]:
76+
"""Get the base fields to exclude when converting to Node model."""
77+
return {"node_id", "required_resources"}
78+
7379
def model_dump_as_node(self) -> dict[str, Any]:
7480
"""Converts a ProjectNode from the database to a Node model for the API.
7581
82+
Usage:
83+
Node.model_validate(project_node_create.model_dump_as_node(), by_name=True)
84+
7685
Handles field mapping and excludes database-specific fields that are not
7786
part of the Node model.
7887
"""
79-
# Get all ProjectNode fields except those that don't belong in Node
80-
exclude_fields = {"node_id", "required_resources"}
8188
return self.model_dump(
8289
# NOTE: this setup ensures using the defaults provided in Node model when the db does not
8390
# provide them, e.g. `state`
84-
exclude=exclude_fields,
91+
exclude=self._get_node_exclude_fields(),
8592
exclude_none=True,
8693
exclude_unset=True,
8794
)
8895

89-
model_config = ConfigDict(frozen=True)
90-
9196

9297
class ProjectNode(ProjectNodeCreate):
9398
created: datetime.datetime
9499
modified: datetime.datetime
95100

96101
model_config = ConfigDict(from_attributes=True)
97102

98-
def model_dump_as_node(self) -> dict[str, Any]:
99-
"""Converts a ProjectNode from the database to a Node model for the API.
103+
def _get_node_exclude_fields(self) -> set[str]:
104+
"""Get the fields to exclude when converting to Node model, including DB-specific fields."""
105+
base_excludes = super()._get_node_exclude_fields()
106+
return base_excludes | {"created", "modified"}
100107

101-
Handles field mapping and excludes database-specific fields that are not
102-
part of the Node model.
103-
"""
104-
# Get all ProjectNode fields except those that don't belong in Node
105-
exclude_fields = {"node_id", "required_resources", "created", "modified"}
106-
return self.model_dump(
107-
# NOTE: this setup ensures using the defaults provided in Node model when the db does not
108-
# provide them, e.g. `state`
109-
exclude=exclude_fields,
110-
exclude_none=True,
111-
exclude_unset=True,
112-
)
108+
# NOTE: model_dump_as_node is inherited from ProjectNodeCreate and uses the overridden _get_node_exclude_fields
113109

114110

115111
@dataclass(frozen=True, kw_only=True)

packages/postgres-database/tests/test_utils_projects_metadata.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,19 @@ async def test_not_implemented_use_cases(
391391
parent_project_uuid=missing_parent_project["uuid"],
392392
parent_node_id=missing_parent_node.node_id,
393393
)
394+
395+
396+
async def test_model_dump_as_node(
397+
connection: SAConnection,
398+
create_fake_user: Callable[..., Awaitable[RowProxy]],
399+
create_fake_project: Callable[..., Awaitable[RowProxy]],
400+
create_fake_projects_node: Callable[[uuid.UUID], Awaitable[ProjectNode]],
401+
):
402+
user: RowProxy = await create_fake_user(connection)
403+
project: RowProxy = await create_fake_project(connection, user, hidden=True)
404+
project_node = await create_fake_projects_node(project["uuid"])
405+
406+
node_data = project_node.model_dump_as_node()
407+
assert isinstance(node_data, dict)
408+
assert node_data["key"] == project_node.key
409+
assert "node_id" not in node_data, "this is only in ProjectNode but not in Node!"

services/web/server/tests/unit/isolated/test_models.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77

88
import pytest
99
from faker import Faker
10+
from models_library.projects_nodes import Node
1011
from pydantic import TypeAdapter, ValidationError
1112
from pytest_simcore.helpers.faker_factories import random_phone_number
13+
from simcore_postgres_database.utils_projects_nodes import (
14+
ProjectNode,
15+
ProjectNodeCreate,
16+
)
1217
from simcore_service_webserver.users._controller.rest._rest_schemas import (
1318
MyPhoneRegister,
1419
PhoneNumberStr,
@@ -67,3 +72,36 @@ def test_invalid_phone_numbers(phone: str):
6772
# This test is used to tune options of PhoneNumberValidator
6873
with pytest.raises(ValidationError):
6974
MyPhoneRegister.model_validate({"phone": phone})
75+
76+
77+
_node_examples = Node.model_json_schema()["examples"]
78+
79+
80+
@pytest.mark.parametrize(
81+
"node_data",
82+
_node_examples,
83+
ids=[f"example-{i}" for i in range(len(_node_examples))],
84+
)
85+
def test_adapters_between_project_node_models(node_data: dict, faker: Faker):
86+
# -> to Node
87+
node_id = faker.uuid4()
88+
node = Node.model_validate(node_data)
89+
90+
# -> to ProjectNodeCreate and ProjectNode
91+
project_node_create = ProjectNodeCreate(
92+
node_id=node_id,
93+
**node.model_dump(by_alias=False, mode="json"),
94+
)
95+
project_node = ProjectNode(
96+
node_id=node_id,
97+
created=faker.date_time(),
98+
modified=faker.date_time(),
99+
**node.model_dump(by_alias=False, mode="json"),
100+
)
101+
102+
# -> to Node
103+
assert (
104+
Node.model_validate(project_node_create.model_dump_as_node(), by_name=True)
105+
== node
106+
)
107+
assert Node.model_validate(project_node.model_dump_as_node(), by_name=True) == node

0 commit comments

Comments
 (0)