Search code examples
node.jstestingaws-step-functions

stepfunctions-local not picking up mock definition file


I am using the amazon/aws-stepfunctions-local image to run AWS Step Functions locally. While the problem I'm having is happening when loading from testcontainers within a jest test, I am having the same problem when running a test against a running container starting from docker cli.

When I create and execute a step function using the MockConfigFile, it errors on a step where it calls a lambda with an error like "The security token included in the request is invalid.":

    2023-07-13 20:09:27.708: arn:aws:states:us-east-1:123456789012:execution:createApp:5cc02245-ce24-4e28-a3de-25edd6a81ce7 : {"Type":"TaskFailed","PreviousEventId":4,"TaskFailedEventDetails":{"ResourceType":"lambda","Resource":"invoke","Error":"Lambda.AWSLambdaException","Cause":"The security token included in the request is invalid. (Service: AWSLambda; Status Code: 403; Error Code: UnrecognizedClientException; Request ID: 4cc1f11b-cc78-4092-8733-c77397823ee2; Proxy: null)"}}

Starting the container:

   const mockFileContainerPath = '/home/stepfunctionslocal/MockConfigFile.json';
  let container;
  let host;
  let port;
  const exposedPort = 8083;
  process.env.TESTCONTAINERS_RYUK_DISABLED = true;
  jest.setTimeout(90000);
  beforeAll(async () => {
    container = await new GenericContainer('amazon/aws-stepfunctions-local')
      .withExposedPorts(exposedPort)
      .withLogConsumer(stream => {
        stream.on("data", line => console.info(line));
        stream.on("err", line => console.error(line));
        stream.on("end", () => console.info("Stream closed"));
      })
      .withBindMounts([{
        source: path.resolve('./step-function-tests/assets/MockConfigFile.json'),
        target: mockFileContainerPath,
        mode: "ro"
      }])
      .withEnvironment({
        SFN_MOCK_CONFIG: mockFileContainerPath},
        AWS_ACCESS_KEY_ID: "blahblah",
        AWS_SECRET_ACCESS_KEY: "supersecret"
      })
      .withWaitStrategy(
        Wait.forLogMessage(RegExp(`.*Starting server on port ${exposedPort}.*`))
      ).start();
    host = container.getHost();
    port = container.getMappedPort(exposedPort);`
  });

I can attach to the running container and see that the MockConfigFile.json exists at /home/stepfunctionslocal/MockConfigFile.json.

It seems like step-functions-local is actually trying to invoke the lambda, as opposed to using the mock response from the config file. Should I expect to see any indication in the logs that step-functions-local has loaded the mock file? My mock file looks like:

{
  "StateMachines": {
    "createApp": {
      "TestCases": {
        "HappyPath": {
          "Create application": "MockedCreateApplicationSuccess"
        }
      }
    }
  },
  "MockedResponses": {
    "MockedCreateApplicationSuccess": {
      "0": {
        "Return": {
          "StatusCode": 200,
          "Payload": {
            "hello": "world"
          }
        }
      }
    }
  }
}

Where "Create application" is the name of a state that invokes the lambda function.


Solution

  • The documentation clearly states that in order for the mock engine to return a response, the execution has to be started with the name of the test case appended to the stateMachineArn, which I was not doing. Skimmed over that part every time I read it, it seems.

        const startCommandInput = {
          stateMachineArn: `${stateMachineArn}#HappyPath`,
          input: JSON.stringify({
            hello: "world"
          })
        };
        const startCommand = new StartExecutionCommand(startCommandInput);
        const { executionArn } = await client.send(startCommand);