diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index e9299c6d..c96f370d 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -73,8 +73,16 @@ async def start_new(self, if response[0] <= 202 and response[1]: return response[1]["id"] + elif response[0] == 400: + # Orchestrator not found, report clean exception + exception_data = response[1] + exception_message = exception_data["ExceptionMessage"] + raise Exception(exception_message) else: - return None + # Catch all: simply surfacing the durable-extension exception + # we surface the stack trace too, since this may be a more involed exception + exception_message = response[1] + raise Exception(exception_message) def create_check_status_response(self, request, instance_id): """Create a HttpResponse that contains useful information for \ diff --git a/tests/models/test_DurableOrchestrationClient.py b/tests/models/test_DurableOrchestrationClient.py index 4585c286..49fc9ff3 100644 --- a/tests/models/test_DurableOrchestrationClient.py +++ b/tests/models/test_DurableOrchestrationClient.py @@ -19,6 +19,13 @@ MESSAGE_500 = 'instance failed with unhandled exception' MESSAGE_501 = "well we didn't expect that" +TEST_ORCHESTRATOR = "MyDurableOrchestrator" +EXCEPTION_ORCHESTRATOR_NOT_FOUND_EXMESSAGE = "The function doesn't exist,"\ + " is disabled, or is not an orchestrator function. Additional info: "\ + "the following are the known orchestrator functions: " +EXCEPTION_ORCHESTRATOR_NOT_FOUND_MESSAGE = "One or more of the arguments submitted is incorrect" +EXCEPTION_TYPE_ORCHESTRATOR_NOT_FOUND = "System.ArgumentException" +STACK_TRACE = "' at Microsoft.Azure.WebJobs.Extensions.DurableTask..." class MockRequest: def __init__(self, expected_url: str, response: [int, any]): @@ -497,3 +504,39 @@ async def test_wait_or_response_check_status_response(binding_string): with pytest.raises(Exception): await client.wait_for_completion_or_create_check_status_response( None, TEST_INSTANCE_ID, timeout_in_milliseconds=500) + +@pytest.mark.asyncio +async def test_start_new_orchestrator_not_found(binding_string): + """Test that we throw the right exception when the orchestrator is not found. + """ + status = dict(ExceptionMessage=EXCEPTION_ORCHESTRATOR_NOT_FOUND_EXMESSAGE, + StackTrace=STACK_TRACE, + Message=EXCEPTION_ORCHESTRATOR_NOT_FOUND_MESSAGE, + ExceptionType=EXCEPTION_TYPE_ORCHESTRATOR_NOT_FOUND) + mock_request = MockRequest(expected_url=f"{RPC_BASE_URL}orchestrators/{TEST_ORCHESTRATOR}", + response=[400, status]) + client = DurableOrchestrationClient(binding_string) + client._post_async_request = mock_request.post + + with pytest.raises(Exception) as ex: + await client.start_new(TEST_ORCHESTRATOR) + ex.match(EXCEPTION_ORCHESTRATOR_NOT_FOUND_EXMESSAGE) + + +@pytest.mark.asyncio +async def test_start_new_orchestrator_internal_exception(binding_string): + """Test that we throw the right exception when the extension fails internally. + """ + status = dict(ExceptionMessage=EXCEPTION_ORCHESTRATOR_NOT_FOUND_EXMESSAGE, + StackTrace=STACK_TRACE, + Message=EXCEPTION_ORCHESTRATOR_NOT_FOUND_MESSAGE, + ExceptionType=EXCEPTION_TYPE_ORCHESTRATOR_NOT_FOUND) + mock_request = MockRequest(expected_url=f"{RPC_BASE_URL}orchestrators/{TEST_ORCHESTRATOR}", + response=[500, status]) + client = DurableOrchestrationClient(binding_string) + client._post_async_request = mock_request.post + + status_str = str(status) + with pytest.raises(Exception) as ex: + await client.start_new(TEST_ORCHESTRATOR) + ex.match(status_str)