"""api.utils.errors coverage.""" from __future__ import annotations from http import HTTPStatus import pytest from fastapi import HTTPException from roboco.api.utils.errors import ( conflict, forbidden, handle_service_error, not_found, service_error_handler, service_unavailable, unauthorized, validation_error, ) from roboco.services.base import ( ConflictError, NotFoundError, ServiceError, ServiceUnavailableError, UnauthorizedError, ValidationError, ) # --------------------------------------------------------------------------- # Factory functions # --------------------------------------------------------------------------- def test_not_found_with_id() -> None: e = not_found("Task", "abc-123") assert e.status_code == HTTPStatus.NOT_FOUND assert "abc-123" in e.detail def test_not_found_without_id() -> None: e = not_found("Task") assert e.status_code == HTTPStatus.NOT_FOUND assert e.detail == "Task not found" def test_forbidden_basic() -> None: e = forbidden("edit task") assert e.status_code == HTTPStatus.FORBIDDEN assert "edit task" in e.detail def test_forbidden_with_reason() -> None: e = forbidden("edit", reason="not owner") assert e.status_code == HTTPStatus.FORBIDDEN assert "not owner" in e.detail def test_unauthorized_default() -> None: e = unauthorized() assert e.status_code == HTTPStatus.UNAUTHORIZED def test_unauthorized_custom() -> None: e = unauthorized("Missing token") assert e.detail == "Missing token" def test_validation_error_basic() -> None: e = validation_error("bad input") assert e.status_code == HTTPStatus.BAD_REQUEST def test_validation_error_with_field() -> None: e = validation_error("required", field="title") assert "title" in e.detail def test_conflict_basic() -> None: e = conflict("duplicate") assert e.status_code == HTTPStatus.CONFLICT def test_conflict_with_resource() -> None: e = conflict("duplicate", resource_type="Channel") assert "Channel" in e.detail def test_service_unavailable_basic() -> None: e = service_unavailable("Orchestrator") assert e.status_code == HTTPStatus.SERVICE_UNAVAILABLE def test_service_unavailable_with_reason() -> None: e = service_unavailable("Orchestrator", reason="not init") assert "not init" in e.detail # --------------------------------------------------------------------------- # handle_service_error translation # --------------------------------------------------------------------------- def test_handle_not_found() -> None: e = handle_service_error(NotFoundError(resource_type="Task", resource_id="abc")) assert e.status_code == HTTPStatus.NOT_FOUND def test_handle_validation_error() -> None: e = handle_service_error(ValidationError("bad", field="x")) assert e.status_code == HTTPStatus.BAD_REQUEST def test_handle_conflict() -> None: e = handle_service_error(ConflictError("dup", resource_type="Channel")) assert e.status_code == HTTPStatus.CONFLICT def test_handle_unauthorized() -> None: e = handle_service_error(UnauthorizedError(action="edit", reason="x")) assert e.status_code == HTTPStatus.FORBIDDEN def test_handle_service_unavailable() -> None: e = handle_service_error(ServiceUnavailableError(service_name="X", reason="r")) assert e.status_code == HTTPStatus.SERVICE_UNAVAILABLE def test_handle_generic_service_error() -> None: e = handle_service_error(ServiceError("oops")) assert e.status_code == HTTPStatus.INTERNAL_SERVER_ERROR # --------------------------------------------------------------------------- # Decorator # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_service_error_handler_translates() -> None: @service_error_handler async def my_route() -> str: raise NotFoundError(resource_type="X", resource_id="1") with pytest.raises(HTTPException) as exc: await my_route() assert exc.value.status_code == HTTPStatus.NOT_FOUND @pytest.mark.asyncio async def test_service_error_handler_passes_through_value() -> None: @service_error_handler async def my_route() -> str: return "ok" result = await my_route() assert result == "ok" @pytest.mark.asyncio async def test_service_error_handler_does_not_catch_other_exceptions() -> None: @service_error_handler async def my_route() -> str: raise ValueError("not a service error") with pytest.raises(ValueError): await my_route()