bug: mocked Throw responses lose the mocked Error and Cause for most service integrations #45
anikolaiev
started this conversation in
Bugs
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
bug: mocked
Throwresponses lose the mocked Error and Cause for most service integrationsIs there an existing report for this?
Searched the Bugs discussions and the archived issue tracker. The only related item is
localstack/localstack#11712, the feature request that added mock config support. This is a
separate defect in how mocked
Throwresponses are evaluated.Current Behavior
With
SFN_MOCK_CONFIG, a mocked response of the form{"Throw": {"Error": "ApiGateway.422", "Cause": "Unable to create a school"}}does not fail the task with
ApiGateway.422. It fails withApiGateway.FailureEventExceptionand an empty cause:
Because the error name is not the one that was mocked,
CatchandRetryblocks never match.Any state machine whose error handling depends on the mocked error name is untestable — the
execution fails with an error nobody wrote, and the
Catchtarget is never reached.Mocked
Returnresponses are unaffected; this is specific toThrow.Expected Behavior
The task fails with the mocked error name and cause, so
CatchandRetrymatch as they doagainst real AWS.
How are you starting LocalStack?
docker run/docker compose, withSFN_MOCK_CONFIGpointing at a mounted mock config file.Steps To Reproduce
State machine — catches the mocked error and routes to a
Failstate:{ "StartAt": "Call API", "States": { "Call API": { "Type": "Task", "Resource": "arn:aws:states:::apigateway:invoke", "Parameters": { "ApiEndpoint": "example.execute-api.eu-west-1.amazonaws.com", "Method": "POST", "Stage": "v1", "Path": "/things" }, "Catch": [{"ErrorEquals": ["ApiGateway.422"], "Next": "Unprocessable"}], "End": true }, "Unprocessable": { "Type": "Fail", "Error": "422", "Cause": "Unable to create a school" } } }Mock config:
{ "StateMachines": {"Test": {"TestCases": {"Case": {"Call API": "Throw422"}}}}, "MockedResponses": {"Throw422": {"0": {"Throw": {"Error": "ApiGateway.422", "Cause": "boom"}}}} }Then:
Expected:
Catchmatches, execution fails witherror: "422",cause: "Unable to create a school".Actual: execution fails with
error: "ApiGateway.FailureEventException", empty cause.Environment
Also reproduced on
localstack/localstack:4.10.0, and present inmotoon current master.Cause
_eval_mocked_response_throw()correctly builds aFailureEventcarrying the mocked error nameand raises it wrapped in
FailureEventException:Each integration's
_from_error()then translates the exception into a TaskFailed event. Only afew unwrap
FailureEventExceptionfirst.StateTaskServiceApiGatewaydoes not:So the deliberately constructed
FailureEventis discarded and replaced by one named after thePython exception class.
The
SfnGatewayExceptionbranch is correct, but it cannot be reached from a mocked response.That exception is raised only inside
_eval_service_task(), from a real HTTP response:and the two paths are mutually exclusive:
In mocked mode
_eval_service_task()never runs, so the only exception that reaches_from_error()isFailureEventException, and theelsebranch is the only one available tohandle it. Error translation on real invocations is unaffected — this is specific to the mocked
path reusing a translator written for the real one.
Affected integrations
These unwrap
FailureEventExceptionin_from_error(), so a mockedThrowfails the task withthe mocked error name, and
Catch/Retrymatch as expected:StateTaskLambda(the directarn:aws:lambda:...resource)StateTaskServiceSfnStateTaskServiceBatchStateTaskServiceGlueThese do not unwrap it, so a mocked
Throwfails the task with<Service>.FailureEventExceptionand an empty cause instead of the mocked values:
StateTaskServiceApiGatewayStateTaskServiceAwsSdkStateTaskServiceLambda(lambda:invoke)StateTaskServiceSqsStateTaskServiceDynamoDBStateTaskServiceSnsStateTaskServiceEventsStateTaskServiceHttpStateTaskActivityWorth noting that the AWS documentation example for mocked service integrations uses Lambda's
direct resource, which is one of the four that work — so the feature appears healthy until
another integration is used.
Verified present in all three implementations:
localstack/pro/core/services/stepfunctions/...localstack-core/localstack/services/stepfunctions/...moto/stepfunctions/parser/...Suggested fix
Unwrap the carried failure event in each affected
_from_error(), exactly asStateTaskLambdaalready does:
Handling it once in the shared base class would cover every integration and stop the same gap
reappearing as new ones are added.
Applying that unwrap at runtime is enough to make the reproduction above behave correctly, so
the fix is confirmed against the reported symptom.
Anything else?
The practical impact is that error-path testing is unavailable for most integrations. In our
case a suite of ~700 Step Functions specs is built around
Catchbehaviour onapigateway:invoke, and none of those assertions can pass while the mocked error name isreplaced.
All reactions