Skip to content

Commit 54b40c0

Browse files
committed
add test
1 parent b3c78e9 commit 54b40c0

File tree

4 files changed

+48
-29
lines changed

4 files changed

+48
-29
lines changed

services/api-server/src/simcore_service_api_server/_service_job.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ async def create_job(
6767
) -> tuple[Job, ProjectGet]:
6868
# creates NEW job as prototype
6969
pre_job = Job.create_job_from_solver_or_program(
70-
solver_or_program_name=solver_or_program.name, inputs=inputs, name=name
70+
solver_or_program_name=solver_or_program.name, inputs=inputs
7171
)
7272
with log_context(
7373
logger=_logger, level=logging.DEBUG, msg=f"Creating job {pre_job.name}"
@@ -77,6 +77,7 @@ async def create_job(
7777
job=pre_job,
7878
inputs=inputs,
7979
description=description,
80+
name=name,
8081
)
8182
new_project: ProjectGet = await self._web_rest_api.create_project(
8283
project_in,

services/api-server/src/simcore_service_api_server/models/schemas/jobs.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
StringConstraints,
2525
TypeAdapter,
2626
ValidationError,
27-
ValidationInfo,
28-
field_validator,
2927
)
3028
from servicelib.logging_utils import LogLevelInt, LogMessageStr
3129
from starlette.datastructures import Headers
@@ -275,28 +273,18 @@ class Job(BaseModel):
275273
}
276274
)
277275

278-
@field_validator("name", mode="before")
279-
@classmethod
280-
def _check_name(cls, v, info: ValidationInfo):
281-
_id = str(info.data["id"])
282-
if not v.endswith(f"/{_id}"):
283-
msg = f"Resource name [{v}] and id [{_id}] do not match"
284-
raise ValueError(msg)
285-
return v
286-
287276
# constructors ------
288277

289278
@classmethod
290279
def create_now(
291280
cls,
292281
parent_name: RelativeResourceName,
293282
inputs_checksum: str,
294-
name: str | None = None,
295283
) -> "Job":
296284
global_uuid = uuid4()
297285

