Skip to content

Commit b2a2599

Browse files
committed
Test error conversion
1 parent 5df2903 commit b2a2599

File tree

1 file changed

+191
-24
lines changed

1 file changed

+191
-24
lines changed

tests/nexus/test_workflow_caller.py

Lines changed: 191 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
WorkflowHandle,
3939
)
4040
from temporalio.common import WorkflowIDConflictPolicy
41-
from temporalio.exceptions import CancelledError, NexusOperationError
41+
from temporalio.exceptions import ApplicationError, CancelledError, NexusOperationError
4242
from temporalio.nexus import WorkflowRunOperationContext, workflow_run_operation
4343
from temporalio.service import RPCError, RPCStatusCode
4444
from temporalio.worker import Worker
@@ -1087,8 +1087,134 @@ async def assert_handler_workflow_has_link_to_caller_workflow(
10871087

10881088
# Handler
10891089

1090+
# @OperationImpl
1091+
# public OperationHandler<NexusService.ErrorTestInput, NexusService.ErrorTestOutput> testError() {
1092+
# return OperationHandler.sync(
1093+
# (ctx, details, input) -> {
1094+
# switch (input.getAction()) {
1095+
# case RAISE_APPLICATION_ERROR:
1096+
# throw ApplicationFailure.newNonRetryableFailure(
1097+
# "application error 1", "APPLICATION_ERROR");
1098+
# case RAISE_CUSTOM_ERROR:
1099+
# throw new MyCustomException("Custom error 1");
1100+
# case RAISE_CUSTOM_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1101+
# // ** THIS DOESN'T WORK **: CHAINED CUSTOM EXCEPTIONS DON'T SERIALIZE
1102+
# MyCustomException customError = new MyCustomException("Custom error 1");
1103+
# customError.initCause(new MyCustomException("Custom error 2"));
1104+
# throw customError;
1105+
# case RAISE_APPLICATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1106+
# throw ApplicationFailure.newNonRetryableFailureWithCause(
1107+
# "application error 1",
1108+
# "APPLICATION_ERROR",
1109+
# new MyCustomException("Custom error 2"));
1110+
# case RAISE_NEXUS_HANDLER_ERROR:
1111+
# throw new HandlerException(HandlerException.ErrorType.NOT_FOUND, "Handler error 1");
1112+
# case RAISE_NEXUS_HANDLER_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1113+
# // ** THIS DOESN'T WORK **
1114+
# // Can't overwrite cause with
1115+
# // io.temporal.samples.nexus.handler.NexusServiceImpl$MyCustomException: Custom error
1116+
# // 2
1117+
# HandlerException handlerErr =
1118+
# new HandlerException(HandlerException.ErrorType.NOT_FOUND, "Handler error 1");
1119+
# handlerErr.initCause(new MyCustomException("Custom error 2"));
1120+
# throw handlerErr;
1121+
# case RAISE_NEXUS_OPERATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1122+
# throw OperationException.failure(
1123+
# ApplicationFailure.newNonRetryableFailureWithCause(
1124+
# "application error 1",
1125+
# "APPLICATION_ERROR",
1126+
# new MyCustomException("Custom error 2")));
1127+
# }
1128+
# return new NexusService.ErrorTestOutput("Unreachable");
1129+
# });
1130+
# }
1131+
1132+
# 🌈 RAISE_APPLICATION_ERROR:
1133+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1134+
# io.nexusrpc.handler.HandlerException(type=no-type-attr, message=handler error: message='application error 1', type='APPLICATION_ERROR', nonRetryable=true)
1135+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='application error 1', type='APPLICATION_ERROR', nonRetryable=true)
1136+
1137+
1138+
# 🌈 RAISE_CUSTOM_ERROR:
1139+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1140+
# io.nexusrpc.handler.HandlerException(type=no-type-attr, message=handler error: message='Custom error wrapped: custom error 1', type='CUSTOM_ERROR', nonRetryable=true)
1141+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='Custom error wrapped: custom error 1', type='CUSTOM_ERROR', nonRetryable=true)
1142+
1143+
1144+
# 🌈 RAISE_APPLICATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1145+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1146+
# io.nexusrpc.handler.HandlerException(type=no-type-attr, message=handler error: message='application error 1', type='APPLICATION_ERROR', nonRetryable=true)
1147+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='application error 1', type='APPLICATION_ERROR', nonRetryable=true)
1148+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='Custom error 2', type='io.temporal.samples.nexus.handler.NexusServiceImpl$MyCustomException', nonRetryable=false)
1149+
1150+
1151+
# 🌈 RAISE_NEXUS_HANDLER_ERROR:
1152+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1153+
# io.nexusrpc.handler.HandlerException(type=no-type-attr, message=handler error: message='Handler error 1', type='java.lang.RuntimeException', nonRetryable=false)
1154+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='Handler error 1', type='java.lang.RuntimeException', nonRetryable=false)
1155+
1156+
1157+
# 🌈 RAISE_NEXUS_HANDLER_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1158+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1159+
# io.temporal.failure.TimeoutFailure(type=no-type-attr, message=message='operation timed out', timeoutType=TIMEOUT_TYPE_SCHEDULE_TO_CLOSE)
1160+
1161+
1162+
# 🌈 RAISE_NEXUS_OPERATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1163+
# io.temporal.failure.NexusOperationFailure(type=no-type-attr, message=Nexus Operation with operation='testErrorservice='NexusService' endpoint='my-nexus-endpoint-name' failed: 'nexus operation completed unsuccessfully'. scheduledEventId=5, operationToken=)
1164+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='application error 1', type='APPLICATION_ERROR', nonRetryable=true)
1165+
# io.temporal.failure.ApplicationFailure(type=no-type-attr, message=message='Custom error 2', type='io.temporal.samples.nexus.handler.NexusServiceImpl$MyCustomException', nonRetryable=false)
1166+
1167+
# @OperationImpl
1168+
# public OperationHandler<NexusService.ErrorTestInput, NexusService.ErrorTestOutput> testError() {
1169+
# return OperationHandler.sync(
1170+
# (ctx, details, input) -> {
1171+
# switch (input.getAction()) {
1172+
# case RAISE_APPLICATION_ERROR:
1173+
# throw ApplicationFailure.newNonRetryableFailure(
1174+
# "application error 1", "APPLICATION_ERROR");
1175+
# case RAISE_CUSTOM_ERROR:
1176+
# throw new MyCustomException("Custom error 1");
1177+
# case RAISE_CUSTOM_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1178+
# // ** THIS DOESN'T WORK **: CHAINED CUSTOM EXCEPTIONS DON'T SERIALIZE
1179+
# MyCustomException customError = new MyCustomException("Custom error 1");
1180+
# customError.initCause(new MyCustomException("Custom error 2"));
1181+
# throw customError;
1182+
# case RAISE_APPLICATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1183+
# throw ApplicationFailure.newNonRetryableFailureWithCause(
1184+
# "application error 1",
1185+
# "APPLICATION_ERROR",
1186+
# new MyCustomException("Custom error 2"));
1187+
# case RAISE_NEXUS_HANDLER_ERROR:
1188+
# throw new HandlerException(HandlerException.ErrorType.NOT_FOUND, "Handler error 1");
1189+
# case RAISE_NEXUS_HANDLER_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1190+
# // ** THIS DOESN'T WORK **
1191+
# // Can't overwrite cause with
1192+
# // io.temporal.samples.nexus.handler.NexusServiceImpl$MyCustomException: Custom error
1193+
# // 2
1194+
# HandlerException handlerErr =
1195+
# new HandlerException(HandlerException.ErrorType.NOT_FOUND, "Handler error 1");
1196+
# handlerErr.initCause(new MyCustomException("Custom error 2"));
1197+
# throw handlerErr;
1198+
# case RAISE_NEXUS_OPERATION_ERROR_WITH_CAUSE_OF_CUSTOM_ERROR:
1199+
# throw OperationException.failure(
1200+
# ApplicationFailure.newNonRetryableFailureWithCause(
1201+
# "application error 1",
1202+
# "APPLICATION_ERROR",
1203+
# new MyCustomException("Custom error 2")));
1204+
# }
1205+
# return new NexusService.ErrorTestOutput("Unreachable");
1206+
# });
1207+
# }
1208+
1209+
10901210
ActionInSyncOp = Literal[
1091-
"raise_handler_error", "raise_operation_error", "raise_custom_error"
1211+
"application_error_non_retryable",
1212+
"custom_error",
1213+
"custom_error_from_custom_error",
1214+
"application_error_non_retryable_from_custom_error",
1215+
"nexus_handler_error_not_found",
1216+
"nexus_handler_error_not_found_from_custom_error",
1217+
"nexus_operation_error_from_application_error_non_retryable_from_custom_error",
10921218
]
10931219

