Skip to content

Commit 497c791

Browse files
committed
✨ Refactor test fixtures for improved clarity and consistency in mocking APIs
1 parent 2b6ea63 commit 497c791

File tree

3 files changed

+49
-51
lines changed

3 files changed

+49
-51
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ async def create_fake_api_keys(
254254
create_user_ids: Callable[[PositiveInt], AsyncGenerator[PositiveInt, None]],
255255
create_product_names: Callable[[PositiveInt], AsyncGenerator[str, None]],
256256
) -> AsyncGenerator[Callable[[PositiveInt], AsyncGenerator[ApiKeyInDB, None]], None]:
257+
257258
async def _generate_fake_api_key(n: PositiveInt):
258259
users, products = create_user_ids(n), create_product_names(n)
259260
excluded_column = "api_secret"
@@ -267,7 +268,9 @@ async def _generate_fake_api_key(n: PositiveInt):
267268
result = await connection.execute(
268269
api_keys.insert()
269270
.values(
270-
api_secret=sa.func.crypt(plain_api_secret, sa.func.gen_salt("bf", 10)),
271+
api_secret=sa.func.crypt(
272+
plain_api_secret, sa.func.gen_salt("bf", 10)
273+
),
271274
**api_auth,
272275
)
273276
.returning(*returning_cols)

services/api-server/tests/unit/_with_db/test_product.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,22 @@ async def test_product_webserver(
2929
mocked_webserver_rest_api_base: respx.MockRouter,
3030
create_fake_api_keys: Callable[[PositiveInt], AsyncGenerator[ApiKeyInDB, None]],
3131
faker: Faker,
32-
) -> None:
32+
):
3333
assert client
3434

35-
keys: dict[int, ApiKeyInDB] = {}
35+
wallet_to_api_keys_map: dict[int, ApiKeyInDB] = {}
3636
wallet_id: int = faker.pyint(min_value=1)
37-
async for key in create_fake_api_keys(2):
37+
async for api_key in create_fake_api_keys(2):
3838
wallet_id += faker.pyint(min_value=1)
39-
keys[wallet_id] = key
39+
wallet_to_api_keys_map[wallet_id] = api_key
4040

4141
def _check_key_product_compatibility(request: httpx.Request, **kwargs):
4242
assert (
4343
received_product_name := request.headers.get("x-simcore-products-name")
4444
) is not None
4545
assert (wallet_id := kwargs.get("wallet_id")) is not None
46-
assert (key := keys[int(wallet_id)]) is not None
47-
assert key.product_name == received_product_name
46+
assert (api_key := wallet_to_api_keys_map[int(wallet_id)]) is not None
47+
assert api_key.product_name == received_product_name
4848
return httpx.Response(
4949
status.HTTP_200_OK,
5050
json=jsonable_encoder(
@@ -53,7 +53,7 @@ def _check_key_product_compatibility(request: httpx.Request, **kwargs):
5353
wallet_id=wallet_id,
5454
name="my_wallet",
5555
description="this is my wallet",
56-
owner=key.id_,
56+
owner=api_key.id_,
5757
thumbnail="something",
5858
status=WalletStatus.ACTIVE,
5959
created=datetime.datetime.now(),
@@ -68,30 +68,29 @@ def _check_key_product_compatibility(request: httpx.Request, **kwargs):
6868
path__regex=r"/wallets/(?P<wallet_id>[-+]?\d+)"
6969
).mock(side_effect=_check_key_product_compatibility)
7070

71-
for wallet_id in keys:
72-
key = keys[wallet_id]
71+
for wallet_id, api_key in wallet_to_api_keys_map.items():
7372
response = await client.get(
7473
f"{API_VTAG}/wallets/{wallet_id}",
75-
auth=httpx.BasicAuth(key.api_key, key.api_secret),
74+
auth=httpx.BasicAuth(api_key.api_key, api_key.api_secret),
7675
)
7776
assert response.status_code == status.HTTP_200_OK
78-
assert wallet_get_mock.call_count == len(keys)
77+
assert wallet_get_mock.call_count == len(wallet_to_api_keys_map)
7978

8079

8180
async def test_product_catalog(
8281
client: httpx.AsyncClient,
8382
mocked_catalog_rpc_api: dict[str, MockType],
8483
create_fake_api_keys: Callable[[PositiveInt], AsyncGenerator[ApiKeyInDB, None]],
85-
) -> None:
84+
):
8685
assert client
8786

88-
keys: list[ApiKeyInDB] = [key async for key in create_fake_api_keys(2)]
89-
assert len({key.product_name for key in keys}) == 2
87+
valid_api_auths: list[ApiKeyInDB] = [key async for key in create_fake_api_keys(2)]
88+
assert len({key.product_name for key in valid_api_auths}) == 2
9089

91-
for key in keys:
90+
for api_auth in valid_api_auths:
9291
await client.get(
9392
f"{API_VTAG}/solvers/simcore/services/comp/isolve/releases/2.0.24",
94-
auth=httpx.BasicAuth(key.api_key, key.api_secret),
93+
auth=httpx.BasicAuth(api_auth.api_key, api_auth.api_secret),
9594
)
9695

9796
assert mocked_catalog_rpc_api["get_service"].called

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

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
# pylint: disable=redefined-outer-name
33
# pylint: disable=unused-argument
44
# pylint: disable=unused-variable
5+
# pylint: disable=broad-exception-caught
56

