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+
18import 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
224from pytest_simcore .helpers .monkeypatch_envs import setenvs_from_dict
325from 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