10941220

@@ -1106,17 +1232,46 @@ class ErrorTestInput:
11061232
class ErrorTestService:
11071233
@sync_operation
11081234
async def op(self, ctx: StartOperationContext, input: ErrorTestInput) -> None:
1109-
if input.action_in_sync_op == "raise_handler_error":
1235+
if input.action_in_sync_op == "application_error_non_retryable":
1236+
raise ApplicationError("application error in nexus op", non_retryable=True)
1237+
elif input.action_in_sync_op == "custom_error":
1238+
raise CustomError("custom error in nexus op")
1239+
elif input.action_in_sync_op == "custom_error_from_custom_error":
1240+
raise CustomError("custom error 1 in nexus op") from CustomError(
1241+
"custom error 2 in nexus op"
1242+
)
1243+
elif (
1244+
input.action_in_sync_op
1245+
== "application_error_non_retryable_from_custom_error"
1246+
):
1247+
raise ApplicationError(
1248+
"application error in nexus op", non_retryable=True
1249+
) from CustomError("custom error in nexus op")
1250+
elif input.action_in_sync_op == "nexus_handler_error_not_found":
11101251
raise nexusrpc.HandlerError(
11111252
"test",
1112-
type=nexusrpc.HandlerErrorType.INTERNAL,
1253+
type=nexusrpc.HandlerErrorType.NOT_FOUND,
11131254
)
1114-
elif input.action_in_sync_op == "raise_operation_error":
1115-
raise nexusrpc.OperationError(
1116-
"test", state=nexusrpc.OperationErrorState.FAILED
1117-
)
1118-
elif input.action_in_sync_op == "raise_custom_error":
1119-
raise CustomError("test")
1255+
elif (
1256+
input.action_in_sync_op == "nexus_handler_error_not_found_from_custom_error"
1257+
):
1258+
raise nexusrpc.HandlerError(
1259+
"test",
1260+
type=nexusrpc.HandlerErrorType.NOT_FOUND,
1261+
) from CustomError("custom error in nexus op")
1262+
elif (
1263+
input.action_in_sync_op
1264+
== "nexus_operation_error_from_application_error_non_retryable_from_custom_error"
1265+
):
1266+
try:
1267+
raise ApplicationError(
1268+
"application error in nexus op", non_retryable=True
1269+
) from CustomError("custom error in nexus op")
1270+
except ApplicationError as err:
1271+
raise nexusrpc.OperationError(
1272+
"operation error in nexus op",
1273+
state=nexusrpc.OperationErrorState.FAILED,
1274+
) from err
11201275
else:
11211276
raise NotImplementedError(
11221277
f"Unhandled action_in_sync_op: {input.action_in_sync_op}"
@@ -1146,14 +1301,26 @@ async def run(self, input: ErrorTestInput) -> list[str]:
11461301
# None
11471302
input,
11481303
)
1149-
except Exception as err:
1150-
return [str(type(err).__name__), str(type(err.__cause__).__name__)]
1304+
except BaseException as err:
1305+
errs = [err]
1306+
while err.__cause__:
1307+
errs.append(err.__cause__)
1308+
err = err.__cause__
1309+
return [type(err).__name__ for err in errs]
11511310
assert False, "Unreachable"
11521311

