Skip to content

Commit 7ec18d9

Browse files
committed
test nih api
1 parent b1f21d9 commit 7ec18d9

File tree

1 file changed

+399
-0
lines changed

1 file changed

+399
-0
lines changed
Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
# pylint: disable=protected-access
2+
# pylint: disable=redefined-outer-name
3+
# pylint: disable=too-many-arguments
4+
# pylint: disable=unused-argument
5+
# pylint: disable=unused-variable
6+
7+
from collections.abc import AsyncIterator
8+
from contextlib import AsyncExitStack
9+
10+
import pytest
11+
from aiohttp.test_utils import TestClient, TestServer
12+
from common_library.json_serialization import json_dumps
13+
from common_library.serialization import model_dump_with_secrets
14+
from pydantic import TypeAdapter
15+
from pytest_simcore.helpers.assert_checks import assert_status
16+
from pytest_simcore.helpers.faker_factories import (
17+
random_service_access_rights,
18+
random_service_consume_filetype,
19+
random_service_meta_data,
20+
)
21+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
22+
from pytest_simcore.helpers.postgres_tools import insert_and_get_row_lifespan
23+
from pytest_simcore.helpers.typing_env import EnvVarsDict
24+
from servicelib.aiohttp import status
25+
from settings_library.rabbit import RabbitSettings
26+
from settings_library.redis import RedisSettings
27+
from simcore_postgres_database.models.services import (
28+
services_access_rights,
29+
services_meta_data,
30+
)
31+
from simcore_postgres_database.models.services_consume_filetypes import (
32+
services_consume_filetypes,
33+
)
34+
from simcore_service_webserver.studies_dispatcher._controller.rest.nih_schemas import (
35+
ServiceGet,
36+
)
37+
from sqlalchemy.ext.asyncio import AsyncEngine
38+
from yarl import URL
39+
40+
pytest_simcore_core_services_selection = [
41+
"rabbit",
42+
]
43+
44+
45+
@pytest.fixture
46+
async def services_metadata_in_db(
47+
asyncpg_engine: AsyncEngine,
48+
) -> AsyncIterator[list[dict]]:
49+
"""Pre-populate services metadata table with test data maintaining original structure."""
50+
services_data = [
51+
random_service_meta_data(
52+
key="simcore/services/dynamic/raw-graphs",
53+
version="2.11.1",
54+
name="2D plot",
55+
description="2D plots powered by RAW Graphs",
56+
thumbnail=None,
57+
),
58+
random_service_meta_data(
59+
key="simcore/services/dynamic/bio-formats-web",
60+
version="1.0.1",
61+
name="bio-formats",
62+
description="Bio-Formats image viewer",
63+
thumbnail="https://www.openmicroscopy.org/img/logos/bio-formats.svg",
64+
),
65+
random_service_meta_data(
66+
key="simcore/services/dynamic/jupyter-octave-python-math",
67+
version="1.6.9",
68+
name="JupyterLab Math",
69+
description="JupyterLab Math with octave and python",
70+
thumbnail=None,
71+
),
72+
random_service_meta_data(
73+
key="simcore/services/dynamic/s4l-ui-modeling",
74+
version="3.2.300",
75+
name="Hornet Flow",
76+
description="Hornet Flow UI for Sim4Life",
77+
thumbnail=None,
78+
),
79+
]
80+
81+
async with AsyncExitStack() as stack:
82+
created_services = []
83+
for service_data in services_data:
84+
row = await stack.enter_async_context(
85+
insert_and_get_row_lifespan(
86+
asyncpg_engine,
87+
table=services_meta_data,
88+
values=service_data,
89+
pk_cols=[services_meta_data.c.key, services_meta_data.c.version],
90+
)
91+
)
92+
created_services.append(row)
93+
94+
yield created_services
95+
96+
97+
@pytest.fixture
98+
async def services_consume_filetypes_in_db(
99+
asyncpg_engine: AsyncEngine, services_metadata_in_db: list[dict]
100+
) -> AsyncIterator[list[dict]]:
101+
"""Pre-populate services consume filetypes table with test data."""
102+
filetypes_data = [
103+
random_service_consume_filetype(
104+
service_key="simcore/services/dynamic/bio-formats-web",
105+
service_version="1.0.1",
106+
service_display_name="bio-formats",
107+
service_input_port="input_1",
108+
filetype="PNG",
109+
preference_order=0,
110+
is_guest_allowed=True,
111+
),
112+
random_service_consume_filetype(
113+
service_key="simcore/services/dynamic/raw-graphs",
114+
service_version="2.11.1",
115+
service_display_name="RAWGraphs",
116+
service_input_port="input_1",
117+
filetype="CSV",
118+
preference_order=0,
119+
is_guest_allowed=True,
120+
),
121+
random_service_consume_filetype(
122+
service_key="simcore/services/dynamic/bio-formats-web",
123+
service_version="1.0.1",
124+
service_display_name="bio-formats",
125+
service_input_port="input_1",
126+
filetype="JPEG",
127+
preference_order=0,
128+
is_guest_allowed=True,
129+
),
130+
random_service_consume_filetype(
131+
service_key="simcore/services/dynamic/raw-graphs",
132+
service_version="2.11.1",
133+
service_display_name="RAWGraphs",
134+
service_input_port="input_1",
135+
filetype="TSV",
136+
preference_order=0,
137+
is_guest_allowed=True,
138+
),
139+
random_service_consume_filetype(
140+
service_key="simcore/services/dynamic/raw-graphs",
141+
service_version="2.11.1",
142+
service_display_name="RAWGraphs",
143+
service_input_port="input_1",
144+
filetype="XLSX",
145+
preference_order=0,
146+
is_guest_allowed=True,
147+
),
148+
random_service_consume_filetype(
149+
service_key="simcore/services/dynamic/raw-graphs",
150+
service_version="2.11.1",
151+
service_display_name="RAWGraphs",
152+
service_input_port="input_1",
153+
filetype="JSON",
154+
preference_order=0,
155+
is_guest_allowed=True,
156+
),
157+
random_service_consume_filetype(
158+
service_key="simcore/services/dynamic/jupyter-octave-python-math",
159+
service_version="1.6.9",
160+
service_display_name="JupyterLab Math",
161+
service_input_port="input_1",
162+
filetype="PY",
163+
preference_order=0,
164+
is_guest_allowed=False,
165+
),
166+
random_service_consume_filetype(
167+
service_key="simcore/services/dynamic/jupyter-octave-python-math",
168+
service_version="1.6.9",
169+
service_display_name="JupyterLab Math",
170+
service_input_port="input_1",
171+
filetype="IPYNB",
172+
preference_order=0,
173+
is_guest_allowed=False,
174+
),
175+
random_service_consume_filetype(
176+
service_key="simcore/services/dynamic/s4l-ui-modeling",
177+
service_version="3.2.300",
178+
service_display_name="Hornet Flow",
179+
service_input_port="input_1",
180+
filetype="HORNET_REPO",
181+
preference_order=0,
182+
is_guest_allowed=False,
183+
),
184+
]
185+
186+
async with AsyncExitStack() as stack:
187+
created_filetypes = []
188+
for filetype_data in filetypes_data:
189+
row = await stack.enter_async_context(
190+
insert_and_get_row_lifespan(
191+
asyncpg_engine,
192+
table=services_consume_filetypes,
193+
values=filetype_data,
194+
pk_cols=[
195+
services_consume_filetypes.c.service_key,
196+
services_consume_filetypes.c.service_version,
197+
services_consume_filetypes.c.filetype,
198+
],
199+
)
200+
)
201+
created_filetypes.append(row)
202+
203+
yield created_filetypes
204+
205+
206+
@pytest.fixture
207+
async def services_access_rights_in_db(
208+
asyncpg_engine: AsyncEngine, services_metadata_in_db: list[dict]
209+
) -> AsyncIterator[list[dict]]:
210+
"""Pre-populate services access rights table with test data."""
211+
access_rights_data = [
212+
random_service_access_rights(
213+
key="simcore/services/dynamic/raw-graphs",
214+
version="2.11.1",
215+
gid=1, # everyone group
216+
execute_access=True,
217+
write_access=False,
218+
product_name="osparc",
219+
),
220+
random_service_access_rights(
221+
key="simcore/services/dynamic/jupyter-octave-python-math",
222+
version="1.6.9",
223+
gid=1, # everyone group
224+
execute_access=True,
225+
write_access=False,
226+
product_name="osparc",
227+
),
228+
random_service_access_rights(
229+
key="simcore/services/dynamic/s4l-ui-modeling",
230+
version="3.2.300",
231+
gid=1, # everyone group
232+
execute_access=True,
233+
write_access=False,
234+
product_name="osparc",
235+
),
236+
]
237+
238+
async with AsyncExitStack() as stack:
239+
created_access_rights = []
240+
for access_data in access_rights_data:
241+
row = await stack.enter_async_context(
242+
insert_and_get_row_lifespan(
243+
asyncpg_engine,
244+
table=services_access_rights,
245+
values=access_data,
246+
pk_cols=[
247+
services_access_rights.c.key,
248+
services_access_rights.c.version,
249+
services_access_rights.c.gid,
250+
services_access_rights.c.product_name,
251+
],
252+
)
253+
)
254+
created_access_rights.append(row)
255+
256+
yield created_access_rights
257+
258+
259+
@pytest.fixture
260+
def app_environment(
261+
app_environment: EnvVarsDict,
262+
monkeypatch: pytest.MonkeyPatch,
263+
rabbit_service: RabbitSettings,
264+
) -> EnvVarsDict:
265+
return setenvs_from_dict(
266+
monkeypatch,
267+
{
268+
"WEBSERVER_RABBITMQ": json_dumps(
269+
model_dump_with_secrets(rabbit_service, show_secrets=True)
270+
)
271+
},
272+
)
273+
274+
275+
@pytest.fixture
276+
def web_server(
277+
redis_service: RedisSettings,
278+
rabbit_service: RabbitSettings,
279+
web_server: TestServer,
280+
# Add dependencies to ensure database is populated before app starts
281+
services_metadata_in_db: list[dict],
282+
services_consume_filetypes_in_db: list[dict],
283+
services_access_rights_in_db: list[dict],
284+
) -> TestServer:
285+
#
286+
# Extends web_server to start redis_service and ensure DB is populated
287+
#
288+
print(
289+
"Redis service started with settings: ", redis_service.model_dump_json(indent=1)
290+
)
291+
return web_server
292+
293+
294+
def _get_base_url(client: TestClient) -> str:
295+
s = client.server
296+
assert isinstance(s.scheme, str)
297+
url = URL.build(scheme=s.scheme, host=s.host, port=s.port)
298+
return f"{url}"
299+
300+
301+
async def test_api_get_viewer_for_file(client: TestClient):
302+
resp = await client.get("/v0/viewers/default?file_type=JPEG")
303+
data, _ = await assert_status(resp, status.HTTP_200_OK)
304+
305+
base_url = _get_base_url(client)
306+
assert data == [
307+
{
308+
"file_type": "JPEG",
309+
"title": "Bio-formats v1.0.1",
310+
"view_url": f"{base_url}/view?file_type=JPEG&viewer_key=simcore/services/dynamic/bio-formats-web&viewer_version=1.0.1",
311+
},
312+
]
313+
314+
315+
async def test_api_get_viewer_for_unsupported_type(client: TestClient):
316+
resp = await client.get("/v0/viewers/default?file_type=UNSUPPORTED_TYPE")
317+
data, error = await assert_status(resp, status.HTTP_200_OK)
318+
assert data == []
319+
assert error is None
320+
321+
322+
async def test_api_list_supported_filetypes(client: TestClient):
323+
resp = await client.get("/v0/viewers/default")
324+
data, _ = await assert_status(resp, status.HTTP_200_OK)
325+
326+
base_url = _get_base_url(client)
327+
assert data == [
328+
{
329+
"title": "Rawgraphs v2.11.1",
330+
"file_type": "CSV",
331+
"view_url": f"{base_url}/view?file_type=CSV&viewer_key=simcore/services/dynamic/raw-graphs&viewer_version=2.11.1",
332+
},
333+
{
334+
"file_type": "HORNET_REPO",
335+
"title": "Hornet flow v3.2.300",
336+
"view_url": f"{base_url}/view?file_type=HORNET_REPO&viewer_key=simcore/services/dynamic/s4l-ui-modeling&viewer_version=3.2.300",
337+
},
338+
{
339+
"title": "Jupyterlab math v1.6.9",
340+
"file_type": "IPYNB",
341+
"view_url": f"{base_url}/view?file_type=IPYNB&viewer_key=simcore/services/dynamic/jupyter-octave-python-math&viewer_version=1.6.9",
342+
},
343+
{
344+
"title": "Bio-formats v1.0.1",
345+
"file_type": "JPEG",
346+
"view_url": f"{base_url}/view?file_type=JPEG&viewer_key=simcore/services/dynamic/bio-formats-web&viewer_version=1.0.1",
347+
},
348+
{
349+
"title": "Rawgraphs v2.11.1",
350+
"file_type": "JSON",
351+
"view_url": f"{base_url}/view?file_type=JSON&viewer_key=simcore/services/dynamic/raw-graphs&viewer_version=2.11.1",
352+
},
353+
{
354+
"title": "Bio-formats v1.0.1",
355+
"file_type": "PNG",
356+
"view_url": f"{base_url}/view?file_type=PNG&viewer_key=simcore/services/dynamic/bio-formats-web&viewer_version=1.0.1",
357+
},
358+
{
359+
"title": "Jupyterlab math v1.6.9",
360+
"file_type": "PY",
361+
"view_url": f"{base_url}/view?file_type=PY&viewer_key=simcore/services/dynamic/jupyter-octave-python-math&viewer_version=1.6.9",
362+
},
363+
{
364+
"title": "Rawgraphs v2.11.1",
365+
"file_type": "TSV",
366+
"view_url": f"{base_url}/view?file_type=TSV&viewer_key=simcore/services/dynamic/raw-graphs&viewer_version=2.11.1",
367+
},
368+
{
369+
"title": "Rawgraphs v2.11.1",
370+
"file_type": "XLSX",
371+
"view_url": f"{base_url}/view?file_type=XLSX&viewer_key=simcore/services/dynamic/raw-graphs&viewer_version=2.11.1",
372+
},
373+
]
374+
375+
376+
async def test_api_list_services(client: TestClient):
377+
assert client.app
378+
379+
url = client.app.router["list_latest_services"].url_for()
380+
response = await client.get(f"{url}")
381+
382+
data, error = await assert_status(response, status.HTTP_200_OK)
383+
384+
services = TypeAdapter(list[ServiceGet]).validate_python(data)
385+
assert services
386+
387+
# latest versions of services with everyone + ospar-product (see services_access_rights_in_db)
388+
assert services[0].key == "simcore/services/dynamic/raw-graphs"
389+
assert services[0].file_extensions == ["CSV", "JSON", "TSV", "XLSX"]
390+
391+
assert services[0].view_url.query
392+
assert "2.11.1" in services[0].view_url.query
393+
394+
assert services[2].key == "simcore/services/dynamic/jupyter-octave-python-math"
395+
assert services[2].file_extensions == ["IPYNB", "PY"]
396+
assert services[2].view_url.query
397+
assert "1.6.9" in services[2].view_url.query
398+
399+
assert error is None

0 commit comments

Comments
 (0)