298286
return cls(
299-
name=name or cls.compose_resource_name(parent_name, global_uuid),
287+
name=cls.compose_resource_name(parent_name, global_uuid),
300288
id=global_uuid,
301289
runner_name=parent_name,
302290
inputs_checksum=inputs_checksum,
@@ -308,10 +296,9 @@ def create_now(
308296

309297
@classmethod
310298
def create_job_from_solver_or_program(
311-
cls, *, solver_or_program_name: str, inputs: JobInputs, name: str | None = None
299+
cls, *, solver_or_program_name: str, inputs: JobInputs
312300
):
313301
return Job.create_now(
314-
name=name,
315302
parent_name=solver_or_program_name,
316303
inputs_checksum=inputs.compute_checksum(),
317304
)

services/api-server/src/simcore_service_api_server/services_http/solver_job_models_converters.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs.py
44
"""
55

6-
import urllib.parse
76
import uuid
87
from collections.abc import Callable
98
from datetime import UTC, datetime
@@ -124,6 +123,7 @@ def create_new_project_for_job(
124123
job: Job,
125124
inputs: JobInputs,
126125
description: str | None = None,
126+
name: str | None = None,
127127
) -> ProjectCreateNew:
128128
"""
129129
Creates a project for a solver's job
@@ -162,7 +162,7 @@ def create_new_project_for_job(
162162

163163
return ProjectCreateNew(
164164
uuid=project_id,
165-
name=job.name, # NOTE: this IS an identifier as well. MUST NOT be changed in the case of project APIs!
165+
name=name or job.name,
166166
description=description
167167
or f"Study associated to solver/study/program job:\n{job_info}",
168168
thumbnail="https://via.placeholder.com/170x120.png", # type: ignore[arg-type]
@@ -199,8 +199,6 @@ def create_job_from_project(
199199
raise ValidationError
200200
"""
201201
assert len(project.workbench) == 1 # nosec
202-
assert solver_or_program.version in project.name # nosec
203-
assert urllib.parse.quote_plus(solver_or_program.id) in project.name # nosec
204202

205203
# get solver node
206204
node_id = next(iter(project.workbench.keys()))
@@ -216,7 +214,9 @@ def create_job_from_project(
216214

217215
job = Job(
218216
id=job_id,
219-
name=project.name,
217+
name=Job.compose_resource_name(
218+
parent_name=solver_or_program_name, job_id=job_id
219+
),
220220
inputs_checksum=job_inputs.compute_checksum(),
221221
created_at=project.creation_date, # type: ignore[arg-type]
222222
runner_name=solver_or_program_name,

services/api-server/tests/unit/test_api_programs.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import httpx
99
import pytest
10+
from faker import Faker
1011
from fastapi import status
1112
from httpx import AsyncClient
1213
from models_library.api_schemas_storage.storage_schemas import FileUploadSchema
@@ -20,6 +21,8 @@
2021
from simcore_service_api_server.models.schemas.jobs import Job
2122
from simcore_service_api_server.models.schemas.programs import Program
2223

24+
_faker = Faker()
25+
2326

2427
async def test_get_program_release(
2528
auth: httpx.BasicAuth,
@@ -43,17 +46,29 @@ async def test_get_program_release(
4346
assert program.version == version
4447

4548

49+
@pytest.mark.parametrize(
50+
"job_name,job_description",
51+
[
52+
(None, None),
53+
(_faker.name(), None),
54+
(None, _faker.sentence()),
55+
(_faker.name(), _faker.sentence()),
56+
],
57+
)
4658
@pytest.mark.parametrize("capture_name", ["create_program_job_success.json"])
4759
async def test_create_program_job(
4860
auth: httpx.BasicAuth,
4961
client: AsyncClient,
5062
mocked_webserver_rest_api_base,
5163
mocked_rpc_catalog_service_api: dict[str, MockType],
64+
mocked_webserver_rpc_api: dict[str, MockType],
5265
create_respx_mock_from_capture: CreateRespxMockCallback,
5366
mocker: MockerFixture,
5467
user_id: UserID,
5568
capture_name: str,
5669
project_tests_dir: Path,
70+
job_name: str | None,
71+
job_description: str | None,
5772
):
5873

5974
mocker.patch(
@@ -76,18 +91,33 @@ def _side_effect(
7691

7792
response_body = capture.response_body
7893

79-
# first call defines the project uuid
94+
# first call creates project
8095
if server_state.get("project_uuid") is None:
81-
_project_uuid = json.loads(request.content.decode("utf-8")).get("uuid")
96+
get_body_field = lambda field: json.loads(
97+
request.content.decode("utf-8")
98+
).get(field)
99+
100+
_project_uuid = get_body_field("uuid")
82101
assert _project_uuid
83102
server_state["project_uuid"] = _project_uuid
84103

104+
_name = get_body_field("name")
105+
assert _name
106+
server_state["name"] = _name
107+
108+
_description = get_body_field("description")
109+
assert _description
110+
server_state["description"] = _description
111+
112+
if job_name:
113+
assert job_name == get_body_field("name")
114+
if job_description:
115+
assert job_description == get_body_field("description")
116+
85117
if request.url.path.endswith("/result"):
86-
capture_uuid = response_body["data"]["uuid"]
87118
response_body["data"]["uuid"] = server_state["project_uuid"]
88-
response_body["data"]["name"] = response_body["data"]["name"].replace(
89-
capture_uuid, server_state["project_uuid"]
90-
)
119+
response_body["data"]["name"] = server_state["name"]
120+
response_body["data"]["description"] = server_state["description"]
91121
assert isinstance(response_body, dict)
92122
return response_body
93123

@@ -100,14 +130,15 @@ def _side_effect(
100130
side_effects_callbacks=3 * [partial(_side_effect, _server_state)],
101131
)
102132

103-
# Arrange
104133
program_key = "simcore/services/dynamic/electrode-selector"
105134
version = "2.1.3"
106135

136+
body = {"name": job_name, "description": job_description}
137+
107138
response = await client.post(
108139
f"/{API_VTAG}/programs/{program_key}/releases/{version}/jobs",
109-
# headers=headers,
110140
auth=auth,
141+
json={k: v for k, v in body.items() if v is not None},
111142
)
112143

113144
# Assert

0 commit comments

Comments
 (0)