67
import json
78
import subprocess
8-
from collections.abc import AsyncIterator, Callable, Iterable, Iterator
9+
from collections.abc import AsyncIterator, Callable, Iterator
910
from copy import deepcopy
1011
from pathlib import Path
1112
from typing import Any
@@ -46,9 +47,6 @@
4647
from pytest_simcore.simcore_webserver_projects_rest_api import GET_PROJECT
4748
from requests.auth import HTTPBasicAuth
4849
from respx import MockRouter
49-
from servicelib.rabbitmq._client_rpc import RabbitMQRPCClient
50-
from servicelib.rabbitmq.rpc_interfaces.catalog import services as catalog_rpc
51-
from simcore_service_api_server.api.dependencies.rabbitmq import get_rabbitmq_rpc_client
5250
from simcore_service_api_server.core.application import init_app
5351
from simcore_service_api_server.core.settings import ApplicationSettings
5452
from simcore_service_api_server.db.repositories.api_keys import UserAndProductTuple
@@ -93,10 +91,19 @@ def app_environment(
9391
def mock_missing_plugins(app_environment: EnvVarsDict, mocker: MockerFixture):
9492
settings = ApplicationSettings.create_from_envs()
9593
if settings.API_SERVER_RABBITMQ is None:
96-
mocker.patch("simcore_service_api_server.core.application.setup_rabbitmq")
97-
mocker.patch(
98-
"simcore_service_api_server.core._prometheus_instrumentation.setup_prometheus_instrumentation"
94+
import simcore_service_api_server.core.application
95+
96+
mocker.patch.object(
97+
simcore_service_api_server.core.application,
98+
"setup_rabbitmq",
99+
autospec=True,
99100
)
101+
mocker.patch.object(
102+
simcore_service_api_server.core.application,
103+
"setup_prometheus_instrumentation",
104+
autospec=True,
105+
)
106+
100107
return app_environment
101108

102109

@@ -346,34 +353,27 @@ def mocked_webserver_rest_api_base(
346353

347354

348355
@pytest.fixture
349-
def mocked_webserver_rpc_api(
350-
app: FastAPI, mocker: MockerFixture
351-
) -> dict[str, MockType]:
352-
from servicelib.rabbitmq.rpc_interfaces.webserver import projects as projects_rpc
353-
from simcore_service_api_server.services_rpc import wb_api_server
354-
355-
# NOTE: mock_missing_plugins patches `setup_rabbitmq`
356-
try:
357-
wb_api_server.WbApiRpcClient.get_from_app_state(app)
358-
except AttributeError:
359-
wb_api_server.setup(
360-
app, RabbitMQRPCClient("fake_rpc_client", settings=mocker.MagicMock())
361-
)
362-
363-
settings: ApplicationSettings = app.state.settings
364-
assert settings.API_SERVER_WEBSERVER
356+
def mocked_webserver_rpc_api(mocker: MockerFixture) -> dict[str, MockType]:
357+
"""
358+
Mocks the webserver's simcore service RPC API for testing purposes.
359+
"""
360+
from servicelib.rabbitmq.rpc_interfaces.webserver import (
361+
projects as projects_rpc, # keep import here
362+
)
365363

366364
side_effects = WebserverRpcSideEffects()
367365

368366
return {
369367
"mark_project_as_job": mocker.patch.object(
370368
projects_rpc,
371369
"mark_project_as_job",
370+
autospec=True,
372371
side_effect=side_effects.mark_project_as_job,
373372
),
374373
"list_projects_marked_as_jobs": mocker.patch.object(
375374
projects_rpc,
376375
"list_projects_marked_as_jobs",
376+
autospec=True,
377377
side_effect=side_effects.list_projects_marked_as_jobs,
378378
),
379379
}
@@ -473,20 +473,16 @@ def mocked_catalog_rest_api_base(
473473

474474

475475
@pytest.fixture
476-
def mocked_catalog_rpc_api(
477-
app: FastAPI, mocker: MockerFixture
478-
) -> Iterable[dict[str, MockType]]:
476+
def mocked_catalog_rpc_api(mocker: MockerFixture) -> dict[str, MockType]:
479477
"""
480-
Mocks the RPC catalog service API for testing purposes.
478+
Mocks the catalog's simcore service RPC API for testing purposes.
481479
"""
480+
from servicelib.rabbitmq.rpc_interfaces.catalog import (
481+
services as catalog_rpc, # keep import here
482+
)
482483

483-
def get_mock_rabbitmq_rpc_client():
484-
return MagicMock()
485-
486-
app.dependency_overrides[get_rabbitmq_rpc_client] = get_mock_rabbitmq_rpc_client
487484
side_effects = CatalogRpcSideEffects()
488-
489-
yield {
485+
return {
490486
"list_services_paginated": mocker.patch.object(
491487
catalog_rpc,
492488
"list_services_paginated",
@@ -518,7 +514,6 @@ def get_mock_rabbitmq_rpc_client():
518514
side_effect=side_effects.get_service_ports,
519515
),
520516
}
521-
app.dependency_overrides.pop(get_rabbitmq_rpc_client)
522517

523518

524519
#
@@ -648,6 +643,7 @@ def clone_project_task(self, request: httpx.Request, *, project_id: str):
648643
return self._set_result_and_get_reponse(project_get)
649644

650645
def get_result(self, request: httpx.Request, *, task_id: str):
646+
assert request
651647
return httpx.Response(
652648
status.HTTP_200_OK, json={"data": self._results[task_id]}
653649
)

0 commit comments

Comments
 (0)