diff --git a/.gitignore b/.gitignore index f86678d7a..fa7937bf4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ **/Deployment/ **/packages **/launchSettings.json +# Keep the launch profiles for the Lambda Test Tool v2 sample projects (they are part of the docs). +!Tools/LambdaTestTool-v2/samples/**/launchSettings.json **/Debug/ **/build/ diff --git a/Tools/LambdaTestTool-v2/README.md b/Tools/LambdaTestTool-v2/README.md index ba025aefa..6918113b3 100644 --- a/Tools/LambdaTestTool-v2/README.md +++ b/Tools/LambdaTestTool-v2/README.md @@ -1,209 +1,402 @@ # AWS Lambda Test Tool -## Overview -The AWS Lambda Test Tool provides local testing capabilities for .NET Lambda functions with support for both Lambda emulation and API Gateway emulation. This tool allows developers to test their Lambda functions locally in three different modes: +Test and debug your .NET AWS Lambda functions locally. The tool runs a local Lambda Runtime API emulator (so your function connects to it exactly as it would in the cloud), an optional API Gateway emulator, and optional SQS / DynamoDB Streams event sources — all driven from a web UI. -1. Lambda Emulator Mode -2. API Gateway Emulator Mode -3. Combined Mode (both emulators) +![The Lambda Test Tool web UI showing the function input editor and invoke controls.](Resources/img.png) -![img.png](Resources/img.png) +> **Preview:** This tool is in preview. See [Known Limitations](#known-limitations). For questions and problems, please [open a GitHub issue](https://github.com/aws/aws-lambda-dotnet/issues). -## Comparison with Previous Test Tool +## Table of Contents -The AWS Lambda Test Tool is an evolution of the previous [AWS .NET Mock Lambda Test Tool](https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool), with several key improvements: - -### New Features -- **API Gateway Emulation**: Direct support for testing API Gateway integrations locally -- Updated to use a new flow for loading Lambda functions that mimics closer to the Lambda service. This solves many of the issues with the older tool when it came to loading dependencies. -- Ability to have multiple Lambda functions use the same instance of the test tool. -- UI refresh -- [Support for integration with .NET Aspire](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17) - -# AWS Lambda Test Tool - -- [Overview](#overview) -- [Comparison with Previous Test Tool](#comparison-with-previous-test-tool) - - [New Features](#new-features) -- [Getting help](#getting-help) -- [.NET Aspire integration](#net-aspire-integration) +- [Quick Start](#quick-start) +- [Prerequisites](#prerequisites) +- [Features](#features) - [Installing](#installing) - [Running the Test Tool](#running-the-test-tool) - - [Lambda Emulator Mode](#lambda-emulator-mode) - - [API Gateway Emulator Mode](#api-gateway-emulator-mode) - - [Required Configuration](#required-configuration) - - [Combined Mode](#combined-mode) + - [Lambda Emulator Mode](#lambda-emulator-mode) + - [API Gateway Emulator Mode](#api-gateway-emulator-mode) + - [Combined Mode](#combined-mode) - [Command Line Options](#command-line-options) +- [Using the Web UI](#using-the-web-ui) + - [Built-in Sample Events](#built-in-sample-events) + - [Saving Requests](#saving-requests) + - [Light / Dark Theme](#light--dark-theme) - [API Gateway Configuration](#api-gateway-configuration) - - [Single Route Configuration](#single-route-configuration) - - [Multiple Routes Configuration](#multiple-routes-configuration) + - [Single Route](#single-route) + - [Multiple Routes](#multiple-routes) + - [Wildcard Paths](#wildcard-paths) +- [Event Sources](#event-sources) + - [SQS Event Source](#sqs-event-source) + - [DynamoDB Streams Event Source](#dynamodb-streams-event-source) - [Example Lambda Function Setup](#example-lambda-function-setup) - - [1. Lambda Function Code](#1-lambda-function-code) - - [Option 1: Using Top-Level Statements](#option-1-using-top-level-statements) - - [Option 2: Using Class Library](#option-2-using-class-library) - - [2. AWS_LAMBDA_RUNTIME_API](#2-aws_lambda_runtime_api) - - [3. API Gateway Configuration](#3-api-gateway-configuration) - - [4. Testing the Function](#4-testing-the-function) + - [Option 1: Top-Level Statements](#option-1-top-level-statements) + - [Option 2: Class Library](#option-2-class-library) + - [The AWS_LAMBDA_RUNTIME_API Environment Variable](#the-aws_lambda_runtime_api-environment-variable) +- [Sample Projects](#sample-projects) +- [.NET Aspire Integration](#net-aspire-integration) +- [Troubleshooting](#troubleshooting) +- [Known Limitations](#known-limitations) +- [What's New Compared to the Previous Test Tool](#whats-new-compared-to-the-previous-test-tool) +- [Getting Help](#getting-help) + +## Quick Start + +This gets you from install to a working local invocation in about five minutes, testing a Lambda function through the API Gateway emulator. -## Getting help +**1. Install the tool** (see [Prerequisites](#prerequisites) if `dotnet` commands aren't found): -This tool is currently in preview and there are some known limitations. For questions and problems please open a GitHub issue in this repository. +``` +dotnet tool install -g amazon.lambda.testtool +``` -## .NET Aspire integration -The easiest way to get started using the features of the new test tool is with .NET Aspire. The integration takes care of installing the tool and provides .NET Aspire extension methods for configuring your Lambda functions and API Gateway emulator in the .NET Aspire AppHost. It avoids all of the steps list below for installing the tooling and setting up environment variables. +**2. Create a Lambda function** that adds two numbers. This uses the AWS Lambda project templates (install once with `dotnet new install Amazon.Lambda.Templates`), or skip ahead and clone [`samples/AddFunctionTopLevel`](samples/AddFunctionTopLevel) instead: -Check out the following tracker issue for information on the .NET Aspire integration and steps for getting started. https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17 +``` +dotnet new lambda.EmptyFunction --name AddLambdaFunction +cd AddLambdaFunction/src/AddLambdaFunction +``` -## Installing +Replace the contents of `Function.cs` with a top-level-statements handler: + +```csharp +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; + +var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => +{ + var x = int.Parse(request.PathParameters["x"]); + var y = int.Parse(request.PathParameters["y"]); + return (x + y).ToString(); +}; + +await LambdaBootstrapBuilder.Create(handler, new CamelCaseLambdaJsonSerializer()) + .Build() + .RunAsync(); +``` -The tool is distributed as .NET Global Tool. To install the tool execute the following command: +Add the packages the handler uses: ``` -dotnet tool install -g amazon.lambda.testtool +dotnet add package Amazon.Lambda.RuntimeSupport +dotnet add package Amazon.Lambda.APIGatewayEvents ``` -To update the tool run the following command: +**3. Tell the function where the local runtime API is.** Add a launch profile to `Properties/launchSettings.json`: +```json +{ + "profiles": { + "AddLambdaFunction": { + "commandName": "Project", + "environmentVariables": { + "AWS_LAMBDA_RUNTIME_API": "localhost:5050/AddLambdaFunction" + } + } + } +} ``` -dotnet tool update -g amazon.lambda.testtool + +**4. Configure the API Gateway route** the emulator will expose. Set this environment variable in the terminal you'll start the test tool from: + +```bash +# Linux/macOS +export APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' ``` -## Running the Test Tool +```powershell +# Windows (PowerShell) +$env:APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' +``` -### Lambda Emulator Mode -Use this mode when you want to test Lambda functions directly without API Gateway integration. +**5. Start the test tool** with both the Lambda and API Gateway emulators (in the terminal from step 4): ``` -# Start Lambda emulator on port 5050 -dotnet lambda-test-tool start --lambda-emulator-port 5050 +dotnet lambda-test-tool start --lambda-emulator-port 5050 --api-gateway-emulator-port 5051 --api-gateway-emulator-mode HttpV2 ``` -### API Gateway Emulator Mode -Use this mode when you want to test Lambda functions through API Gateway endpoints. **Note: Running this mode by itself will not work, you will still need have the lambda runtime client running elsewhere and reference it in the `Endpoint` parameter in the `APIGATEWAY_EMULATOR_ROUTE_CONFIG` env varible (see below [Required Configuration](#required-configuration))** Api gateway mode requires additional configuration through environment variables. +**6. Start your function** (in a separate terminal, from the function directory): ``` -# Start API Gateway emulator on port 5051 in REST mode -dotnet lambda-test-tool start \ - --api-gateway-emulator-port 5051 \ - --api-gateway-emulator-mode Rest +dotnet run --launch-profile AddLambdaFunction +``` + +**7. Invoke it** through the API Gateway emulator: ``` -#### Required Configuration -When running via command line, you must set the environment variable for API Gateway route configuration: +curl "http://localhost:5051/add/5/3" +``` -Linux/macOS: -```bash -export APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"{LAMBDA_RUNTIME_API}"}' +Expected response: ``` +8 +``` + +> A ready-made version of this walkthrough lives in [`samples/AddFunctionTopLevel`](samples/AddFunctionTopLevel). See [Sample Projects](#sample-projects). + +## Prerequisites -Windows (Command Prompt): +- **.NET SDK 8.0 or later.** The tool targets `net8.0` and `net10.0` and rolls forward, so a .NET 8, 9, or 10 SDK works. Verify with `dotnet --version`. +- **The .NET global tools directory must be on your `PATH`.** `dotnet tool install -g` installs to `~/.dotnet/tools` (Linux/macOS) or `%USERPROFILE%\.dotnet\tools` (Windows). If `dotnet lambda-test-tool` reports "command not found" after installing, add that directory to your `PATH` and open a new terminal. +- Verify the install with `dotnet lambda-test-tool info`, which prints the installed version and path. +## Features + +- **Lambda Runtime API emulator** — run and debug any .NET Lambda function locally; it connects to the emulator exactly as it would to the real Lambda service. +- **API Gateway emulator** — test functions through REST, HTTP API v1, or HTTP API v2 request/response shapes. +- **SQS and DynamoDB Streams event sources** — poll a real queue or table stream and invoke your function with batched events. +- **Web UI** — pick or edit an event, invoke, inspect the response, and re-invoke from history. +- **58 built-in sample events** — S3, SQS, SNS, DynamoDB, API Gateway, CloudWatch, Kinesis, Alexa, Lex, and more, available directly in the UI. +- **Saved requests** — save and reuse request payloads across sessions. +- **Light / dark theme.** +- **[.NET Aspire integration](#net-aspire-integration)** — for a lower-configuration setup. + +## Installing + +The tool is distributed as a .NET Global Tool: + +``` +dotnet tool install -g amazon.lambda.testtool +``` + +To update: + +``` +dotnet tool update -g amazon.lambda.testtool ``` -set APIGATEWAY_EMULATOR_ROUTE_CONFIG={"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"{LAMBDA_RUNTIME_API}"} +To confirm the installed version and location (useful for the class-library setup below): + +``` +dotnet lambda-test-tool info ``` -Windows (PowerShell): +## Running the Test Tool + +The test tool can run the Lambda emulator, the API Gateway emulator, or both. + +### Lambda Emulator Mode + +Use this mode to invoke Lambda functions directly (via the web UI or the SDK), without API Gateway. This is the simplest way to test event-driven functions (S3, SQS, custom events). ``` -$env:APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"{LAMBDA_RUNTIME_API}"}' +# Start the Lambda emulator on port 5050 +dotnet lambda-test-tool start --lambda-emulator-port 5050 +``` + +The web UI opens automatically. Point your function at the emulator with the [`AWS_LAMBDA_RUNTIME_API`](#the-aws_lambda_runtime_api-environment-variable) environment variable, then invoke it from the UI. + +### API Gateway Emulator Mode + +Use this mode to test functions through API Gateway endpoints. + +> **The API Gateway emulator does not run your function.** It forwards requests to a Lambda Runtime API endpoint that must already be running — either the Lambda emulator (Combined Mode below) or one you point at with the route config's `Endpoint`. Running this mode by itself will not invoke anything. +``` +dotnet lambda-test-tool start --api-gateway-emulator-port 5051 --api-gateway-emulator-mode HttpV2 ``` -Replace `{LAMBDA_RUNTIME_API}` with your Lambda runtime API endpoint (e.g., "http://localhost:5050/AddLambdaFunction" or the endpoint specified in your `AWS_LAMBDA_RUNTIME_API` environment variable). +You must also set the `APIGATEWAY_EMULATOR_ROUTE_CONFIG` environment variable to map routes to functions — see [API Gateway Configuration](#api-gateway-configuration). +> **Choose the mode to match your handler.** `Rest`, `HttpV1`, and `HttpV2` differ in the request event shape and how the response is interpreted. A handler taking `APIGatewayHttpApiV2ProxyRequest` (like the Quick Start) must use `HttpV2`; `APIGatewayProxyRequest` is used by `Rest` and `HttpV1`. A mismatch typically produces an HTTP 502. `HttpV2` is the default if `--api-gateway-emulator-mode` is omitted. ### Combined Mode -Use this mode when you want to run both Lambda and API Gateway emulators simultaneously. + +Run both emulators together — the API Gateway emulator forwards to the Lambda emulator automatically. This is the most common setup. ``` -# Start both emulators dotnet lambda-test-tool start \ --lambda-emulator-port 5050 \ --api-gateway-emulator-port 5051 \ - --api-gateway-emulator-mode Rest + --api-gateway-emulator-mode HttpV2 ``` ## Command Line Options -| Option | Description | Required For | -|--------|-------------|--------------| -| `--lambda-emulator-port` | Port for Lambda emulator | Lambda Mode | -| `--lambda-emulator-host` | Host for Lambda emulator | Lambda Mode | -| `--api-gateway-emulator-port` | Port for API Gateway | API Gateway Mode | -| `--api-gateway-emulator-mode` | API Gateway mode (Rest/HttpV1/HttpV2) | API Gateway Mode | -| `--no-launch-window` | Disable auto-launching web interface | Optional | -| `--config-storage-path` | Path for saving settings and requests | Optional | +All options belong to the `start` command (`dotnet lambda-test-tool start ...`). -## API Gateway Configuration -When using API Gateway mode, you need to configure the route mapping using the APIGATEWAY_EMULATOR_ROUTE_CONFIG environment variable. This can be a single route or an array of routes: +| Option | Description | Default | +|--------|-------------|---------| +| `-p`, `--lambda-emulator-port ` | Port for the Lambda emulator / web UI. If set, the Lambda emulator starts. | — | +| `--lambda-emulator-host ` | Host for the web UI. Any value other than an IP or `localhost` (e.g. `*`, `+`) binds to all public addresses. | `localhost` | +| `--lambda-emulator-https-port ` | HTTPS port for the web UI. Requires certs configured for the host. | — | +| `--api-gateway-emulator-port ` | Port for the API Gateway emulator. If set, the emulator starts (requires `--api-gateway-emulator-mode`). | — | +| `--api-gateway-emulator-mode ` | API Gateway mode: `Rest`, `HttpV1`, or `HttpV2`. | `HttpV2` | +| `--api-gateway-emulator-https-port ` | HTTPS port for the API Gateway emulator. Requires certs configured for the host. | — | +| `--sqs-eventsource-config ` | Configure an [SQS event source](#sqs-event-source). | — | +| `--dynamodbstreams-eventsource-config ` | Configure a [DynamoDB Streams event source](#dynamodb-streams-event-source). | — | +| `--no-launch-window` | Do not auto-launch the web UI in a browser. | off | +| `--config-storage-path ` | Absolute path for [saving settings and requests](#saving-requests). | — | -### Single Route Configuration +The tool also has an `info` command: + +``` +dotnet lambda-test-tool info [--format Text|Json] ``` + +It prints the installed `Version` and `InstallPath`. `--format` defaults to `Text`. + +## Using the Web UI + +When the Lambda emulator starts, the web UI opens automatically (disable with `--no-launch-window`). It's the primary way to invoke and debug functions — no `curl` required. + +The typical flow: + +1. **Select the function** to invoke (the "Switch function" control lists every function connected to the emulator). +2. **Provide the event** in the Function Input editor. Type/paste JSON, or pick a [built-in sample event](#built-in-sample-events) or a [saved request](#saving-requests) from the dropdown. +3. **Invoke** the function. The request moves through the **Active Event**, **Queued**, and **History** tabs. +4. **Inspect the response** in the request/response dialog — the response body or the error and stack trace if the function threw. +5. **Re-Invoke** any past request from History, or **Clear** the queue/history. + +### Built-in Sample Events + +The tool ships **58 sample event payloads** (S3, SQS, SNS, DynamoDB, API Gateway, CloudWatch Logs, Kinesis, Alexa, Lex, CloudFront, and more), available from the "Example Requests" dropdown above the Function Input editor. Pick one to populate the editor with a realistic event instead of hand-writing JSON — the fastest way to test an event-driven handler. + +### Saving Requests + +You can save request payloads for quick reuse. Saved requests appear in a dropdown above the Function Input editor, and the UI provides: + +- A **Save** dialog to name and store the current input. +- A **Manage Saved Requests** dialog to delete saved requests. +- Toggles to show/hide the sample events, saved requests, and the requests list. + +Saving is only enabled when you provide a storage path at startup, because requests are persisted to disk there: + +``` +dotnet lambda-test-tool start --lambda-emulator-port 5050 --config-storage-path +``` + +### Light / Dark Theme + +The UI has a light/dark theme switcher in the top navigation. Your choice persists across sessions (when a `--config-storage-path` is configured). + +## API Gateway Configuration + +When using the API Gateway emulator, map routes to functions with the `APIGATEWAY_EMULATOR_ROUTE_CONFIG` environment variable. Each route has: + +- `LambdaResourceName` — the function name (matches the name in `AWS_LAMBDA_RUNTIME_API`). +- `HttpMethod` — e.g. `Get`, `Post` (matched case-insensitively). +- `Path` — the route template, e.g. `/add/{x}/{y}`. +- `Endpoint` — the **base URL** of the Lambda Runtime API, e.g. `http://localhost:5050`. Do **not** append the function name here; that comes from `LambdaResourceName`. In Combined Mode this is the Lambda emulator's address. + +The value can be a single route object or an array of routes. + +### Single Route + +```json { "LambdaResourceName": "AddLambdaFunction", "HttpMethod": "Get", - "Path": "/add/{x}/{y}" + "Path": "/add/{x}/{y}", + "Endpoint": "http://localhost:5050" } ``` -### Multiple Routes Configuration +### Multiple Routes -``` +```json [ { "LambdaResourceName": "AddLambdaFunction", "HttpMethod": "Get", - "Path": "/add/{x}/{y}" + "Path": "/add/{x}/{y}", + "Endpoint": "http://localhost:5050" }, { "LambdaResourceName": "SubtractLambdaFunction", "HttpMethod": "Get", - "Path": "/minus/{x}/{y}" + "Path": "/minus/{x}/{y}", + "Endpoint": "http://localhost:5050" } ] - ``` +> **Setting the variable on Windows Command Prompt:** quote the whole assignment so special characters are preserved: +> ``` +> set "APIGATEWAY_EMULATOR_ROUTE_CONFIG={"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}" +> ``` -#### Wildcard Paths -The API Gateway emulator supports the use of wildcard path. To define a wildcard path, you can use the `{proxy+}` syntax in the route pattern. See [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html) for a more detailed explanation on how proxies work. +### Wildcard Paths -Here's an example of how to set up an API Gateway emulator with a wildcard path: +Use the `{proxy+}` syntax to proxy any additional path segments to a function. See the [API Gateway proxy integration docs](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html) for details. -``` +```json [ { "LambdaResourceName": "RootFunction", "HttpMethod": "Get", - "Path": "/root" + "Path": "/root", + "Endpoint": "http://localhost:5050" }, { "LambdaResourceName": "MyOtherLambdaFunction", "HttpMethod": "Get", - "Path": "/root/{proxy+}" + "Path": "/root/{proxy+}", + "Endpoint": "http://localhost:5050" } ] +``` + +This maps `/root` to `RootFunction` and any deeper path (e.g. `/root/a/b`) to `MyOtherLambdaFunction`. + +## Event Sources + +The tool can poll a real AWS SQS queue or DynamoDB table stream and invoke your function with the batched events, mirroring how Lambda event source mappings work. These use real AWS credentials (via the `Profile`/`Region` keys or your default credential chain). + +### SQS Event Source + +Long-polls a queue, batches messages into an `SQSEvent`, invokes the function, and deletes messages on success (honoring partial-batch failures via `SQSBatchResponse`). ``` +dotnet lambda-test-tool start \ + --lambda-emulator-port 5050 \ + --sqs-eventsource-config "QueueUrl=,FunctionName=,VisibilityTimeout=100" +``` -This JSON configuration sets up two API Gateway resources: +The config is a comma-delimited list of key pairs. Supported keys: -1. `/root` that is mapped to the `RootFunction` Lambda function. -2. `/root/{proxy+}` that is a proxy resource mapped to the `MyOtherLambdaFunction` Lambda function. +| Key | Description | +|-----|-------------| +| `QueueUrl` | The queue to poll. | +| `FunctionName` | Function to invoke. Defaults to the current emulator's function. | +| `BatchSize` | Max messages per batch. | +| `VisibilityTimeout` | Visibility timeout (seconds) for received messages. | +| `DisableMessageDelete` | If `true`, don't delete messages after a successful invoke. | +| `LambdaRuntimeApi` | Runtime API endpoint if the function runs outside this instance. | +| `Region` | AWS region of the queue. | +| `Profile` | AWS profile for credentials. | -The `{proxy+}` syntax in the second path allows the API Gateway to proxy any additional path segments to the integrated Lambda function. +### DynamoDB Streams Event Source -## Example Lambda Function Setup +Resolves the table's latest stream, discovers shards, and polls for records to invoke your function. -Here's a simple Lambda function that adds two numbers together. +``` +dotnet lambda-test-tool start \ + --lambda-emulator-port 5050 \ + --dynamodbstreams-eventsource-config "TableName=,FunctionName=,BatchSize=100" +``` + +The config accepts comma-delimited key pairs, a JSON object, a JSON array, or a path to a JSON file. Supported keys: + +| Key | Description | +|-----|-------------| +| `TableName` | The table whose stream to poll. | +| `FunctionName` | Function to invoke. Defaults to the current emulator's function. | +| `BatchSize` | Max records per batch. | +| `PollingIntervalMs` | Delay between polls, in milliseconds. | +| `LambdaRuntimeApi` | Runtime API endpoint if the function runs outside this instance. | +| `Region` | AWS region of the table. | +| `Profile` | AWS profile for credentials. | + +## Example Lambda Function Setup -### 1. Lambda Function Code -This can be implemented in two ways: +A function can be wired to the test tool in two ways. Option 1 is the lowest-friction path and is recommended for getting started. -#### Option 1: Using Top-Level Statements +### Option 1: Top-Level Statements +The handler is the entry point, launched with a simple `Project` launch profile. ```csharp using Amazon.Lambda.APIGatewayEvents; @@ -211,27 +404,24 @@ using Amazon.Lambda.Core; using Amazon.Lambda.RuntimeSupport; using Amazon.Lambda.Serialization.SystemTextJson; -var Add = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => +var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => { - // Parse x and y from the path parameters var x = int.Parse(request.PathParameters["x"]); var y = int.Parse(request.PathParameters["y"]); return (x + y).ToString(); }; -await LambdaBootstrapBuilder.Create(Add, new CamelCaseLambdaJsonSerializer()) +await LambdaBootstrapBuilder.Create(handler, new CamelCaseLambdaJsonSerializer()) .Build() .RunAsync(); - ``` -Configure the Lambda function to use the test tool: - **Properties/launchSettings.json** -``` + +```json { "profiles": { - "AspireTestFunction": { + "AddLambdaFunction": { "commandName": "Project", "environmentVariables": { "AWS_LAMBDA_RUNTIME_API": "localhost:5050/AddLambdaFunction" @@ -241,8 +431,13 @@ Configure the Lambda function to use the test tool: } ``` -#### Option 2: Using Class Library -``` +Run it with `dotnet run --launch-profile AddLambdaFunction`. A complete project is in [`samples/AddFunctionTopLevel`](samples/AddFunctionTopLevel). + +### Option 2: Class Library + +For a class-library function (a handler method rather than top-level statements), you run the function assembly under the test tool's copy of the Lambda runtime support library. This works the same whether you launch it from the command line or an IDE. + +```csharp using Amazon.Lambda.APIGatewayEvents; using Amazon.Lambda.Core; @@ -259,17 +454,59 @@ public class Function } ``` -Configure the Lambda function to use the test tool: +**1. Make sure dependencies land in the build output.** A class library doesn't copy its NuGet dependencies (e.g. `Amazon.Lambda.Core.dll`) next to the output DLL by default, so add this to the `.csproj`: -**Properties/launchSettings.json** +```xml + + true + +``` + +**2. Build the function** so the `.deps.json` / `.runtimeconfig.json` exist and dependencies are copied: + +``` +dotnet build ``` + +#### Run it from the command line + +Set [`AWS_LAMBDA_RUNTIME_API`](#the-aws_lambda_runtime_api-environment-variable), then launch the function assembly under the test tool's runtime support shim. From the build output directory (e.g. `bin/Debug/net8.0`): + +```bash +# Linux/macOS +export AWS_LAMBDA_RUNTIME_API="localhost:5050/AddLambdaFunction" + +dotnet exec \ + --depsfile ./MyLambdaFunction.deps.json \ + --runtimeconfig ./MyLambdaFunction.runtimeconfig.json \ + "$HOME/.dotnet/tools/.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/Amazon.Lambda.RuntimeSupport/{TARGET_FRAMEWORK}/Amazon.Lambda.RuntimeSupport.TestTool.dll" \ + "{FUNCTION_HANDLER}" +``` + +On Windows (PowerShell), the shim path is under `$env:USERPROFILE\.dotnet\tools\.store\...`. + +Replace the three placeholders: + +1. **`{TEST_TOOL_VERSION}`** — your installed test tool version (appears **twice** in the `.store` path). Find it with `dotnet lambda-test-tool info` or `dotnet tool list -g`. +2. **`{TARGET_FRAMEWORK}`** — your Lambda project's target framework, e.g. `net8.0`. +3. **`{FUNCTION_HANDLER}`** — your handler in the form `::.::`, e.g. `MyLambdaFunction::MyLambdaFunction.Function::Add`. + +> The runtime support assembly is named `Amazon.Lambda.RuntimeSupport.TestTool.dll` (not `Amazon.Lambda.RuntimeSupport.dll`) to avoid conflicting with the version your function already references. + +#### Run it from an IDE (Visual Studio / Rider) + +If you'd rather press F5, add a launch profile. This wraps the same `dotnet exec` command; the IDE expands `$(Configuration)` and resolves `workingDirectory` for you (plain `dotnet run --launch-profile` does not, so use the command-line form above outside an IDE). + +**Properties/launchSettings.json** + +```json { "profiles": { - "LambdaRuntimeClient_FunctionHandler": { - "workingDirectory": ".\\bin\\$(Configuration)\\net8.0", + "LambdaTestTool": { "commandName": "Executable", - "commandLineArgs": "exec --depsfile ./MyLambdaFunction.deps.json --runtimeconfig ./MyLambdaFunction.runtimeconfig.json %USERPROFILE%/.dotnet/tools/.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/Amazon.Lambda.RuntimeSupport/{TARGET_FRAMEWORK}/Amazon.Lambda.RuntimeSupport.TestTool.dll MyLambdaFunction::MyLambdaFunction.Function::Add", "executablePath": "dotnet", + "workingDirectory": ".\\bin\\$(Configuration)\\{TARGET_FRAMEWORK}", + "commandLineArgs": "exec --depsfile ./MyLambdaFunction.deps.json --runtimeconfig ./MyLambdaFunction.runtimeconfig.json %USERPROFILE%/.dotnet/tools/.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/Amazon.Lambda.RuntimeSupport/{TARGET_FRAMEWORK}/Amazon.Lambda.RuntimeSupport.TestTool.dll {FUNCTION_HANDLER}", "environmentVariables": { "AWS_LAMBDA_RUNTIME_API": "localhost:5050/AddLambdaFunction" } @@ -278,68 +515,72 @@ Configure the Lambda function to use the test tool: } ``` -There are three variables you may need to replace: +The same three placeholders apply. On **Linux/macOS**, replace `%USERPROFILE%` with `$HOME`/`~` and use forward slashes throughout the `workingDirectory`. -There are three variables you need to update in the launch settings: +A complete, build-verified example is in [`samples/AddFunctionClassLibrary`](samples/AddFunctionClassLibrary). -1. `{TEST_TOOL_VERSION}` - Replace with the current Amazon.Lambda.TestTool version (e.g., `0.0.3` in the example above) - - This appears in the path: `.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/` +### The AWS_LAMBDA_RUNTIME_API Environment Variable -2. `{TARGET_FRAMEWORK}` - Replace with your Lambda project's target framework version (e.g., `net8.0` in the example above) - - This appears in two places: - - The working directory: `.\\bin\\$(Configuration)\\{TARGET_FRAMEWORK}` - - The runtime support DLL path: `Amazon.Lambda.RuntimeSupport/{TARGET_FRAMEWORK}/Amazon.Lambda.RuntimeSupport.TestTool.dll` - - **Note: For the test tool the Amazon.Lambda.RuntimeSupport.dll assembly was renamed to Amazon.Lambda.RuntimeSupport.TestTool.dll to avoid conflicts with versions of Amazon.Lambda.RuntimeSupport used by the Lambda function itself.** +This variable tells your function where the Lambda Runtime API emulator is. Its format is: -3. `{FUNCTION_HANDLER}` - Replace with your function's handler using the format: `::.::` - - Example: `MyLambdaFunction::MyLambdaFunction.Function::Add` +``` +host:port/functionName +``` +The host and port must match the Lambda emulator (`--lambda-emulator-port`). In the examples above the emulator runs on `localhost:5050` and the function name is `AddLambdaFunction`, so the value is `localhost:5050/AddLambdaFunction`. +> **Do not add an `http://` prefix** to this value — the function will fail to connect if you do. (This differs from the API Gateway route config's `Endpoint`, which *does* include `http://`.) -### 2. AWS_LAMBDA_RUNTIME_API +## Sample Projects -The `AWS_LAMBDA_RUNTIME_API` environment variable tells the Lambda function where to find the Lambda runtime API endpoint. It has the following format: +Runnable starter projects live under [`samples/`](samples). Each has its own README with exact run steps. -`host:port/functionName` +| Sample | What it shows | +|--------|---------------| +| [`AddFunctionTopLevel`](samples/AddFunctionTopLevel) | The Quick Start: top-level-statements function behind the API Gateway emulator. | +| [`AddFunctionClassLibrary`](samples/AddFunctionClassLibrary) | A class-library function with a pre-filled `Executable` launch profile. | +| [`SQSProcessor`](samples/SQSProcessor) | An `SQSEvent` handler for the SQS event source (and the built-in `sqs.json` sample event). | +| [`ToUpperFunction`](samples/ToUpperFunction) | A minimal, zero-dependency function for exploring the web UI and sample events. | +## .NET Aspire Integration -The host and port should match the port that the lambda emulator is running on. -In this example we will be running the lambda runtime api emulator on `localhost` on port `5050` and our function name will be `AddLambdaFunction`. **Warning**: You should *not* add `http://` prefix to the host (if you do the lambda will fail to connect). +If you use [.NET Aspire](https://learn.microsoft.com/dotnet/aspire/), the AWS integration installs the test tool for you and provides extension methods to configure your Lambda functions and API Gateway emulator in the Aspire AppHost — avoiding the manual install and environment-variable setup described above. -### 3. API Gateway Configuration -To expose this Lambda function through API Gateway, set the APIGATEWAY_EMULATOR_ROUTE_CONFIG: +See the [.NET Aspire integration tracker](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17) for status and setup steps. -``` -{ - "LambdaResourceName": "AddLambdaFunction", - "HttpMethod": "GET", - "Path": "/add/{x}/{y}" -} -``` +## Troubleshooting -### 4. Testing the Function -1. Start the test tool with both Lambda and API Gateway emulators: -``` -dotnet lambda-test-tool start \ - --lambda-emulator-port 5050 \ - --api-gateway-emulator-port 5051 \ - --api-gateway-emulator-mode HTTPV2 +| Symptom | Cause / Fix | +|---------|-------------| +| `dotnet lambda-test-tool: command not found` | The .NET global tools directory isn't on your `PATH`. See [Prerequisites](#prerequisites), then open a new terminal. | +| Function won't connect to the runtime API | Don't put `http://` in `AWS_LAMBDA_RUNTIME_API`; use `host:port/functionName`. | +| API Gateway requests do nothing / time out | The API Gateway emulator doesn't run your function. Use [Combined Mode](#combined-mode) or point `Endpoint` at a running Lambda Runtime API. | +| HTTP 502 from the API Gateway emulator | The emulator mode doesn't match your handler's request/response type. Match `Rest`/`HttpV1`/`HttpV2` to your handler — see [API Gateway Emulator Mode](#api-gateway-emulator-mode). | +| Invoke fails with a wrong function name | The route config `Endpoint` should be the base URL (`http://localhost:5050`), not include the function name. The function name comes from `LambdaResourceName` / `AWS_LAMBDA_RUNTIME_API`. | +| Class-library profile: "file not found" for the runtime support DLL | The `{TEST_TOOL_VERSION}` or `{TARGET_FRAMEWORK}` in `launchSettings.json` is wrong. Confirm the version with `dotnet lambda-test-tool info`. | +| Port already in use | Choose different `--lambda-emulator-port` / `--api-gateway-emulator-port` values. | -``` -2. Send a test request: -``` -curl -X GET "http://localhost:5051/add/5/3" -H "Content-Type: application/json" -d '"hello world"' -``` +## Known Limitations -Expected response: -``` -8 -``` +This tool is in preview. Notable current limitations: + +- The API Gateway emulator does not run your function — a Lambda Runtime API endpoint must be running separately (or use Combined Mode). +- The class-library launch profile (Option 2) requires manual path configuration and is Windows-oriented in the example. +- Event sources (SQS, DynamoDB Streams) require real AWS resources and credentials. + +Please [open a GitHub issue](https://github.com/aws/aws-lambda-dotnet/issues) if you hit a limitation not listed here. + +## What's New Compared to the Previous Test Tool -## Saving Lambda Requests +This tool is the evolution of the [AWS .NET Mock Lambda Test Tool](https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool), with several improvements: -The Test Tool provides users with the ability to save Lambda requests for quick access. Saved requests will be listed in a drop down above the request input area. +- **API Gateway emulation** — test API Gateway integrations locally. +- **A new function-loading flow** that mirrors the Lambda service more closely, resolving many dependency-loading issues in the older tool. +- **Multiple functions** can share one instance of the test tool. +- **SQS and DynamoDB Streams event sources.** +- **Refreshed web UI** with sample events, saved requests, and theming. +- **[.NET Aspire integration](#net-aspire-integration).** -In order to enable saving requests, you will need to provide a storage path during the test tool startup. +## Getting Help -You can use the command line argument `--config-storage-path ` to specify the storage path. +For questions and problems, please [open a GitHub issue](https://github.com/aws/aws-lambda-dotnet/issues) in this repository. diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/AddFunctionClassLibrary.csproj b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/AddFunctionClassLibrary.csproj new file mode 100644 index 000000000..439260fee --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/AddFunctionClassLibrary.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + Lambda + AddFunctionClassLibrary + + true + + true + + + + + + + + + diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Function.cs b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Function.cs new file mode 100644 index 000000000..e2d052489 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Function.cs @@ -0,0 +1,20 @@ +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; + +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.CamelCaseLambdaJsonSerializer))] + +namespace AddFunctionClassLibrary; + +public class Function +{ + /// + /// Adds the two path parameters {x} and {y} and returns the sum. + /// Handler string: AddFunctionClassLibrary::AddFunctionClassLibrary.Function::Add + /// + public int Add(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) + { + var x = int.Parse(request.PathParameters["x"]); + var y = int.Parse(request.PathParameters["y"]); + return x + y; + } +} diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Properties/launchSettings.json b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Properties/launchSettings.json new file mode 100644 index 000000000..ed4ee9adf --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "LambdaTestTool": { + "commandName": "Executable", + "executablePath": "dotnet", + "workingDirectory": ".\\bin\\$(Configuration)\\net8.0", + "commandLineArgs": "exec --depsfile ./AddFunctionClassLibrary.deps.json --runtimeconfig ./AddFunctionClassLibrary.runtimeconfig.json %USERPROFILE%/.dotnet/tools/.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/Amazon.Lambda.RuntimeSupport/net8.0/Amazon.Lambda.RuntimeSupport.TestTool.dll AddFunctionClassLibrary::AddFunctionClassLibrary.Function::Add", + "environmentVariables": { + "AWS_LAMBDA_RUNTIME_API": "localhost:5050/AddLambdaFunction" + } + } + } +} diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/README.md b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/README.md new file mode 100644 index 000000000..2dec03346 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionClassLibrary/README.md @@ -0,0 +1,78 @@ +# AddFunctionClassLibrary + +The same "add two numbers" function as [`AddFunctionTopLevel`](../AddFunctionTopLevel), but as a **class library** (a handler method rather than top-level statements). A class-library function is launched by running its assembly under the test tool's copy of the Lambda runtime support library. You can do this from the command line or from an IDE. + +Handler: `AddFunctionClassLibrary::AddFunctionClassLibrary.Function::Add` + +## Setup (once) + +The `.csproj` sets `true` so the function's NuGet dependencies (e.g. `Amazon.Lambda.Core.dll`) are copied next to the output DLL — required for the command-line launch below. + +Find your installed test tool version, which you'll substitute for `{TEST_TOOL_VERSION}`: + +``` +dotnet lambda-test-tool info +``` + +(or `dotnet tool list -g`). For example, `0.15.0`. + +> The runtime support assembly is `Amazon.Lambda.RuntimeSupport.TestTool.dll` — renamed from `Amazon.Lambda.RuntimeSupport.dll` to avoid conflicting with the version your function references. + +## Run it (command line) + +**1. Build the function:** + +``` +dotnet build +``` + +**2. Set the API Gateway route** (this directory): + +```bash +# Linux/macOS +export APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' +``` + +```powershell +# Windows (PowerShell) +$env:APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' +``` + +**3. Start the test tool** (same terminal): + +``` +dotnet lambda-test-tool start --lambda-emulator-port 5050 --api-gateway-emulator-port 5051 --api-gateway-emulator-mode HttpV2 +``` + +**4. Start the function** from its build output directory (separate terminal), replacing `{TEST_TOOL_VERSION}` with your installed version: + +```bash +# Linux/macOS +cd bin/Debug/net8.0 +export AWS_LAMBDA_RUNTIME_API="localhost:5050/AddLambdaFunction" + +dotnet exec \ + --depsfile ./AddFunctionClassLibrary.deps.json \ + --runtimeconfig ./AddFunctionClassLibrary.runtimeconfig.json \ + "$HOME/.dotnet/tools/.store/amazon.lambda.testtool/{TEST_TOOL_VERSION}/amazon.lambda.testtool/{TEST_TOOL_VERSION}/content/Amazon.Lambda.RuntimeSupport/net8.0/Amazon.Lambda.RuntimeSupport.TestTool.dll" \ + "AddFunctionClassLibrary::AddFunctionClassLibrary.Function::Add" +``` + +**5. Invoke it:** + +``` +curl "http://localhost:5051/add/5/3" +# => 8 +``` + +## Run it (IDE: Visual Studio / Rider) + +The committed [`launchSettings.json`](Properties/launchSettings.json) wraps the same command as an `Executable` profile you can launch with F5. Before using it, replace `{TEST_TOOL_VERSION}` (it appears **twice** in the `commandLineArgs` `.store` path) with your installed version. + +The other values are already set for this project: target framework `net8.0`, deps/runtimeconfig file names, and the handler string. + +> This profile relies on the IDE expanding `$(Configuration)` and resolving `workingDirectory`. Plain `dotnet run --launch-profile` does **not** do this — use the command-line steps above outside an IDE. + +> The profile is written for **Windows** (`%USERPROFILE%`, backslashes). On **Linux/macOS**, replace `%USERPROFILE%` with `$HOME` and use forward slashes in `workingDirectory` (`./bin/$(Configuration)/net8.0`). + +Then set `APIGATEWAY_EMULATOR_ROUTE_CONFIG`, start the test tool (steps 2–3 above), press F5 on the `LambdaTestTool` profile, and invoke with the step 5 curl. diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/AddFunctionTopLevel.csproj b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/AddFunctionTopLevel.csproj new file mode 100644 index 000000000..f23907f63 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/AddFunctionTopLevel.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + Lambda + AddFunctionTopLevel + + + + + + + + + + diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Program.cs b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Program.cs new file mode 100644 index 000000000..950153aa7 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Program.cs @@ -0,0 +1,17 @@ +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; + +// Adds the two path parameters {x} and {y} and returns the sum. +// Uses the HTTP API v2 request shape, so run the API Gateway emulator in HttpV2 mode. +var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => +{ + var x = int.Parse(request.PathParameters["x"]); + var y = int.Parse(request.PathParameters["y"]); + return (x + y).ToString(); +}; + +await LambdaBootstrapBuilder.Create(handler, new CamelCaseLambdaJsonSerializer()) + .Build() + .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Properties/launchSettings.json b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Properties/launchSettings.json new file mode 100644 index 000000000..3751fdf1d --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "AddLambdaFunction": { + "commandName": "Project", + "environmentVariables": { + "AWS_LAMBDA_RUNTIME_API": "localhost:5050/AddLambdaFunction" + } + } + } +} diff --git a/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/README.md b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/README.md new file mode 100644 index 000000000..15bff3210 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/AddFunctionTopLevel/README.md @@ -0,0 +1,38 @@ +# AddFunctionTopLevel + +The [Quick Start](../../README.md#quick-start) function: a top-level-statements Lambda that adds two numbers, invoked through the API Gateway emulator. Uses the HTTP API v2 request shape, so the API Gateway emulator runs in `HttpV2` mode. + +## Run it + +**1. In this directory, set the API Gateway route** the emulator should expose: + +```bash +# Linux/macOS +export APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' +``` + +```powershell +# Windows (PowerShell) +$env:APIGATEWAY_EMULATOR_ROUTE_CONFIG='{"LambdaResourceName":"AddLambdaFunction","HttpMethod":"Get","Path":"/add/{x}/{y}","Endpoint":"http://localhost:5050"}' +``` + +**2. Start the test tool** (same terminal): + +``` +dotnet lambda-test-tool start --lambda-emulator-port 5050 --api-gateway-emulator-port 5051 --api-gateway-emulator-mode HttpV2 +``` + +**3. Start the function** (separate terminal, this directory): + +``` +dotnet run --launch-profile AddLambdaFunction +``` + +**4. Invoke it:** + +``` +curl "http://localhost:5051/add/5/3" +# => 8 +``` + +You can also invoke the function directly (without API Gateway) from the web UI that opens at `http://localhost:5050`. diff --git a/Tools/LambdaTestTool-v2/samples/README.md b/Tools/LambdaTestTool-v2/samples/README.md new file mode 100644 index 000000000..8d43da3d4 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/README.md @@ -0,0 +1,17 @@ +# Lambda Test Tool v2 — Sample Projects + +Runnable starter functions for the [AWS Lambda Test Tool](../README.md). Each is a minimal, self-contained project with its own README and a ready-to-use launch profile. + +| Sample | What it shows | Emulator setup | +|--------|---------------|----------------| +| [`AddFunctionTopLevel`](AddFunctionTopLevel) | Top-level-statements function behind the API Gateway emulator (the [Quick Start](../README.md#quick-start)). | Lambda + API Gateway (`HttpV2`) | +| [`AddFunctionClassLibrary`](AddFunctionClassLibrary) | A class-library function with a pre-filled `Executable` launch profile. | Lambda + API Gateway (`HttpV2`) | +| [`SQSProcessor`](SQSProcessor) | An `SQSEvent` handler, testable via the SQS event source or the built-in `sqs.json` sample event. | Lambda (+ optional SQS event source) | +| [`ToUpperFunction`](ToUpperFunction) | A minimal, zero-dependency function for exploring the web UI and sample events. | Lambda only | + +## Prerequisites + +- .NET 8 SDK or later. +- The test tool installed: `dotnet tool install -g amazon.lambda.testtool` (see the [main README](../README.md#prerequisites)). + +Start with [`AddFunctionTopLevel`](AddFunctionTopLevel) if you're new — it's the lowest-friction path from install to a working invocation. diff --git a/Tools/LambdaTestTool-v2/samples/SQSProcessor/Program.cs b/Tools/LambdaTestTool-v2/samples/SQSProcessor/Program.cs new file mode 100644 index 000000000..aa770b177 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/SQSProcessor/Program.cs @@ -0,0 +1,21 @@ +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; +using Amazon.Lambda.SQSEvents; + +// Processes a batch of SQS messages, logging each message body. +// Test it either with the built-in "sqs.json" sample event from the web UI, +// or by wiring a real queue with --sqs-eventsource-config (see this sample's README). +var handler = (SQSEvent evnt, ILambdaContext context) => +{ + foreach (var message in evnt.Records) + { + context.Logger.LogLine($"Processing message {message.MessageId}: {message.Body}"); + } + + context.Logger.LogLine($"Processed {evnt.Records.Count} message(s)."); +}; + +await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/samples/SQSProcessor/Properties/launchSettings.json b/Tools/LambdaTestTool-v2/samples/SQSProcessor/Properties/launchSettings.json new file mode 100644 index 000000000..16b3a27fd --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/SQSProcessor/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "SQSProcessor": { + "commandName": "Project", + "environmentVariables": { + "AWS_LAMBDA_RUNTIME_API": "localhost:5050/SQSProcessor" + } + } + } +} diff --git a/Tools/LambdaTestTool-v2/samples/SQSProcessor/README.md b/Tools/LambdaTestTool-v2/samples/SQSProcessor/README.md new file mode 100644 index 000000000..ebce2591b --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/SQSProcessor/README.md @@ -0,0 +1,39 @@ +# SQSProcessor + +An `SQSEvent` handler that logs each message body. Demonstrates the shape of an SQS-triggered Lambda and the [SQS event source](../../README.md#sqs-event-source). You can test it two ways. + +## Option A: With the built-in sample event (no AWS needed) + +**1. Start the Lambda emulator:** + +``` +dotnet lambda-test-tool start --lambda-emulator-port 5050 +``` + +**2. Start the function** (separate terminal, this directory): + +``` +dotnet run --launch-profile SQSProcessor +``` + +**3. In the web UI** (`http://localhost:5050`), select `SQSProcessor`, choose the built-in **`sqs.json`** sample from the Example Requests dropdown, and click **Invoke**. The message body is logged in the function's console. + +## Option B: With a real SQS queue (event source polling) + +This polls an actual queue using your AWS credentials. + +**1. Start the function** (separate terminal, this directory): + +``` +dotnet run --launch-profile SQSProcessor +``` + +**2. Start the test tool with the SQS event source** pointed at your queue: + +``` +dotnet lambda-test-tool start \ + --lambda-emulator-port 5050 \ + --sqs-eventsource-config "QueueUrl=https://sqs..amazonaws.com//,FunctionName=SQSProcessor,Region=" +``` + +Send a message to the queue; the tool batches it into an `SQSEvent`, invokes `SQSProcessor`, and (on success) deletes the message. See the [main README](../../README.md#sqs-event-source) for all supported config keys. diff --git a/Tools/LambdaTestTool-v2/samples/SQSProcessor/SQSProcessor.csproj b/Tools/LambdaTestTool-v2/samples/SQSProcessor/SQSProcessor.csproj new file mode 100644 index 000000000..2311aaebd --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/SQSProcessor/SQSProcessor.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + Lambda + SQSProcessor + + + + + + + + + + diff --git a/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Program.cs b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Program.cs new file mode 100644 index 000000000..5b99b40ba --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Program.cs @@ -0,0 +1,21 @@ +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; + +// A minimal function: uppercases the input string. +// Send the string "error" to see how the tool renders a thrown exception. +var handler = (string input, ILambdaContext context) => +{ + context.Logger.LogLine($"Executing function with input: {input}"); + + if (string.Equals("error", input, StringComparison.OrdinalIgnoreCase)) + { + throw new Exception("Forced error to demonstrate error rendering."); + } + + return input?.ToUpper(); +}; + +await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Properties/launchSettings.json b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Properties/launchSettings.json new file mode 100644 index 000000000..eaed33dda --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "ToUpperFunction": { + "commandName": "Project", + "environmentVariables": { + "AWS_LAMBDA_RUNTIME_API": "localhost:5050/ToUpperFunction" + } + } + } +} diff --git a/Tools/LambdaTestTool-v2/samples/ToUpperFunction/README.md b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/README.md new file mode 100644 index 000000000..55b7f1bdf --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/README.md @@ -0,0 +1,27 @@ +# ToUpperFunction + +The gentlest sample: a zero-dependency function that uppercases its input string. Good for a first look at the [web UI](../../README.md#using-the-web-ui) — no API Gateway or AWS resources needed. + +## Run it + +**1. Start the Lambda emulator:** + +``` +dotnet lambda-test-tool start --lambda-emulator-port 5050 +``` + +The web UI opens at `http://localhost:5050`. + +**2. Start the function** (separate terminal, this directory): + +``` +dotnet run --launch-profile ToUpperFunction +``` + +**3. Invoke it from the web UI:** + +1. Select `ToUpperFunction` as the function. +2. In the Function Input editor, enter a JSON string, e.g. `"hello world"`. +3. Click **Invoke**. The response is `"HELLO WORLD"`. + +Try `"error"` as the input to see how the tool renders a thrown exception and stack trace. diff --git a/Tools/LambdaTestTool-v2/samples/ToUpperFunction/ToUpperFunction.csproj b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/ToUpperFunction.csproj new file mode 100644 index 000000000..878aff8f8 --- /dev/null +++ b/Tools/LambdaTestTool-v2/samples/ToUpperFunction/ToUpperFunction.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + Lambda + ToUpperFunction + + + + + + + + +