Skip to content

Commit f986597

Browse files
committed
Don't require HandlerError cause and fix test assertions
Respond to upstream: Unknown{Operation,Service}Error are just HandlerError
1 parent 1466b5e commit f986597

File tree

2 files changed

+40
-40
lines changed

2 files changed

+40
-40
lines changed

temporalio/worker/_nexus.py

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,7 @@ async def _handle_start_operation_task(
223223
handler_err = _exception_to_handler_error(err)
224224
completion = temporalio.bridge.proto.nexus.NexusTaskCompletion(
225225
task_token=task_token,
226-
error=temporalio.api.nexus.v1.HandlerError(
227-
error_type=handler_err.type.value,
228-
failure=await self._exception_to_failure_proto(
229-
handler_err.__cause__
230-
),
231-
retry_behavior=(
232-
temporalio.api.enums.v1.NexusHandlerErrorRetryBehavior.NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_RETRYABLE
233-
if handler_err.retryable
234-
else temporalio.api.enums.v1.NexusHandlerErrorRetryBehavior.NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_NON_RETRYABLE
235-
),
236-
),
226+
error=await self._handler_error_to_proto(handler_err),
237227
)
238228
else:
239229
completion = temporalio.bridge.proto.nexus.NexusTaskCompletion(
@@ -319,18 +309,6 @@ async def _start_operation(
319309
"or nexusrpc.handler.StartOperationResultAsync"
320310
)
321311
)
322-
323-
except (
324-
nexusrpc.handler.UnknownServiceError,
325-
nexusrpc.handler.UnknownOperationError,
326-
) as err:
327-
# TODO(nexus-prerelease): error message
328-
raise nexusrpc.handler.HandlerError(
329-
"No matching operation handler",
330-
type=nexusrpc.handler.HandlerErrorType.NOT_FOUND,
331-
cause=err,
332-
retryable=False,
333-
) from err
334312
except nexusrpc.handler.OperationError as err:
335313
return temporalio.api.nexus.v1.StartOperationResponse(
336314
operation_error=await self._operation_error_to_proto(err),

tests/nexus/test_handler.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ class UnsuccessfulResponse:
365365
# Expected value of Nexus-Request-Retryable header
366366
retryable_header: Optional[bool]
367367
failure_message: Union[str, Callable[[str], bool]]
368+
# Is the Nexus Failure expected to have the details field populated?
369+
failure_details: bool = True
368370
# Expected value of inverse of non_retryable attribute of exception.
369371
retryable_exception: bool = True
370372
# TODO(nexus-prerelease): the body of a successful response need not be JSON; test non-JSON-parseable string
@@ -429,13 +431,25 @@ def check_response(
429431
else:
430432
assert cls.expected.retryable_header is None
431433

432-
if failure.exception_from_details:
434+
if cls.expected.failure_details:
435+
assert (
436+
failure.exception_from_details is not None
437+
), "Expected exception details, but found none."
433438
assert isinstance(failure.exception_from_details, ApplicationError)
434-
assert failure.exception_from_details.non_retryable == (
439+
440+
exception_from_failure_details = failure.exception_from_details
441+
if (
442+
exception_from_failure_details.type == "HandlerError"
443+
and exception_from_failure_details.__cause__
444+
):
445+
exception_from_failure_details = (
446+
exception_from_failure_details.__cause__
447+
)
448+
assert isinstance(exception_from_failure_details, ApplicationError)
449+
450+
assert exception_from_failure_details.non_retryable == (
435451
not cls.expected.retryable_exception
436452
)
437-
else:
438-
print(f"TODO(dan): {cls} did not yield a Failure with exception details")
439453

440454

441455
class SyncHandlerHappyPath(_TestCase):
@@ -573,6 +587,7 @@ class UpstreamTimeoutViaRequestTimeout(_FailureTestCase):
573587
retryable_header=None,
574588
# This error is returned by the server; it doesn't populate metadata or details, and it
575589
# doesn't set temporal-nexus-failure-source.
590+
failure_details=False,
576591
failure_message="upstream timeout",
577592
headers={
578593
"content-type": "application/json",
@@ -597,33 +612,40 @@ class BadRequest(_FailureTestCase):
597612
expected = UnsuccessfulResponse(
598613
status_code=400,
599614
retryable_header=False,
600-
failure_message=lambda s: s.startswith("Failed converting field"),
615+
failure_message=lambda s: s.startswith(
616+
"Data converter failed to decode Nexus operation input"
617+
),
601618
)
602619

603620

604-
class NonRetryableApplicationError(_FailureTestCase):
605-
operation = "non_retryable_application_error"
606-
expected = UnsuccessfulResponse(
607-
status_code=500,
608-
retryable_header=False,
609-
retryable_exception=False,
610-
failure_message="non-retryable application error",
611-
)
621+
class _ApplicationErrorTestCase(_FailureTestCase):
622+
"""Test cases in which the operation raises an ApplicationError."""
612623

613624
@classmethod
614625
def check_response(
615626
cls, response: httpx.Response, with_service_definition: bool
616627
) -> None:
617628
super().check_response(response, with_service_definition)
618629
failure = Failure(**response.json())
619-
err = failure.exception_from_details
630+
assert failure.exception_from_details
631+
assert isinstance(failure.exception_from_details, ApplicationError)
632+
err = failure.exception_from_details.__cause__
620633
assert isinstance(err, ApplicationError)
621-
assert err.non_retryable
622634
assert err.type == "TestFailureType"
623635
assert err.details == ("details arg",)
624636

625637

626-
class RetryableApplicationError(_FailureTestCase):
638+
class NonRetryableApplicationError(_ApplicationErrorTestCase):
639+
operation = "non_retryable_application_error"
640+
expected = UnsuccessfulResponse(
641+
status_code=500,
642+
retryable_header=False,
643+
retryable_exception=False,
644+
failure_message="non-retryable application error",
645+
)
646+
647+
648+
class RetryableApplicationError(_ApplicationErrorTestCase):
627649
operation = "retryable_application_error"
628650
expected = UnsuccessfulResponse(
629651
status_code=500,
@@ -638,7 +660,7 @@ class HandlerErrorInternal(_FailureTestCase):
638660
status_code=500,
639661
# TODO(nexus-prerelease): check this assertion
640662
retryable_header=False,
641-
failure_message="cause message",
663+
failure_message="deliberate internal handler error",
642664
)
643665

644666

0 commit comments

Comments
 (0)