Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

class CapturedParameterSchema(BaseModel):
title: str | None = None
type_: Literal["str", "int", "float", "bool"] | None = Field(None, alias="type")
type_: Literal["str", "int", "float", "bool", "null"] | None = Field(
None, alias="type"
)
pattern: str | None = None
format_: Literal["uuid"] | None = Field(None, alias="format")
exclusiveMinimum: bool | None = None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[
{
"name": "GET /services/simcore%2Fservices%2Fdynamic%2Fmy_program/1.0.0",
"description": "<Request('GET', 'http://catalog:8005/v0/services/simcore%2Fservices%2Fdynamic%2Fmy_program/1.0.0?user_id=1')>",
"method": "GET",
"host": "catalog",
"path": {
"path": "/v0/services/{service_key}/{service_version}",
"path_parameters": [
{
"in": "path",
"name": "service_key",
"required": true,
"schema": {
"title": "Service Key",
"type": "str",
"pattern": "^simcore/services/((comp|dynamic|frontend))/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$"
},
"response_value": "services"
},
{
"in": "path",
"name": "service_version",
"required": true,
"schema": {
"title": "Service Version",
"type": "str",
"pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$"
},
"response_value": "simcore/services/dynamic/my_program"
}
]
},
"query": "user_id=1",
"response_body": {
"error": {
"errors": [
"Not Found"
]
}
},
"status_code": 404
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[
{
"name": "GET /services/simcore%2Fservices%2Fdynamic%2Felectrode-selector/2.1.3",
"description": "<Request('GET', 'http://catalog:8005/v0/services/simcore%2Fservices%2Fdynamic%2Felectrode-selector/2.1.3?user_id=1')>",
"method": "GET",
"host": "catalog",
"path": {
"path": "/v0/services/{service_key}/{service_version}",
"path_parameters": [
{
"in": "path",
"name": "service_version",
"required": true,
"schema": {
"title": "Service Version",
"type": "str",
"pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$"
},
"response_value": "simcore/services/dynamic/electrode-selector"
},
{
"in": "path",
"name": "service_key",
"required": true,
"schema": {
"title": "Service Key",
"type": "str",
"pattern": "^simcore/services/((comp|dynamic|frontend))/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$"
},
"response_value": "services"
}
]
},
"query": "user_id=1",
"response_body": {
"name": "electrode-selector",
"thumbnail": null,
"icon": null,
"description": "GUI for selecting/visualizing electrode groups and target tissue",
"description_ui": false,
"version_display": null,
"deprecated": null,
"classifiers": [],
"quality": {},
"key": "simcore/services/dynamic/electrode-selector",
"version": "2.1.3",
"integration-version": "1.0.0",
"type": "dynamic",
"authors": [
{
"name": "Odei Maiz",
"email": "[email protected]",
"affiliation": "ITIS Foundation"
}
],
"contact": "[email protected]",
"inputs": {
"input_1": {
"displayOrder": 1.0,
"label": "Sim info",
"description": "Information on which simulations are done",
"type": "data:application/json"
},
"input_2": {
"displayOrder": 2.0,
"label": "Targets list",
"description": "List of available targets for the model",
"type": "data:application/json"
}
},
"outputs": {
"output_1": {
"displayOrder": 1.0,
"label": "Output",
"description": "Electrode groups and Target tissue",
"type": "data:application/json"
}
},
"image_digest": "sha256:37f2603d079cfedcc77cce6c0846eba6668c52a4da15d81ef4e9a742c081b224",
"owner": null
}
}
]
92 changes: 92 additions & 0 deletions services/api-server/tests/mocks/list_programs_success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[
{
"name": "GET /services",
"description": "<Request('GET', 'http://catalog:8005/v0/services?user_id=1&details=true')>",
"method": "GET",
"host": "catalog",
"path": {
"path": "/v0/services",
"path_parameters": []
},
"query": "user_id=1&details=true",
"response_body": [
{
"name": "File Picker",
"thumbnail": null,
"icon": null,
"description": "File Picker",
"description_ui": false,
"version_display": null,
"deprecated": null,
"classifiers": [],
"quality": {},
"accessRights": {
"1": {
"execute_access": true,
"write_access": false
}
},
"key": "simcore/services/frontend/file-picker",
"version": "1.0.0",
"integration-version": "1.0.0",
"type": "frontend",
"authors": [
{
"name": "Unknown",
"email": "[email protected]",
"affiliation": "unknown"
}
],
"contact": "[email protected]",
"inputs": {},
"outputs": {
"outFile": {
"displayOrder": 0.0,
"label": "File",
"description": "Chosen File",
"type": "data:*/*"
}
},
"owner": null
},
{
"name": "Number parameter",
"thumbnail": "https://fakeimg.pl/100x100/ff0000%2C128/000%2C255/?text=number",
"icon": null,
"description": "Produces a number value at its outputs",
"description_ui": false,
"version_display": null,
"deprecated": null,
"classifiers": [],
"quality": {},
"accessRights": {
"1": {
"execute_access": true,
"write_access": false
}
},
"key": "simcore/services/frontend/parameter/number",
"version": "1.0.0",
"integration-version": "1.0.0",
"type": "frontend",
"authors": [
{
"name": "Unknown",
"email": "[email protected]",
"affiliation": "unknown"
}
],
"contact": "[email protected]",
"inputs": {},
"outputs": {
"out_1": {
"label": "number_source",
"description": "Input number value",
"type": "number"
}
},
"owner": null
}
]
}
]
2 changes: 1 addition & 1 deletion services/api-server/tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ def _patch(href):
return data.status_href, data.result_href

return mocker.patch(
"simcore_service_api_server.services.webserver._get_lrt_urls",
"simcore_service_api_server.services_http.webserver._get_lrt_urls",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIP: to avoid these it is better if you import the module and then patch the object. It is more resistant to refactoring. Also add auto_spec=True to enhance validity of your mock

import simcore_service_api_server.services_http.webserver
mocker.patch.object(simcore_service_api_server.services_http.webserver,._get_lrt_urls", auto_spec=True, side_effect=_get_lrt_urls)

side_effect=_get_lrt_urls,
)

Expand Down
109 changes: 109 additions & 0 deletions services/api-server/tests/unit/test_api_programs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from pathlib import Path

import httpx
import pytest
from fastapi import status
from httpx import AsyncClient
from pytest_simcore.helpers.httpx_calls_capture_models import CreateRespxMockCallback
from simcore_service_api_server._meta import API_VTAG


@pytest.mark.parametrize(
"capture,expected_status_code",
[
("create_program_job_invalid_program.json", status.HTTP_404_NOT_FOUND),
("create_program_job_success.json", status.HTTP_201_CREATED),
],
)
async def test_create_program_job(
client: AsyncClient,
mocked_webserver_rest_api_base,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotate all fixtures

create_respx_mock_from_capture: CreateRespxMockCallback,
auth: httpx.BasicAuth,
project_tests_dir: Path,
capture: str,
expected_status_code: int,
):
respx_mock = create_respx_mock_from_capture(
respx_mocks=[mocked_webserver_rest_api_base],
capture_path=project_tests_dir / "mocks" / capture,
side_effects_callbacks=[],
)
assert respx_mock

program_key = "simcore/services/comp/my_program"
version = "1.0.0"

response = await client.post(
f"{API_VTAG}/programs/{program_key}/releases/{version}/jobs",
auth=auth,
)
assert response.status_code == expected_status_code
if response.status_code == status.HTTP_201_CREATED:
assert "job_id" in response.json()


@pytest.mark.parametrize(
"capture,expected_status_code",
[
("list_programs_success.json", status.HTTP_200_OK),
],
)
async def test_list_programs(
client: AsyncClient,
mocked_catalog_rest_api_base,
create_respx_mock_from_capture: CreateRespxMockCallback,
auth: httpx.BasicAuth,
project_tests_dir: Path,
capture: str,
expected_status_code: int,
):
respx_mock = create_respx_mock_from_capture(
respx_mocks=[mocked_catalog_rest_api_base],
capture_path=project_tests_dir / "mocks" / capture,
side_effects_callbacks=[],
)
assert respx_mock

response = await client.get(f"{API_VTAG}/programs", auth=auth)
assert response.status_code == expected_status_code
if response.status_code == status.HTTP_200_OK:
programs = response.json()
assert isinstance(programs, list)


@pytest.mark.parametrize(
"capture,expected_status_code",
[
("get_program_release_success.json", status.HTTP_200_OK),
("get_program_release_not_found.json", status.HTTP_404_NOT_FOUND),
],
)
async def test_get_program_release(
client: AsyncClient,
mocked_catalog_rest_api_base,
create_respx_mock_from_capture: CreateRespxMockCallback,
auth: httpx.BasicAuth,
project_tests_dir: Path,
capture: str,
expected_status_code: int,
):
respx_mock = create_respx_mock_from_capture(
respx_mocks=[mocked_catalog_rest_api_base],
capture_path=project_tests_dir / "mocks" / capture,
side_effects_callbacks=[],
)
assert respx_mock

program_key = "simcore/services/dynamic/electrode-selector"
version = "2.1.3"

response = await client.get(
f"{API_VTAG}/programs/{program_key}/releases/{version}", auth=auth
)
assert response.status_code == expected_status_code
if response.status_code == status.HTTP_200_OK:
program_release = response.json()
assert "id" in program_release
assert program_release["id"] == program_key
assert program_release["version"] == version
Loading