Skip to content

Commit b0951cb

Browse files
Cleanup test fixtures (#238)
1 parent 07fd466 commit b0951cb

2 files changed

Lines changed: 158 additions & 97 deletions

File tree

clients/python/test/e2e/test_solvers_api.py

Lines changed: 89 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,57 @@
77
import json
88

99
import osparc
10+
import tenacity
1011
from _utils import skip_if_osparc_version
1112
from httpx import AsyncClient
1213
from packaging.version import Version
1314
from uuid import UUID
14-
15-
DEFAULT_TIMEOUT_SECONDS = 15 * 60 # 10 min
15+
import pytest
16+
from contextlib import contextmanager
17+
from typing import Callable, Iterator, Set
18+
from tenacity import Retrying
19+
20+
DEFAULT_TIMEOUT_SECONDS = 15 * 60 # 15 min
21+
22+
23+
@pytest.fixture
24+
def create_sleeper_jobs(
25+
api_client: osparc.ApiClient,
26+
sleeper: osparc.Solver,
27+
) -> Callable[[int], Iterator[Set[UUID | str]]]:
28+
@contextmanager
29+
def sleeper_jobs(n_jobs: int = 1) -> Iterator[Set[UUID | str]]:
30+
job_ids = set()
31+
solvers_api = osparc.SolversApi(api_client=api_client)
32+
try:
33+
for _ in range(n_jobs):
34+
job = solvers_api.create_job(
35+
sleeper.id, sleeper.version, osparc.JobInputs({"input1": 1.0})
36+
)
37+
assert isinstance(job, osparc.Job)
38+
print(job.to_str())
39+
job_ids.add(job.id)
40+
yield job_ids
41+
finally:
42+
for job_id in job_ids:
43+
for attempt in Retrying(
44+
reraise=True,
45+
wait=tenacity.wait_fixed(2),
46+
stop=tenacity.stop_after_delay(60),
47+
):
48+
with attempt:
49+
solvers_api.stop_job(sleeper.id, sleeper.version, job_id)
50+
solvers_api.delete_job(sleeper.id, sleeper.version, job_id)
51+
52+
return sleeper_jobs
1653

1754

1855
@skip_if_osparc_version(at_least=Version("0.8.3.post0.dev20"))
19-
def test_jobs(api_client: osparc.ApiClient, sleeper: osparc.Solver):
56+
def test_jobs(
57+
api_client: osparc.ApiClient,
58+
create_sleeper_jobs: Callable[[int], Iterator[Set[UUID | str]]],
59+
sleeper: osparc.Solver,
60+
):
2061
"""Test the jobs method
2162
2263
Args:
@@ -30,68 +71,56 @@ def test_jobs(api_client: osparc.ApiClient, sleeper: osparc.Solver):
3071
assert n_init_iter >= 0
3172

3273
# create n_jobs jobs
33-
created_job_ids = []
34-
for _ in range(n_jobs):
35-
job: osparc.Job = solvers_api.create_job(
36-
sleeper.id, sleeper.version, osparc.JobInputs({"input1": 1.0})
37-
)
38-
created_job_ids.append(job.id)
39-
40-
tmp_iter = solvers_api.iter_jobs(sleeper.id, sleeper.version)
41-
solvers_api.iter_jobs(sleeper.id, sleeper.version)
42-
43-
final_iter = solvers_api.iter_jobs(sleeper.id, sleeper.version)
44-
assert len(final_iter) > 0, "No jobs were available"
45-
assert n_init_iter + n_jobs == len(
46-
final_iter
47-
), "An incorrect number of jobs was recorded"
48-
49-
for ii, elm in enumerate(tmp_iter):
50-
assert isinstance(elm, osparc.Job)
51-
if ii > 100:
52-
break
74+
with create_sleeper_jobs(n_jobs):
75+
tmp_iter = solvers_api.iter_jobs(sleeper.id, sleeper.version)
76+
solvers_api.iter_jobs(sleeper.id, sleeper.version)
77+
final_iter = solvers_api.iter_jobs(sleeper.id, sleeper.version)
78+
assert len(final_iter) > 0, "No jobs were available"
79+
assert n_init_iter + n_jobs == len(
80+
final_iter
81+
), "An incorrect number of jobs was recorded"
5382

54-
# cleanup
55-
for elm in created_job_ids:
56-
solvers_api.delete_job(sleeper.id, sleeper.version, elm)
83+
for ii, elm in enumerate(tmp_iter):
84+
assert isinstance(elm, osparc.Job)
85+
if ii > 100:
86+
break
5787

5888

5989
@skip_if_osparc_version(at_least=Version("0.6.5"))
6090
async def test_logstreaming(
61-
api_client: osparc.ApiClient, sleeper: osparc.Solver, async_client: AsyncClient
91+
api_client: osparc.ApiClient,
92+
sleeper: osparc.Solver,
93+
create_sleeper_jobs: Callable[[int], Iterator[Set[UUID]]],
94+
async_client: AsyncClient,
6295
):
6396
"""Test log streaming"""
6497
solvers_api: osparc.SolversApi = osparc.SolversApi(api_client)
65-
job = solvers_api.create_job(
66-
sleeper.id, sleeper.version, osparc.JobInputs({"input1": 1.0})
67-
)
68-
assert isinstance(job, osparc.Job)
69-
print(job.to_str())
70-
71-
solvers_api.start_job(sleeper.id, sleeper.version, job.id)
72-
73-
nloglines: int = 0
74-
url = f"/v0/solvers/{sleeper.id}/releases/{sleeper.version}/jobs/{job.id}/logstream"
75-
print(f"starting logstreaming from {url}...")
76-
77-
async with async_client.stream(
78-
"GET",
79-
url,
80-
timeout=DEFAULT_TIMEOUT_SECONDS,
81-
) as response:
82-
print(response.headers)
83-
async for line in response.aiter_lines():
84-
log = json.loads(line)
85-
job_id = log.get("job_id")
86-
assert job_id
87-
assert job_id == (
88-
f"{job.id}" if isinstance(job.id, UUID) else job.id
89-
) # keep test backwards compatible
90-
nloglines += 1
91-
print("\n".join(log.get("messages")))
92-
await response.aclose()
93-
break
94-
95-
assert nloglines > 0, f"Could not stream log for {sleeper.id=}, \
96-
{sleeper.version=} and {job.id=}" # type: ignore
97-
solvers_api.delete_job(sleeper.id, sleeper.version, job.id)
98+
with create_sleeper_jobs() as jobs:
99+
job_id = next(iter(jobs))
100+
101+
solvers_api.start_job(sleeper.id, sleeper.version, job_id)
102+
103+
nloglines: int = 0
104+
url = f"/v0/solvers/{sleeper.id}/releases/{sleeper.version}/jobs/{job_id}/logstream"
105+
print(f"starting logstreaming from {url}...")
106+
107+
async with async_client.stream(
108+
"GET",
109+
url,
110+
timeout=DEFAULT_TIMEOUT_SECONDS,
111+
) as response:
112+
print(response.headers)
113+
async for line in response.aiter_lines():
114+
log = json.loads(line)
115+
log_job_id = log.get("job_id")
116+
assert log_job_id
117+
assert log_job_id == (
118+
f"{job_id}" if isinstance(job_id, UUID) else job_id
119+
) # keep test backwards compatible
120+
nloglines += 1
121+
print("\n".join(log.get("messages")))
122+
await response.aclose()
123+
break
124+
125+
assert nloglines > 0, f"Could not stream log for {sleeper.id=}, \
126+
{sleeper.version=} and {job_id=}" # type: ignore

clients/python/test/e2e/test_studies.py

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
# pylint: disable=unused-argument
55
# pylint: disable=unused-variable
66

7+
from contextlib import contextmanager
78
import shutil
89
from datetime import timedelta
910
from pathlib import Path
11+
from typing import Iterator
1012
from uuid import UUID
1113

1214
import osparc
@@ -18,50 +20,80 @@
1820
_WAIT_TIMEOUT = timedelta(minutes=15)
1921

2022

23+
@pytest.fixture
24+
def create_study_job(
25+
api_client: osparc.ApiClient, file_with_number: osparc.File, sleeper_study_id: UUID
26+
):
27+
studies_api = osparc.StudiesApi(api_client=api_client)
28+
29+
@contextmanager
30+
def _create_study_job() -> Iterator[osparc.Job]:
31+
job_inputs = osparc.JobInputs(
32+
values={
33+
"input_file": file_with_number,
34+
}
35+
)
36+
job = studies_api.create_study_job(
37+
study_id=f"{sleeper_study_id}", job_inputs=job_inputs
38+
)
39+
assert isinstance(job, osparc.Job)
40+
print(job)
41+
try:
42+
yield job
43+
finally:
44+
for attempt in tenacity.Retrying(
45+
reraise=True,
46+
wait=tenacity.wait_fixed(2),
47+
stop=tenacity.stop_after_delay(60),
48+
):
49+
with attempt:
50+
studies_api.stop_study_job(
51+
study_id=f"{sleeper_study_id}", job_id=job.id
52+
)
53+
studies_api.delete_study_job(
54+
study_id=f"{sleeper_study_id}", job_id=job.id
55+
)
56+
57+
return _create_study_job
58+
59+
2160
@skip_if_osparc_version(at_least=Version("0.6.6.post7"))
2261
@pytest.mark.parametrize("download_dir", [True, False])
2362
async def test_studies_logs(
2463
api_client: osparc.ApiClient,
25-
file_with_number: osparc.File,
2664
sleeper_study_id: UUID,
65+
create_study_job: Iterator[osparc.Job],
2766
download_dir: bool,
2867
tmp_path: Path,
2968
):
3069
studies_api = osparc.StudiesApi(api_client=api_client)
31-
job_inputs = osparc.JobInputs(
32-
values={
33-
"input_file": file_with_number,
34-
}
35-
)
36-
job = studies_api.create_study_job(
37-
study_id=f"{sleeper_study_id}", job_inputs=job_inputs
38-
)
39-
assert isinstance(job, osparc.Job)
40-
print(job)
41-
status = studies_api.start_study_job(study_id=f"{sleeper_study_id}", job_id=job.id)
42-
assert isinstance(status, osparc.JobStatus)
43-
async for attempt in tenacity.AsyncRetrying(
44-
reraise=True,
45-
wait=tenacity.wait_fixed(timedelta(seconds=5)),
46-
stop=tenacity.stop_after_delay(_WAIT_TIMEOUT),
47-
retry=tenacity.retry_if_exception_type(AssertionError),
48-
):
49-
with attempt:
50-
status = studies_api.inspect_study_job(
51-
study_id=f"{sleeper_study_id}", job_id=job.id
52-
)
53-
assert isinstance(status, osparc.JobStatus)
54-
print(f"--- seconds idle: {attempt.retry_state.idle_for}\n", status)
55-
assert status.stopped_at is not None
56-
assert status.state == "SUCCESS"
57-
try:
58-
log_dir = await studies_api.get_study_job_output_logfiles_async(
59-
study_id=f"{sleeper_study_id}",
60-
job_id=job.id,
61-
download_dir=tmp_path if download_dir else None,
70+
with create_study_job() as job:
71+
status = studies_api.start_study_job(
72+
study_id=f"{sleeper_study_id}", job_id=job.id
6273
)
63-
assert log_dir.is_dir()
64-
n_logfiles = sum(1 for _ in log_dir.rglob("*") if _.is_file())
65-
assert n_logfiles > 0
66-
finally:
67-
shutil.rmtree(log_dir)
74+
assert isinstance(status, osparc.JobStatus)
75+
async for attempt in tenacity.AsyncRetrying(
76+
reraise=True,
77+
wait=tenacity.wait_fixed(timedelta(seconds=5)),
78+
stop=tenacity.stop_after_delay(_WAIT_TIMEOUT),
79+
retry=tenacity.retry_if_exception_type(AssertionError),
80+
):
81+
with attempt:
82+
status = studies_api.inspect_study_job(
83+
study_id=f"{sleeper_study_id}", job_id=job.id
84+
)
85+
assert isinstance(status, osparc.JobStatus)
86+
print(f"--- seconds idle: {attempt.retry_state.idle_for}\n", status)
87+
assert status.stopped_at is not None
88+
assert status.state == "SUCCESS"
89+
try:
90+
log_dir = await studies_api.get_study_job_output_logfiles_async(
91+
study_id=f"{sleeper_study_id}",
92+
job_id=job.id,
93+
download_dir=tmp_path if download_dir else None,
94+
)
95+
assert log_dir.is_dir()
96+
n_logfiles = sum(1 for _ in log_dir.rglob("*") if _.is_file())
97+
assert n_logfiles > 0
98+
finally:
99+
shutil.rmtree(log_dir)

0 commit comments

Comments
 (0)