Skip to content

Commit 5824f2b

Browse files
committed
finish task tests
1 parent ca2c013 commit 5824f2b

File tree

2 files changed

+84
-49
lines changed

2 files changed

+84
-49
lines changed

services/api-server/src/simcore_service_api_server/api/routes/tasks.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Annotated, Any
33

44
from celery.exceptions import CeleryError # type: ignore[import-untyped]
5+
from common_library.error_codes import create_error_code
56
from fastapi import APIRouter, Depends, FastAPI, HTTPException, status
67
from models_library.api_schemas_long_running_tasks.base import TaskProgress
78
from models_library.api_schemas_long_running_tasks.tasks import (
@@ -15,8 +16,9 @@
1516
)
1617
from models_library.products import ProductName
1718
from models_library.users import UserID
18-
from servicelib.celery.models import TaskFilter, TaskUUID
19+
from servicelib.celery.models import TaskFilter, TaskState, TaskUUID
1920
from servicelib.fastapi.dependencies import get_app
21+
from servicelib.logging_errors import create_troubleshootting_log_kwargs
2022

2123
from ...models.schemas.base import ApiServerEnvelope
2224
from ...models.schemas.errors import ErrorGet
@@ -199,7 +201,6 @@ async def get_task_result(
199201
task_filter = _get_task_filter(user_id, product_name)
200202

201203
try:
202-
# First check if task exists and is done
203204
task_status = await task_manager.get_task_status(
204205
task_filter=task_filter,
205206
task_uuid=TaskUUID(f"{task_id}"),
@@ -210,15 +211,37 @@ async def get_task_result(
210211
status_code=status.HTTP_404_NOT_FOUND,
211212
detail="Task result not available yet",
212213
)
214+
if task_status.task_state == TaskState.ABORTED:
215+
raise HTTPException(
216+
status_code=status.HTTP_409_CONFLICT,
217+
detail="Task was cancelled",
218+
)
213219

214-
result = await task_manager.get_task_result(
220+
task_result = await task_manager.get_task_result(
215221
task_filter=task_filter,
216222
task_uuid=TaskUUID(f"{task_id}"),
217223
)
218-
return TaskResult(result=result, error=None)
219-
220224
except CeleryError as exc:
221225
raise HTTPException(
222226
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
223227
detail="Encountered issue when getting task result",
224228
) from exc
229+
230+
if task_status.task_state == TaskState.FAILURE:
231+
assert isinstance(task_result, Exception)
232+
user_error_msg = f"The execution of task {task_id} failed"
233+
support_id = create_error_code(task_result)
234+
_logger.exception(
235+
**create_troubleshootting_log_kwargs(
236+
user_error_msg,
237+
error=task_result,
238+
error_code=support_id,
239+
tip="Unexpected error in Celery",
240+
)
241+
)
242+
raise HTTPException(
243+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
244+
detail=user_error_msg,
245+
)
246+
247+
return TaskResult(result=task_result, error=None)

services/api-server/tests/unit/celery/test_tasks.py

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
from faker import Faker
1010
from fastapi import status
1111
from httpx import AsyncClient, BasicAuth
12-
from models_library.api_schemas_long_running_tasks.tasks import TaskGet, TaskStatus
12+
from models_library.api_schemas_long_running_tasks.tasks import TaskGet
13+
from models_library.progress_bar import ProgressReport, ProgressStructuredMessage
1314
from pytest_mock import MockerFixture, MockType, mocker
15+
from servicelib.celery.models import TaskState, TaskStatus, TaskUUID
1416
from simcore_service_api_server.api.routes import tasks as task_routes
1517
from simcore_service_api_server.models.schemas.base import ApiServerEnvelope
1618

@@ -89,14 +91,15 @@ async def test_get_result(
8991

9092

9193
@pytest.mark.parametrize(
92-
"method, url, list_tasks_return_value, get_task_status_return_value, cancel_task_return_value, expected_status_code",
94+
"method, url, list_tasks_return_value, get_task_status_return_value, cancel_task_return_value, get_task_result_return_value, expected_status_code",
9395
[
9496
(
9597
"GET",
9698
"/v0/tasks",
9799
CeleryError(),
98100
None,
99101
None,
102+
None,
100103
status.HTTP_500_INTERNAL_SERVER_ERROR,
101104
),
102105
(
@@ -105,6 +108,7 @@ async def test_get_result(
105108
None,
106109
CeleryError(),
107110
None,
111+
None,
108112
status.HTTP_500_INTERNAL_SERVER_ERROR,
109113
),
110114
(
@@ -113,8 +117,58 @@ async def test_get_result(
113117
None,
114118
None,
115119
CeleryError(),
120+
None,
116121
status.HTTP_500_INTERNAL_SERVER_ERROR,
117122
),
123+
(
124+
"GET",
125+
f"/v0/tasks/{_faker.uuid4()}/result",
126+
None,
127+
CeleryError(),
128+
None,
129+
None,
130+
status.HTTP_500_INTERNAL_SERVER_ERROR,
131+
),
132+
(
133+
"GET",
134+
f"/v0/tasks/{_faker.uuid4()}/result",
135+
None,
136+
TaskStatus(
137+
task_uuid=TaskUUID("123e4567-e89b-12d3-a456-426614174000"),
138+
task_state=TaskState.STARTED,
139+
progress_report=ProgressReport(
140+
actual_value=0.5,
141+
total=1.0,
142+
unit="Byte",
143+
message=ProgressStructuredMessage.model_config["json_schema_extra"][
144+
"examples"
145+
][0],
146+
),
147+
),
148+
None,
149+
None,
150+
status.HTTP_404_NOT_FOUND,
151+
),
152+
(
153+
"GET",
154+
f"/v0/tasks/{_faker.uuid4()}/result",
155+
None,
156+
TaskStatus(
157+
task_uuid=TaskUUID("123e4567-e89b-12d3-a456-426614174000"),
158+
task_state=TaskState.ABORTED,
159+
progress_report=ProgressReport(
160+
actual_value=0.5,
161+
total=1.0,
162+
unit="Byte",
163+
message=ProgressStructuredMessage.model_config["json_schema_extra"][
164+
"examples"
165+
][0],
166+
),
167+
),
168+
None,
169+
None,
170+
status.HTTP_409_CONFLICT,
171+
),
118172
],
119173
)
120174
async def test_celery_error_propagation(
@@ -127,45 +181,3 @@ async def test_celery_error_propagation(
127181
):
128182
response = await client.request(method=method, url=url, auth=auth)
129183
assert response.status_code == expected_status_code
130-
131-
132-
# @pytest.mark.parametrize(
133-
# "async_job_error, expected_status_code",
134-
# [
135-
# (None, status.HTTP_200_OK),
136-
# (
137-
# JobError(
138-
# job_id=_faker.uuid4(),
139-
# exc_type=Exception,
140-
# exc_message="An exception from inside the async job",
141-
# ),
142-
# status.HTTP_500_INTERNAL_SERVER_ERROR,
143-
# ),
144-
# (
145-
# JobNotDoneError(job_id=_faker.uuid4()),
146-
# status.HTTP_404_NOT_FOUND,
147-
# ),
148-
# (
149-
# JobAbortedError(job_id=_faker.uuid4()),
150-
# status.HTTP_409_CONFLICT,
151-
# ),
152-
# (
153-
# JobSchedulerError(
154-
# exc=Exception("A very rare exception raised by the scheduler")
155-
# ),
156-
# status.HTTP_500_INTERNAL_SERVER_ERROR,
157-
# ),
158-
# ],
159-
# )
160-
# async def test_get_async_job_result(
161-
# client: AsyncClient,
162-
# mocked_async_jobs_rpc_api: dict[str, MockType],
163-
# async_job_error: Exception | None,
164-
# auth: BasicAuth,
165-
# expected_status_code: int,
166-
# ):
167-
# task_id = f"{_faker.uuid4()}"
168-
# response = await client.get(f"/v0/tasks/{task_id}/result", auth=auth)
169-
# assert response.status_code == expected_status_code
170-
# assert mocked_async_jobs_rpc_api["result"].called
171-
# assert f"{mocked_async_jobs_rpc_api['result'].call_args[1]['job_id']}" == task_id

0 commit comments

Comments
 (0)