Skip to content

Commit 03d6c46

Browse files
committed
drafting tests
1 parent c853404 commit 03d6c46

File tree

6 files changed

+422
-217
lines changed

6 files changed

+422
-217
lines changed

services/api-server/docs/api-server.drawio.svg

Lines changed: 263 additions & 168 deletions
Loading

services/api-server/tests/unit/service/conftest.py

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,141 @@
33
# pylint: disable=unused-argument
44
# pylint: disable=unused-variable
55

6+
from typing import Any
7+
68
import pytest
9+
from models_library.api_schemas_catalog import CATALOG_RPC_NAMESPACE
10+
from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
11+
from models_library.api_schemas_webserver.projects import ProjectCreateNew, ProjectGet
712
from models_library.products import ProductName
13+
from models_library.rabbitmq_basic_types import RPCMethodName, RPCNamespace
814
from models_library.users import UserID
915
from pytest_mock import MockerFixture, MockType
16+
from pytest_simcore.helpers.catalog_rpc_server import CatalogRpcSideEffects
17+
from pytest_simcore.helpers.webserver_rpc_server import WebserverRpcSideEffects
18+
from servicelib.rabbitmq._client_rpc import RabbitMQRPCClient
1019
from simcore_service_api_server._service_jobs import JobService
20+
from simcore_service_api_server._service_programs import ProgramService
1121
from simcore_service_api_server._service_solvers import SolverService
1222
from simcore_service_api_server._service_studies import StudyService
23+
from simcore_service_api_server.services_http.webserver import AuthSession
1324
from simcore_service_api_server.services_rpc.catalog import CatalogService
1425
from simcore_service_api_server.services_rpc.wb_api_server import WbApiRpcClient
1526

1627

