|
14 | 14 | import httpx |
15 | 15 | import pytest |
16 | 16 | import respx |
| 17 | +from celery import Task |
17 | 18 | from faker import Faker |
| 19 | +from fastapi import FastAPI |
18 | 20 | from httpx import AsyncClient |
19 | 21 | from models_library.api_schemas_long_running_tasks.tasks import TaskGet |
20 | 22 | from models_library.functions import ( |
|
25 | 27 | RegisteredFunctionJob, |
26 | 28 | RegisteredFunctionJobCollection, |
27 | 29 | RegisteredProjectFunction, |
| 30 | + RegisteredProjectFunctionJob, |
28 | 31 | ) |
29 | 32 | from models_library.functions_errors import ( |
30 | 33 | FunctionIDNotFoundError, |
31 | 34 | FunctionReadAccessDeniedError, |
32 | 35 | ) |
33 | 36 | from models_library.rest_pagination import PageMetaInfoLimitOffset |
34 | 37 | from models_library.users import UserID |
| 38 | +from pydantic import EmailStr |
35 | 39 | from pytest_mock import MockerFixture, MockType |
36 | 40 | from pytest_simcore.helpers.httpx_calls_capture_models import HttpApiCallCaptureModel |
37 | 41 | from servicelib.aiohttp import status |
| 42 | +from servicelib.celery.app_server import BaseAppServer |
| 43 | +from servicelib.celery.models import TaskID |
38 | 44 | from servicelib.common_headers import ( |
39 | 45 | X_SIMCORE_PARENT_NODE_ID, |
40 | 46 | X_SIMCORE_PARENT_PROJECT_UUID, |
41 | 47 | ) |
| 48 | +from servicelib.rabbitmq._client_rpc import RabbitMQRPCClient |
42 | 49 | from simcore_service_api_server._meta import API_VTAG |
| 50 | +from simcore_service_api_server.api.dependencies.authentication import Identity |
| 51 | +from simcore_service_api_server.celery.worker_tasks import functions_tasks |
| 52 | +from simcore_service_api_server.models.api_resources import JobLinks |
| 53 | +from simcore_service_api_server.services_rpc.wb_api_server import WbApiRpcClient |
43 | 54 |
|
44 | 55 | _faker = Faker() |
45 | 56 |
|
@@ -379,6 +390,103 @@ async def async_magic(): |
379 | 390 | ) |
380 | 391 |
|
381 | 392 |
|
| 393 | +@pytest.mark.parametrize("capture", ["run_study_function_parent_info.json"]) |
| 394 | +async def test_run_project_function( |
| 395 | + mocker: MockerFixture, |
| 396 | + mocked_webserver_rpc_api: dict[str, MockType], |
| 397 | + app: FastAPI, |
| 398 | + client: AsyncClient, |
| 399 | + mock_handler_in_functions_rpc_interface: Callable[[str, Any], None], |
| 400 | + mock_registered_project_function: RegisteredProjectFunction, |
| 401 | + mock_registered_project_function_job: RegisteredFunctionJob, |
| 402 | + auth: httpx.BasicAuth, |
| 403 | + user_identity: Identity, |
| 404 | + user_email: EmailStr, |
| 405 | + job_links: JobLinks, |
| 406 | + mocked_webserver_rest_api_base: respx.MockRouter, |
| 407 | + mocked_directorv2_rest_api_base: respx.MockRouter, |
| 408 | + create_respx_mock_from_capture, |
| 409 | + project_tests_dir: Path, |
| 410 | + capture: str, |
| 411 | +) -> None: |
| 412 | + |
| 413 | + def _get_app_server(celery_app: Any) -> FastAPI: |
| 414 | + app_server = mocker.Mock(spec=BaseAppServer) |
| 415 | + app_server.app = app |
| 416 | + return app_server |
| 417 | + |
| 418 | + mocker.patch.object(functions_tasks, "get_app_server", _get_app_server) |
| 419 | + |
| 420 | + def _get_rabbitmq_rpc_client(app: FastAPI) -> RabbitMQRPCClient: |
| 421 | + return mocker.MagicMock(spec=RabbitMQRPCClient) |
| 422 | + |
| 423 | + mocker.patch.object( |
| 424 | + functions_tasks, "get_rabbitmq_rpc_client", _get_rabbitmq_rpc_client |
| 425 | + ) |
| 426 | + |
| 427 | + async def _get_wb_api_rpc_client(app: FastAPI) -> WbApiRpcClient: |
| 428 | + wb_api_rpc_client = WbApiRpcClient( |
| 429 | + _client=mocker.MagicMock(spec=RabbitMQRPCClient) |
| 430 | + ) |
| 431 | + return wb_api_rpc_client |
| 432 | + |
| 433 | + mocker.patch.object( |
| 434 | + functions_tasks, "get_wb_api_rpc_client", _get_wb_api_rpc_client |
| 435 | + ) |
| 436 | + |
| 437 | + def _default_side_effect( |
| 438 | + request: httpx.Request, |
| 439 | + path_params: dict[str, Any], |
| 440 | + capture: HttpApiCallCaptureModel, |
| 441 | + ) -> Any: |
| 442 | + return capture.response_body |
| 443 | + |
| 444 | + create_respx_mock_from_capture( |
| 445 | + respx_mocks=[mocked_webserver_rest_api_base, mocked_directorv2_rest_api_base], |
| 446 | + capture_path=project_tests_dir / "mocks" / capture, |
| 447 | + side_effects_callbacks=[_default_side_effect] * 50, |
| 448 | + ) |
| 449 | + |
| 450 | + mock_handler_in_functions_rpc_interface( |
| 451 | + "get_function_user_permissions", |
| 452 | + FunctionUserAccessRights( |
| 453 | + user_id=user_identity.user_id, |
| 454 | + execute=True, |
| 455 | + read=True, |
| 456 | + write=True, |
| 457 | + ), |
| 458 | + ) |
| 459 | + mock_handler_in_functions_rpc_interface( |
| 460 | + "get_function", mock_registered_project_function |
| 461 | + ) |
| 462 | + mock_handler_in_functions_rpc_interface("find_cached_function_jobs", []) |
| 463 | + mock_handler_in_functions_rpc_interface( |
| 464 | + "register_function_job", mock_registered_project_function_job |
| 465 | + ) |
| 466 | + mock_handler_in_functions_rpc_interface( |
| 467 | + "get_functions_user_api_access_rights", |
| 468 | + FunctionUserApiAccessRights( |
| 469 | + user_id=user_identity.user_id, |
| 470 | + execute_functions=True, |
| 471 | + write_functions=True, |
| 472 | + read_functions=True, |
| 473 | + ), |
| 474 | + ) |
| 475 | + |
| 476 | + job = await functions_tasks.run_function( |
| 477 | + task=MagicMock(spec=Task), |
| 478 | + task_id=TaskID(_faker.uuid4()), |
| 479 | + user_identity=user_identity, |
| 480 | + function=mock_registered_project_function, |
| 481 | + function_inputs={}, |
| 482 | + pricing_spec=None, |
| 483 | + job_links=job_links, |
| 484 | + x_simcore_parent_project_uuid=None, |
| 485 | + x_simcore_parent_node_id=None, |
| 486 | + ) |
| 487 | + assert isinstance(job, RegisteredProjectFunctionJob) |
| 488 | + |
| 489 | + |
382 | 490 | @pytest.mark.parametrize( |
383 | 491 | "parent_project_uuid, parent_node_uuid, expected_status_code", |
384 | 492 | [ |
|
0 commit comments