Skip to content

Commit 6c3d6e9

Browse files
committed
Move api tests of functions to deeper level
1 parent 8402ba0 commit 6c3d6e9

File tree

3 files changed

+423
-337
lines changed

3 files changed

+423
-337
lines changed

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions_rpc_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ async def delete_function(
102102
async def list_functions(
103103
rabbitmq_rpc_client: RabbitMQRPCClient,
104104
*,
105-
pagination_limit: int,
106105
pagination_offset: int,
106+
pagination_limit: int,
107107
) -> tuple[list[RegisteredFunction], PageMetaInfoLimitOffset]:
108108
result: tuple[list[RegisteredFunction], PageMetaInfoLimitOffset] = (
109109
await rabbitmq_rpc_client.request(

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

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
1+
# pylint: disable=redefined-outer-name
2+
# pylint: disable=no-value-for-parameter
3+
# pylint: disable=super-init-not-called
4+
# pylint: disable=unused-argument
5+
6+
from uuid import uuid4
7+
18
import pytest
9+
from fastapi import FastAPI
10+
from models_library.api_schemas_webserver.functions_wb_schema import (
11+
Function,
12+
FunctionIDNotFoundError,
13+
FunctionJob,
14+
FunctionJobCollection,
15+
FunctionJobCollectionIDNotFoundError,
16+
FunctionJobIDNotFoundError,
17+
RegisteredFunction,
18+
RegisteredFunctionJob,
19+
RegisteredFunctionJobCollection,
20+
)
21+
from models_library.rest_pagination import PageMetaInfoLimitOffset
22+
from pydantic import TypeAdapter
23+
from pytest_mock import MockerFixture
224
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
325
from pytest_simcore.helpers.typing_env import EnvVarsDict
26+
from servicelib.rabbitmq._client_rpc import RabbitMQRPCClient
27+
from simcore_service_api_server.api.routes.functions_routes import get_wb_api_rpc_client
28+
from simcore_service_api_server.services_rpc.wb_api_server import WbApiRpcClient
429

530

631
@pytest.fixture
@@ -16,3 +41,302 @@ def app_environment(
1641
"WEBSERVER_FUNCTIONS": "1",
1742
},
1843
)
44+
45+
46+
class DummyRpcClient(RabbitMQRPCClient):
47+
48+
def __init__(self):
49+
self.client_name = "dummy_client"
50+
self.settings = {} # Add a settings attribute to avoid AttributeError
51+
52+
async def request(self, namespace: str, method_name: str, **kwargs):
53+
# Mock implementation of the request method
54+
assert isinstance(namespace, str)
55+
assert isinstance(method_name, str)
56+
assert isinstance(kwargs, dict)
57+
return {"mocked_response": True}
58+
59+
60+
@pytest.fixture
61+
async def mock_wb_api_server_rpc(app: FastAPI, mocker: MockerFixture) -> MockerFixture:
62+
63+
app.dependency_overrides[get_wb_api_rpc_client] = lambda: WbApiRpcClient(
64+
_client=DummyRpcClient()
65+
)
66+
return mocker
67+
68+
69+
class MockFunctionRegister:
70+
def __init__(self) -> None:
71+
self._functions = {}
72+
self._function_jobs = {}
73+
self._function_job_collections = {}
74+
75+
async def register_function(
76+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function: Function
77+
) -> RegisteredFunction:
78+
assert isinstance(rabbitmq_rpc_client, RabbitMQRPCClient)
79+
uid = uuid4()
80+
self._functions[uid] = TypeAdapter(RegisteredFunction).validate_python(
81+
{
82+
"uid": str(uid),
83+
"title": function.title,
84+
"function_class": function.function_class,
85+
"project_id": getattr(function, "project_id", None),
86+
"description": function.description,
87+
"input_schema": function.input_schema,
88+
"output_schema": function.output_schema,
89+
"default_inputs": None,
90+
}
91+
)
92+
return self._functions[uid]
93+
94+
async def get_function(
95+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_id: str
96+
) -> RegisteredFunction:
97+
assert isinstance(rabbitmq_rpc_client, RabbitMQRPCClient)
98+
# Mimic retrieval of a function based on function_id and raise 404 if not found
99+
if function_id not in self._functions:
100+
raise FunctionIDNotFoundError(function_id=function_id)
101+
return self._functions[function_id]
102+
103+
async def run_function(
104+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_id: str, inputs: dict
105+
) -> dict:
106+
# Mimic running a function and returning a success status
107+
if function_id not in self._functions:
108+
raise FunctionIDNotFoundError(function_id=function_id)
109+
return {"status": "success", "function_id": function_id, "inputs": inputs}
110+
111+
async def list_functions(
112+
self,
113+
rabbitmq_rpc_client: RabbitMQRPCClient,
114+
pagination_offset: int = 0,
115+
pagination_limit: int = 10,
116+
) -> tuple[list[RegisteredFunction], PageMetaInfoLimitOffset]:
117+
# Mimic listing all functions
118+
functions_list = list(self._functions.values())[
119+
pagination_offset : pagination_offset + pagination_limit
120+
]
121+
total_count = len(self._functions)
122+
page_meta_info = PageMetaInfoLimitOffset(
123+
total=total_count,
124+
limit=pagination_limit,
125+
offset=pagination_offset,
126+
count=len(functions_list),
127+
)
128+
return functions_list, page_meta_info
129+
130+
async def delete_function(
131+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_id: str
132+
) -> None:
133+
# Mimic deleting a function
134+
if function_id in self._functions:
135+
del self._functions[function_id]
136+
else:
137+
raise FunctionIDNotFoundError(function_id=function_id)
138+
139+
async def register_function_job(
140+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_job: FunctionJob
141+
) -> RegisteredFunctionJob:
142+
# Mimic registering a function job
143+
uid = uuid4()
144+
self._function_jobs[uid] = TypeAdapter(RegisteredFunctionJob).validate_python(
145+
{
146+
"uid": str(uid),
147+
"function_uid": function_job.function_uid,
148+
"title": function_job.title,
149+
"description": function_job.description,
150+
"project_job_id": getattr(function_job, "project_job_id", None),
151+
"inputs": function_job.inputs,
152+
"outputs": function_job.outputs,
153+
"function_class": function_job.function_class,
154+
}
155+
)
156+
return self._function_jobs[uid]
157+
158+
async def get_function_job(
159+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_job_id: str
160+
) -> RegisteredFunctionJob:
161+
# Mimic retrieval of a function job based on function_job_id and raise 404 if not found
162+
if function_job_id not in self._function_jobs:
163+
raise FunctionJobIDNotFoundError(function_id=function_job_id)
164+
return self._function_jobs[function_job_id]
165+
166+
async def list_function_jobs(
167+
self,
168+
rabbitmq_rpc_client: RabbitMQRPCClient,
169+
pagination_offset: int,
170+
pagination_limit: int,
171+
) -> tuple[list[RegisteredFunctionJob], PageMetaInfoLimitOffset]:
172+
# Mimic listing all function jobs
173+
function_jobs_list = list(self._function_jobs.values())[
174+
pagination_offset : pagination_offset + pagination_limit
175+
]
176+
total_count = len(self._function_jobs)
177+
page_meta_info = PageMetaInfoLimitOffset(
178+
total=total_count,
179+
limit=pagination_limit,
180+
offset=pagination_offset,
181+
count=len(function_jobs_list),
182+
)
183+
return function_jobs_list, page_meta_info
184+
185+
async def delete_function_job(
186+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_job_id: str
187+
) -> None:
188+
# Mimic deleting a function job
189+
if function_job_id in self._function_jobs:
190+
del self._function_jobs[function_job_id]
191+
else:
192+
raise FunctionJobIDNotFoundError(function_id=function_job_id)
193+
194+
async def register_function_job_collection(
195+
self,
196+
rabbitmq_rpc_client: RabbitMQRPCClient,
197+
function_job_collection: FunctionJobCollection,
198+
) -> RegisteredFunctionJobCollection:
199+
# Mimic registering a function job collection
200+
uid = uuid4()
201+
self._function_job_collections[uid] = TypeAdapter(
202+
RegisteredFunctionJobCollection
203+
).validate_python(
204+
{
205+
"uid": str(uid),
206+
"title": function_job_collection.title,
207+
"description": function_job_collection.description,
208+
"job_ids": function_job_collection.job_ids,
209+
}
210+
)
211+
return self._function_job_collections[uid]
212+
213+
async def get_function_job_collection(
214+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_job_collection_id: str
215+
) -> RegisteredFunctionJobCollection:
216+
# Mimic retrieval of a function job collection based on collection_id and raise 404 if not found
217+
if function_job_collection_id not in self._function_job_collections:
218+
raise FunctionJobCollectionIDNotFoundError(
219+
function_job_collection_id=function_job_collection_id
220+
)
221+
return self._function_job_collections[function_job_collection_id]
222+
223+
async def list_function_job_collections(
224+
self,
225+
rabbitmq_rpc_client: RabbitMQRPCClient,
226+
pagination_offset: int,
227+
pagination_limit: int,
228+
) -> tuple[list[RegisteredFunctionJobCollection], PageMetaInfoLimitOffset]:
229+
# Mimic listing all function job collections
230+
function_job_collections_list = list(self._function_job_collections.values())[
231+
pagination_offset : pagination_offset + pagination_limit
232+
]
233+
total_count = len(self._function_job_collections)
234+
page_meta_info = PageMetaInfoLimitOffset(
235+
total=total_count,
236+
limit=pagination_limit,
237+
offset=pagination_offset,
238+
count=len(function_job_collections_list),
239+
)
240+
return function_job_collections_list, page_meta_info
241+
242+
async def delete_function_job_collection(
243+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_job_collection_id: str
244+
) -> None:
245+
# Mimic deleting a function job collection
246+
if function_job_collection_id in self._function_job_collections:
247+
del self._function_job_collections[function_job_collection_id]
248+
else:
249+
raise FunctionJobCollectionIDNotFoundError(
250+
function_job_collection_id=function_job_collection_id
251+
)
252+
253+
async def update_function_title(
254+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_id: str, title: str
255+
) -> RegisteredFunction:
256+
# Mimic updating the title of a function
257+
if function_id not in self._functions:
258+
raise FunctionIDNotFoundError(function_id=function_id)
259+
self._functions[function_id].title = title
260+
return self._functions[function_id]
261+
262+
async def update_function_description(
263+
self, rabbitmq_rpc_client: RabbitMQRPCClient, function_id: str, description: str
264+
) -> RegisteredFunction:
265+
# Mimic updating the description of a function
266+
if function_id not in self._functions:
267+
raise FunctionIDNotFoundError(function_id=function_id)
268+
self._functions[function_id].description = description
269+
return self._functions[function_id]
270+
271+
272+
@pytest.fixture()
273+
def backend_function_register() -> MockFunctionRegister:
274+
"""Fixture to mock the backend function register."""
275+
return MockFunctionRegister()
276+
277+
278+
@pytest.fixture()
279+
def mock_function_register(
280+
mock_wb_api_server_rpc: MockerFixture,
281+
backend_function_register: MockFunctionRegister,
282+
) -> None:
283+
mock_wb_api_server_rpc.patch(
284+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.register_function",
285+
backend_function_register.register_function,
286+
)
287+
mock_wb_api_server_rpc.patch(
288+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.get_function",
289+
backend_function_register.get_function,
290+
)
291+
mock_wb_api_server_rpc.patch(
292+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.run_function",
293+
backend_function_register.run_function,
294+
)
295+
mock_wb_api_server_rpc.patch(
296+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.list_functions",
297+
backend_function_register.list_functions,
298+
)
299+
mock_wb_api_server_rpc.patch(
300+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.delete_function",
301+
backend_function_register.delete_function,
302+
)
303+
mock_wb_api_server_rpc.patch(
304+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.register_function_job",
305+
backend_function_register.register_function_job,
306+
)
307+
mock_wb_api_server_rpc.patch(
308+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.get_function_job",
309+
backend_function_register.get_function_job,
310+
)
311+
mock_wb_api_server_rpc.patch(
312+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.list_function_jobs",
313+
backend_function_register.list_function_jobs,
314+
)
315+
mock_wb_api_server_rpc.patch(
316+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.delete_function_job",
317+
backend_function_register.delete_function_job,
318+
)
319+
mock_wb_api_server_rpc.patch(
320+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.register_function_job_collection",
321+
backend_function_register.register_function_job_collection,
322+
)
323+
mock_wb_api_server_rpc.patch(
324+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.get_function_job_collection",
325+
backend_function_register.get_function_job_collection,
326+
)
327+
mock_wb_api_server_rpc.patch(
328+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.list_function_job_collections",
329+
backend_function_register.list_function_job_collections,
330+
)
331+
mock_wb_api_server_rpc.patch(
332+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.delete_function_job_collection",
333+
backend_function_register.delete_function_job_collection,
334+
)
335+
mock_wb_api_server_rpc.patch(
336+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.update_function_title",
337+
backend_function_register.update_function_title,
338+
)
339+
mock_wb_api_server_rpc.patch(
340+
"servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions_rpc_interface.update_function_description",
341+
backend_function_register.update_function_description,
342+
)

0 commit comments

Comments
 (0)