28+
async def catalog_rpc_side_effect():
29+
return CatalogRpcSideEffects()
30+
31+
1732
@pytest.fixture
18-
def job_service(
33+
def mocked_rpc_client(mocker: MockerFixture) -> MockType:
34+
35+
async def _request(
36+
namespace: RPCNamespace,
37+
method_name: RPCMethodName,
38+
**kwargs,
39+
) -> Any:
40+
41+
kwargs.pop("timeout_s", None) # remove timeout from kwargs
42+
43+
# NOTE: we could switch to different namespaces
44+
if namespace == WEBSERVER_RPC_NAMESPACE:
45+
webserver_side_effect = WebserverRpcSideEffects()
46+
return await getattr(webserver_side_effect, method_name)(
47+
mocker.MagicMock(), **kwargs
48+
)
49+
50+
if namespace == CATALOG_RPC_NAMESPACE:
51+
catalog_side_effect = CatalogRpcSideEffects()
52+
return await getattr(catalog_side_effect, method_name)(
53+
mocker.MagicMock(), **kwargs
54+
)
55+
56+
pytest.fail(f"Unexpected namespace {namespace} and method {method_name}")
57+
58+
mock = mocker.MagicMock(spec=RabbitMQRPCClient)
59+
mock.request.side_effect = _request
60+
61+
return mock
62+
63+
64+
@pytest.fixture
65+
def wb_api_rpc_client(
66+
mocked_rpc_client: MockType,
67+
) -> WbApiRpcClient:
68+
return WbApiRpcClient(_client=mocked_rpc_client)
69+
70+
71+
@pytest.fixture
72+
def auth_session(
1973
mocker: MockerFixture,
20-
mocked_webserver_rest_api_base: dict[str, MockType],
21-
mocked_webserver_rpc_api: dict[str, MockType],
74+
# mocked_webserver_rest_api_base: MockRouter, app: FastAPI
75+
) -> AuthSession:
76+
# return AuthSession.create(app, session_cookies={}, product_extra_headers={})
77+
mock = mocker.AsyncMock(spec=AuthSession)
78+
79+
async def _create_project(project: ProjectCreateNew, **kwargs):
80+
example = ProjectGet.model_json_schema()["examples"][0]
81+
example.update(project.model_dump(exclude_unset=True))
82+
return ProjectGet.model_validate(example)
83+
84+
mock.create_project.side_effect = _create_project
85+
return mock
86+
87+
88+
@pytest.fixture
89+
def job_service(
90+
auth_session: AuthSession,
91+
wb_api_rpc_client: WbApiRpcClient,
2292
product_name: ProductName,
2393
user_id: UserID,
2494
) -> JobService:
2595
return JobService(
26-
web_rest_api=mocker.MagicMock(), # TODO: might want to return the real one
27-
web_rpc_api=WbApiRpcClient(_client=mocker.MagicMock()),
96+
web_rest_api=auth_session,
97+
web_rpc_api=wb_api_rpc_client,
2898
user_id=user_id,
2999
product_name=product_name,
30100
)
31101

32102

33103
@pytest.fixture
34104
def catalog_service(
35-
mocker: MockerFixture,
36-
mocked_catalog_rpc_api: dict[str, MockType],
105+
mocked_rpc_client: MockType,
37106
) -> CatalogService:
38-
return CatalogService(client=mocker.MagicMock())
107+
return CatalogService(client=mocked_rpc_client)
39108

40109

41110
@pytest.fixture
42111
def solver_service(
43112
catalog_service: CatalogService,
44113
job_service: JobService,
114+
product_name: ProductName,
115+
user_id: UserID,
45116
) -> SolverService:
46-
return SolverService(catalog_service=catalog_service, job_service=job_service)
117+
return SolverService(
118+
catalog_service=catalog_service,
119+
job_service=job_service,
120+
user_id=user_id,
121+
product_name=product_name,
122+
)
47123

48124

49125
@pytest.fixture
50126
def study_service(
51127
job_service: JobService,
128+
product_name: ProductName,
129+
user_id: UserID,
52130
) -> StudyService:
53131

54-
return StudyService(job_service=job_service)
132+
return StudyService(
133+
job_service=job_service,
134+
user_id=user_id,
135+
product_name=product_name,
136+
)
137+
138+
139+
@pytest.fixture
140+
def program_service(
141+
catalog_service: CatalogService,
142+
) -> ProgramService:
143+
return ProgramService(_catalog_service=catalog_service)

services/api-server/tests/unit/service/test_service_jobs.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@
44
# pylint: disable=unused-variable
55

66

7-
from models_library.products import ProductName
8-
from models_library.users import UserID
97
from pytest_mock import MockType
108
from simcore_service_api_server._service_jobs import JobService
119
from simcore_service_api_server.models.schemas.jobs import Job, JobInputs
1210
from simcore_service_api_server.models.schemas.solvers import Solver
1311

1412

1513
async def test_list_jobs_by_resource_prefix(
14+
mocked_rpc_client: MockType,
1615
job_service: JobService,
17-
mocked_webserver_rpc_api: dict[str, MockType],
1816
):
1917
# Test with default pagination parameters
2018
jobs, page_meta = await job_service.list_jobs_by_resource_prefix(
2119
job_parent_resource_name_prefix="solvers/some-solver"
2220
)
2321

2422
assert isinstance(jobs, list)
25-
mocked_webserver_rpc_api["list_projects_marked_as_jobs"].assert_called_once()
23+
24+
assert len(jobs) == page_meta.count
25+
assert mocked_rpc_client.request.call_args.args == (
26+
"webserver",
27+
"list_projects_marked_as_jobs",
28+
)
2629

2730
# Check pagination info
2831
assert page_meta.total >= 0
@@ -31,11 +34,8 @@ async def test_list_jobs_by_resource_prefix(
3134

3235

3336
async def test_create_job(
37+
mocked_rpc_client: MockType,
3438
job_service: JobService,
35-
mocked_webserver_rest_api_base: dict[str, MockType],
36-
mocked_webserver_rpc_api: dict[str, MockType],
37-
product_name: ProductName,
38-
user_id: UserID,
3939
):
4040
# Create mock solver and inputs
4141
solver = Solver(
@@ -44,9 +44,10 @@ async def test_create_job(
4444
title="Test Solver",
4545
maintainer="[email protected]",
4646
description="Test solver for testing",
47+
url=None,
4748
)
4849

49-
inputs = JobInputs(items={})
50+
inputs = JobInputs(values={})
5051

5152
# Mock URL generator
5253
def mock_url_for(*args, **kwargs):
@@ -70,5 +71,7 @@ def mock_url_for(*args, **kwargs):
7071
assert "test-solver" in job.name
7172

7273
# Verify API calls
73-
mocked_webserver_rest_api_base["create_project"].assert_called_once()
74-
mocked_webserver_rpc_api["mark_project_as_job"].assert_called_once()
74+
assert mocked_rpc_client.request.call_args.args == (
75+
"webserver",
76+
"mark_project_as_job",
77+
)

services/api-server/tests/unit/service/test_service_solvers.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212

1313
async def test_get_solver(
14+
mocked_rpc_client: MockType,
1415
solver_service: SolverService,
15-
mocked_catalog_rpc_api: dict[str, MockType],
1616
product_name: ProductName,
1717
user_id: UserID,
1818
):
@@ -24,18 +24,28 @@ async def test_get_solver(
2424
)
2525

2626
assert isinstance(solver, Solver)
27-
mocked_catalog_rpc_api["get_service"].assert_called_once()
27+
assert solver.id == "simcore/services/comp/solver-1"
28+
29+
assert mocked_rpc_client.request.called
30+
assert mocked_rpc_client.request.call_args.args == ("catalog", "get_service")
2831

2932

3033
async def test_list_jobs(
34+
mocked_rpc_client: MockType,
3135
solver_service: SolverService,
32-
mocked_webserver_rpc_api: dict[str, MockType],
3336
):
3437
# Test default parameters
3538
jobs, page_meta = await solver_service.list_jobs()
36-
assert isinstance(jobs, list)
37-
mocked_webserver_rpc_api["list_projects_marked_as_jobs"].assert_called_once()
39+
40+
assert jobs
41+
assert len(jobs) == page_meta.count
42+
43+
assert mocked_rpc_client.request.call_args.args == (
44+
"webserver",
45+
"list_projects_marked_as_jobs",
46+
)
47+
3848
assert page_meta.total >= 0
39-
assert page_meta.limit == 49
49+
assert page_meta.limit >= page_meta.count
4050
assert page_meta.offset == 0
4151
assert page_meta.count > 0

services/api-server/tests/unit/service/test_service_studies.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,50 @@
99

1010

1111
async def test_list_jobs_no_study_id(
12+
mocked_rpc_client: MockType,
1213
study_service: StudyService,
13-
mocked_webserver_rpc_api: dict[str, MockType],
1414
):
1515
# Test with default parameters
1616
jobs, page_meta = await study_service.list_jobs()
1717

1818
assert isinstance(jobs, list)
19-
mocked_webserver_rpc_api["list_projects_marked_as_jobs"].assert_called_once()
19+
assert mocked_rpc_client.request.call_args.args == (
20+
"webserver",
21+
"list_projects_marked_as_jobs",
22+
)
2023

2124
# Check pagination info
2225
assert page_meta.total >= 0
2326
assert page_meta.limit > 0
2427
assert page_meta.offset == 0
2528

2629
# Verify proper prefix was used
27-
call_kwargs = mocked_webserver_rpc_api[
28-
"list_projects_marked_as_jobs"
29-
].call_args.kwargs
30-
assert call_kwargs["job_parent_resource_name_prefix"] == "study"
30+
assert (
31+
mocked_rpc_client.request.call_args.kwargs["job_parent_resource_name_prefix"]
32+
== "study"
33+
)
34+
35+
# Check pagination parameters were passed correctly
36+
assert mocked_rpc_client.request.call_args.kwargs["offset"] == page_meta.offset
37+
assert mocked_rpc_client.request.call_args.kwargs["limit"] == page_meta.limit
3138

3239

3340
async def test_list_jobs_with_study_id(
41+
mocked_rpc_client: MockType,
3442
study_service: StudyService,
35-
mocked_webserver_rpc_api: dict[str, MockType],
3643
):
3744
# Test with a specific study ID
38-
study_id = StudyID("test-study-id")
45+
study_id = StudyID("914c7c33-8fb6-4164-9787-7b88b5c148bf")
3946
jobs, page_meta = await study_service.list_jobs(study_id=study_id)
4047

4148
assert isinstance(jobs, list)
4249

4350
# Verify proper prefix was used with study ID
44-
call_kwargs = mocked_webserver_rpc_api[
45-
"list_projects_marked_as_jobs"
46-
].call_args.kwargs
47-
assert call_kwargs["job_parent_resource_name_prefix"] == f"study/{study_id}"
51+
assert (
52+
mocked_rpc_client.request.call_args.kwargs["job_parent_resource_name_prefix"]
53+
== f"study/{study_id}"
54+
)
4855

4956
# Check pagination parameters were passed correctly
50-
assert call_kwargs["offset"] == 0
51-
assert call_kwargs["limit"] > 0
57+
assert mocked_rpc_client.request.call_args.kwargs["offset"] == page_meta.offset
58+
assert mocked_rpc_client.request.call_args.kwargs["limit"] == page_meta.limit

services/api-server/tests/unit/service/test_services_catalog.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from simcore_service_api_server.services_rpc.catalog import CatalogService
1515

1616

17-
def to_solver(
17+
def _to_solver_schema(
1818
service: LatestServiceGet | ServiceGetV2, href_self: HttpUrl | None = None
1919
) -> Solver:
2020
# NOTE: this is an adapter around models on CatalogService interface
@@ -31,14 +31,14 @@ def to_solver(
3131
async def test_catalog_service_read_solvers(
3232
product_name: ProductName,
3333
user_id: UserID,
34-
mocked_catalog_rpc_api: dict[str, MockType],
34+
mocked_rpc_client: MockType,
3535
catalog_service: CatalogService,
3636
):
3737
# Step 1: List latest releases in a page
3838
latest_releases, meta = await catalog_service.list_latest_releases(
3939
product_name=product_name, user_id=user_id
4040
)
41-
solver_releases_page = [to_solver(srv) for srv in latest_releases]
41+
solver_releases_page = [_to_solver_schema(srv) for srv in latest_releases]
4242

4343
assert solver_releases_page, "Releases page should not be empty"
4444
assert meta.offset == 0
@@ -62,7 +62,7 @@ async def test_catalog_service_read_solvers(
6262
name=selected_solver.id,
6363
version=oldest_release.version,
6464
)
65-
solver = to_solver(service)
65+
solver = _to_solver_schema(service)
6666
assert solver.id == selected_solver.id
6767
assert solver.version == oldest_release.version
6868

@@ -80,7 +80,8 @@ async def test_catalog_service_read_solvers(
8080
assert any(port.kind == "output" for port in ports), "Should contain output ports"
8181

8282
# checks calls to rpc
83-
mocked_catalog_rpc_api["list_services_paginated"].assert_called_once()
84-
mocked_catalog_rpc_api["list_my_service_history_paginated"].assert_called_once()
85-
mocked_catalog_rpc_api["get_service"].assert_called_once()
86-
mocked_catalog_rpc_api["get_service_ports"].assert_called_once()
83+
mocked_rpc_client.request
84+
# mocked_catalog_rpc_api["list_services_paginated"].assert_called_once()
85+
# mocked_catalog_rpc_api["list_my_service_history_paginated"].assert_called_once()
86+
# mocked_catalog_rpc_api["get_service"].assert_called_once()
87+
# mocked_catalog_rpc_api["get_service_ports"].assert_called_once()

0 commit comments

Comments
 (0)