11531312

11541313
@pytest.mark.parametrize(
11551314
"action_in_sync_op",
1156-
["raise_handler_error", "raise_operation_error", "raise_custom_error"],
1315+
[
1316+
"application_error_non_retryable",
1317+
"custom_error",
1318+
"custom_error_from_custom_error",
1319+
"application_error_non_retryable_from_custom_error",
1320+
"nexus_handler_error_not_found",
1321+
"nexus_handler_error_not_found_from_custom_error",
1322+
"nexus_operation_error_from_application_error_non_retryable_from_custom_error",
1323+
],
11571324
)
11581325
async def test_errors_raised_by_nexus_operation(
11591326
client: Client, action_in_sync_op: ActionInSyncOp
@@ -1178,14 +1345,14 @@ async def test_errors_raised_by_nexus_operation(
11781345

11791346
print(f"\n\n\n{action_in_sync_op}: \n", result, "\n\n\n")
11801347

1181-
if action_in_sync_op == "raise_handler_error":
1182-
assert result == ["NexusOperationError", "HandlerError"]
1183-
elif action_in_sync_op == "raise_operation_error":
1184-
assert result == ["NexusOperationError", "ApplicationError"]
1185-
elif action_in_sync_op == "raise_custom_error":
1186-
# assert result == ["NexusOperationError", "CustomError"]
1187-
pass
1188-
else:
1189-
raise NotImplementedError(
1190-
f"Unhandled action_in_sync_op: {action_in_sync_op}"
1191-
)
1348+
# if action_in_sync_op == "handler_error":
1349+
# assert result == ["NexusOperationError", "HandlerError"]
1350+
# elif action_in_sync_op == "operation_error":
1351+
# assert result == ["NexusOperationError", "ApplicationError"]
1352+
# elif action_in_sync_op == "custom_error":
1353+
# # assert result == ["NexusOperationError", "CustomError"]
1354+
# pass
1355+
# else:
1356+
# raise NotImplementedError(
1357+
# f"Unhandled action_in_sync_op: {action_in_sync_op}"
1358+
# )

0 commit comments

Comments
 (0)