AWS Lambda
Overview
The cloudstub-lambda module mocks AWS Lambda. Lambda uses the REST JSON protocol (paths such as
POST /2015-03-31/functions and POST /2015-03-31/functions/{name}/invocations), which AWS SDK v2
drives directly.
The function lifecycle and tag operations are state-backed: a function created by
CreateFunction is returned by a later GetFunction, listed by ListFunctions, survives a restart
when a persistent store directory is configured, and is visible through the REST API.
Invoke does not run code: it echoes the request payload back with status 200, so callers get a
deterministic, inspectable result. See Supported operations for the full
list.
Standalone usage
Start the standalone server with the Lambda module enabled (it is auto-downloaded if not already in the plugin directory):
Applications talk to it through the AWS SDK by pointing the client's endpoint at the mock port
(http://localhost:4566), see the Test example for a LambdaClient setup and
Standalone Mode for the full configuration.
Create a function and invoke it with the AWS CLI:
$ aws lambda create-function --endpoint-url http://localhost:4566 \
--function-name processor \
--runtime nodejs20.x --role arn:aws:iam::000000000000:role/lambda-role \
--handler index.handler --zip-file fileb://function.zip
$ aws lambda invoke --endpoint-url http://localhost:4566 \
--function-name processor --payload '{"order":42}' out.json
$ cat out.json
{"order":42}
Invoke returns the payload it received (the mock does not execute the function), so a client can
verify its request and response wiring end to end.
To inspect function state from the terminal, use the CLI (clb), or call the
REST API on the API port (4567) directly:
$ curl -s "http://localhost:4567/api/lambda/list-functions"
{"functions":[{"FunctionName":"processor","Runtime":"nodejs20.x","Handler":"index.handler"}]}
$ curl -s "http://localhost:4567/api/lambda/get-function?name=processor"
{"configuration":{"FunctionName":"processor","Runtime":"nodejs20.x","State":"Active"}}
The REST API and the SDK share the same state store, so a function your application creates through
the SDK is listed by GET /api/lambda/list-functions. The REST surface is read-oriented; creating
and invoking functions goes through the AWS SDK. See REST API access for the full
route set.
Test example
In embedded mode, add cloudstub-lambda (see Getting Started) and exercise
the service end to end with CloudStubExtension:
import io.cloudstub.junit.CloudStubExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
import java.net.URI;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(CloudStubExtension.class)
class ProcessorFunctionTest {
@Test
void invokeEchoesThePayload() {
LambdaClient lambda = LambdaClient.builder()
.endpointOverride(URI.create(System.getProperty("aws.endpoint-url")))
.credentialsProvider(AnonymousCredentialsProvider.create())
.region(Region.US_EAST_1)
.build();
lambda.createFunction(b -> b.functionName("processor")
.runtime("nodejs20.x").role("arn:aws:iam::000000000000:role/lambda-role")
.handler("index.handler")
.code(c -> c.zipFile(SdkBytes.fromUtf8String("exports.handler=async e=>e;"))));
InvokeResponse response = lambda.invoke(b -> b.functionName("processor")
.payload(SdkBytes.fromUtf8String("{\"order\":42}")));
assertEquals(200, response.statusCode());
assertEquals("{\"order\":42}", response.payload().asUtf8String());
}
}
REST API access
The module exposes a REST API under /api/lambda/…. These routes read the same
state as the AWS-protocol stubs, so a function created with the SDK is listed by
GET /api/lambda/list-functions. Parameters are passed as query-string values.
| Route | Parameters | Description |
|---|---|---|
GET /api/lambda/list-functions |
— | List functions |
GET /api/lambda/get-function |
name |
Show a function's configuration |
Supported operations
State-backed operations return live data from the shared state store:
| Operation | Behavior |
|---|---|
CreateFunction |
Records the function; returns its configuration |
GetFunction |
Returns the stored configuration, code location, and tags |
GetFunctionConfiguration |
Returns the stored configuration |
ListFunctions |
Returns every created function's configuration |
UpdateFunctionConfiguration |
Merges the supplied fields into the stored configuration |
UpdateFunctionCode |
Recomputes CodeSize/CodeSha256 from the new inline code |
DeleteFunction |
Removes the function and its tags |
Invoke |
Echoes the request payload with X-Amz-Executed-Version: $LATEST |
TagResource / UntagResource |
Adds or removes function tags |
ListTags |
Returns the function's tags |
GetAccountSettings |
Returns fixed account limits plus the live function count |
Limitations
Invokedoes not execute the function: it returns the request payload verbatim. Function logic, the log tail, and theFunctionErrorfield are not simulated.- Versions and aliases are not simulated: a qualifier in the function name or ARN (for example
processor:PROD) is stripped and resolves to the unqualified function. CodeSizeandCodeSha256are computed from the decoded inlineCode.ZipFilebytes. Code supplied from S3 or an image URI, and layers, are not stored.- Event source mappings, function URLs, concurrency, event-invoke config, and durable executions are not implemented.
CreateFunctionaccepts a function without validating the runtime, role, or handler; requests always succeed unless the function already exists (ResourceConflictException).lambdaands3can run on the same server. Both use URL-path routing, and S3's catch-all object patterns (/[^/]+/.+) overlap Lambda's paths, so routing relies on stub specificity: Lambda's literal/2015-03-31/functions/...prefixes are more specific than S3's catch-alls and win, while every normal S3 bucket and key is unaffected. The one exception: an S3 bucket named exactly like a Lambda API date prefix (2015-03-31,2016-08-19,2017-03-31) with a key path matching a Lambda route is shadowed by the Lambda stub. Use a different bucket name.
See also: Troubleshooting for common integration problems and workarounds.