Skip to content

feat(testing): add MinimalLambda.Testing package#233

Merged
j-d-ha merged 134 commits into
mainfrom
feature/#185-add-testing-package
Dec 13, 2025
Merged

feat(testing): add MinimalLambda.Testing package#233
j-d-ha merged 134 commits into
mainfrom
feature/#185-add-testing-package

Conversation

@j-d-ha

@j-d-ha j-d-ha commented Dec 13, 2025

Copy link
Copy Markdown
Collaborator

🚀 Pull Request

📋 Summary

This PR introduces the new MinimalLambda.Testing package, providing a comprehensive testing infrastructure for MinimalLambda-based applications. The package enables integration testing of Lambda functions with full lifecycle simulation, dependency injection testing, and support for various Lambda handler patterns.

Key Features:

  • LambdaApplicationFactory - Factory for creating test hosts based on ASP.NET Core testing patterns
  • LambdaTestServer - In-memory test server that simulates the AWS Lambda Runtime API
  • DeferredHostBuilder - Support for deferred host configuration
  • Support for multiple .NET framework versions (net8.0, net9.0)
  • Comprehensive unit tests with multiple test Lambda applications
  • Full XML documentation

Testing Infrastructure:

  • Created 4 test Lambda applications (SimpleLambda, DiLambda, NoEventLambda, NoResponseLambda)
  • Added extensive unit tests covering various scenarios:
    • Request/response handling
    • Dependency injection with multiple DI containers (Autofac support)
    • Lifecycle management and shutdown scenarios
    • Error handling and propagation
    • Cancellation token support
    • Concurrent invocations

Additional Changes:

  • Updated .editorconfig and added .csharpierignore
  • Added THIRD-PARTY-LICENSES.txt for attribution
  • Enhanced exception handling in LambdaHostedService
  • Added DefaultLambdaJsonSerializerOptions to abstractions
  • Updated dependency versions and package references

✅ Checklist

  • My changes build cleanly
  • I've added/updated relevant tests
  • I've added/updated documentation or README
  • I've followed the coding style for this project
  • I've tested the changes locally (if applicable)

🧪 Related Issues or PRs

Closes #185


💬 Notes for Reviewers

This is a substantial addition (~3,580 lines added) that establishes the foundation for testing MinimalLambda applications. The implementation is based on ASP.NET Core's WebApplicationFactory pattern but adapted for Lambda's unique runtime model.

Key areas to review:

  • LambdaTestServer transaction-based architecture for managing request/response cycles
  • HostFactoryResolver content root resolution logic (adapted from ASP.NET Core)
  • Exception handling in host lifecycle management
  • Thread safety in concurrent invocation scenarios

The package includes comprehensive unit tests demonstrating various usage patterns and edge cases.

- Added a new project `AwsLambda.Host.Testing` to support unit testing for AwsLambda.Host.
- Configured multi-targeting for .NET 8.0, 9.0, and 10.0 with preview language features.
- Included necessary metadata for NuGet packaging, including README file support.
- Updated solution file to include the new project.
- Introduced new example project to demonstrate testing with AwsLambda.Host.
- Configured the project to target .NET 10.0 with appropriate properties and metadata.
- Added basic Lambda handler implementation with a `name` parameter.
- Included `appsettings.json` for logging configuration.
- Configured `launchSettings.json` for local testing with mock runtime.
- Updated solution to include the new example project.
…ing setup

- Added `LambdaHostTest` class with a sample test for validating Lambda host functionality.
- Configured necessary testing dependencies like xUnit, NSubstitute, and coverlet.collector.
- Included `appsettings.json` with `PreserveNewest` copy directive for output directory.
- Introduced `DeferredHostBuilder` to enable deferred execution of IHostBuilder configuration.
- Added support for capturing and replaying configuration calls with `ConfigureHostBuilder`.
- Implemented `DeferredHost` to manage deferred host startup and ensure proper disposal.
- Linked implementation with MIT-licensed parts from aspnetcore, providing proper attribution.
- Introduced `LambdaApplicationFactory` for managing and testing Lambda application lifecycle.
- Implemented proper disposal handling with `Dispose` and `DisposeAsync` methods.
- Ensured compatibility with `IHost` for seamless integration and teardown.
- Added detailed examples for AWS Lambda local invocation happy and error paths.
- Included formatted HTTP request and response content for better understanding.
- Documented the error handling flow, including stack traces and error serialization.
- Added attribution to aspnetcore's `WebApplicationFactory.cs` under the MIT license.
- Included reference to the source repository and license file for transparency.
- Updated header comments in `LambdaApplicationFactory.cs` for proper acknowledgment.
- Introduced `LoggingHttpHandler` for detailed HTTP request and response logging.
- Updated `Program.cs` to configure `BootstrapHttpClient` using `LoggingHttpHandler`.
- Enabled `ClearLambdaOutputFormatting` for better Lambda output readability.
- Changed version extraction from `<VersionPrefix>` to `<Version>` in `Directory.Build.props`.
- Updated error message to reflect the new version tag.
- Added `HostFactoryResolver`, copied and adapted from .NET runtime's implementation.
- Enables dynamic resolution and creation of `IHost` and `IServiceProvider` for testing scenarios.
- Included proper licensing attribution in the file header.
- Ensures compatibility with diagnostic event-based host building patterns.
- Updated copyright notices to include ".NET Foundation and Contributors" in relevant files.
- Added new entry for the .NET runtime license to THIRD-PARTY-LICENSES.txt.
- Ensured consistency in licensing attribution across files for proper acknowledgment.
- Added `.csharpierignore` to exclude specific files from formatting checks.
- Updated `.editorconfig` to disable formatting for `HostFactoryResolver.cs`.
- Included `.csharpierignore` and `.editorconfig` in the solution file (`AwsLambda.Host.sln`).
…stingHttpHandler

- Introduced `LambdaApplicationFactoryClientOptions` for customizing test client configurations.
- Added `LambdaTestingHttpHandler` for handling HTTP-specific test scenarios.
- Extended `LambdaApplicationFactory` to improve support for Lambda app testing workflows.
- Replaced IWebHostBuilder with IHostBuilder across LambdaApplicationFactory.
- Updated documentation to reflect the use of IHostBuilder methods and patterns.
- Simplified host configuration by deprecating IWebHostBuilder-specific logic.
- Added `Microsoft.AspNetCore.Routing` as a package reference in the Testing project.
- Updated `Directory.Packages.props` to include `Microsoft.AspNetCore.Routing` version 2.3.0.
…data

- Introduced `ErrorResponse` model for consistent error representation in testing scenarios.
- Added nested `ErrorCause` class to handle detailed error information with potential nested causes.
- Included JSON serialization attributes for seamless JSON handling.
…ementation

- Introduced `LambdaRuntimeRouteManager` and `ILambdaRuntimeRouteManager` for route matching logic.
- Implemented support for matching HTTP requests to specific Lambda runtime routes.
- Defined route templates for `/invocation/next`, `/invocation/{requestId}/response`, and `/invocation/{requestId}/error`.
- Utilized `TemplateMatcher` for parsing and matching route templates.
…fication

- Introduced `RequestType` enum with values: `GetNextInvocation`, `PostResponse`, and `PostError`.
- Enables clear categorization of Lambda runtime request types in testing scenarios.
- Introduced `InvocationResponse<TResponse>` for modeling Lambda invocation outcomes.
- Added `WasSuccess`, `Response`, and `Error` properties for detailed testing scenarios.
- Introduced `LambdaBootstrapRequest` model to encapsulate Lambda runtime request data.
- Added `RequestType`, `RequestMessage`, and `RouteValue` properties as required fields.
- Implemented `RequestId` property using `RouteValueDictionary` for request identification.
- Introduced `LambdaClient` to handle Lambda runtime requests in testing scenarios.
- Implemented `WaitForBootstrapAsync` to process bootstrap-specific requests.
- Added `WaitForRequestAsync` with support for route matching using `ILambdaRuntimeRouteManager`.
- Included `Invoke<TEvent, TResponse>` method for serializing events and initiating invocation responses.
… channel support

- Added constructor to `LambdaTestingHttpHandler` accepting request and response channels.
- Implemented `SendAsync` to pass requests to `requestChannel` and receive responses from `responseChannel`.
- Replaced hardcoded "2018-06-01" route version with a dynamic "{version}" placeholder.
- Updated route templates for `GetNextInvocation`, `PostResponse`, and `PostError` requests.
- Improved flexibility and compatibility for handling versioned Lambda runtime paths.
…e handling

- Introduced `LambdaClientOptions` to configure response headers for Lambda invocations.
- Added `LambdaInvocationHeaders` to customize attributes like date, chunked transfer, and AWS metadata.
- Enhanced `Invoke` method to return a mocked `HttpResponseMessage` with serialized invocation data.
- Added `LambdaClientOptions` as a configurable option for `LambdaClient`.
- Implemented `ConfigureOptions` method to allow runtime customization of `LambdaClientOptions`.
- Ensured null-check for `configureOptions` to prevent potential null reference exceptions.
…` method in `LambdaClient`

- Moved request creation code from `Invoke<TEvent, TResponse>` to a new private method `CreateRequest<TEvent>`.
- Simplified `Invoke` by reusing the `CreateRequest` method.
- Improved maintainability and readability by centralizing HTTP response setup logic.
- Updated to use values from `LambdaClientOptions.InvocationHeaders` for response headers.
…and client configuration

- Introduced `LambdaInvocationHeaderOptions` to define additional headers, metadata, and timeouts.
- Added `LambdaClientOptions` to encapsulate and configure invocation header options.
…ient options

- Reorganized `LambdaClientOptions` within `LambdaClient` for clarity and proper initialization.
- Refactored response header setup to use `InvocationHeaderOptions` for better flexibility.
- Added support for calculating dynamic `DeadlineMs` header based on function timeout.
- Enabled adding custom additional headers via `InvocationHeaderOptions.AdditionalHeaders`.
…in LambdaClient

- Replaced `RequestId` with `InvocationCounter` to generate zero-padded 12-digit request IDs.
- Incremented `InvocationCounter` after each invocation to ensure unique request IDs.
- Renamed `Invoke` to `InvokeAsync` for consistency with asynchronous method naming conventions.
- Renamed `WaitForBootstrapAsync` to `WaitForNextRequestAsync` for improved clarity.
- Refactored `Invoke` response logic to handle success and error scenarios more robustly.
- Added support for serializing invocation responses including success status and error details.
- Simplified the invocation flow to improve readability and maintainability.
j-d-ha and others added 5 commits December 13, 2025 09:51
- Introduced `SimpleLambda_WorksWhenInvokeCalledMultipleTimes_WithoutStart` unit test.
- Validated concurrent invocations with expected responses in order.
- Ensured successful execution without requiring initial `Start`.
- Replaced hardcoded exception messages with interpolated strings using `nameof(LambdaTestServer)`.
- Enhanced clarity by including current and expected server states in specific exceptions.
- Ensured consistency and maintainability of error messages throughout the code.
- Introduced `InitAlreadyCompleted` to `InitStatus` for improved state representation.
- Updated `StartAsync` to return `InitAlreadyCompleted` when already initialized.
- Modified state checks to handle `InitAlreadyCompleted` in initialization flow.
- Adjusted error handling logic for enhanced clarity and accurate status reporting.
@j-d-ha

j-d-ha commented Dec 13, 2025

Copy link
Copy Markdown
Collaborator Author

@codex review

@codecov

codecov Bot commented Dec 13, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.86755% with 152 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../MinimalLambda.Testing/LambdaApplicationFactory.cs 68.26% 52 Missing and 14 partials ⚠️
src/MinimalLambda.Testing/LambdaTestServer.cs 83.52% 25 Missing and 18 partials ⚠️
...ng/LambdaApplicationFactoryContentRootAttribute.cs 0.00% 23 Missing ⚠️
src/MinimalLambda.Testing/DeferredHostBuilder.cs 86.15% 8 Missing and 1 partial ⚠️
src/MinimalLambda.Testing/LambdaHttpHandler.cs 82.75% 5 Missing ⚠️
src/MinimalLambda.Testing/TaskHelpers.cs 86.20% 2 Missing and 2 partials ⚠️
...MinimalLambda.Testing/LambdaRuntimeRouteManager.cs 98.14% 0 Missing and 1 partial ⚠️
src/MinimalLambda.Testing/Models/ErrorResponse.cs 88.88% 1 Missing ⚠️

❌ Your patch status has failed because the patch coverage (79.86%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #233      +/-   ##
==========================================
- Coverage   89.61%   87.91%   -1.70%     
==========================================
  Files         104      122      +18     
  Lines        2128     2864     +736     
  Branches      253      334      +81     
==========================================
+ Hits         1907     2518     +611     
- Misses        129      216      +87     
- Partials       92      130      +38     
Files with missing lines Coverage Δ
...ions/Options/DefaultLambdaJsonSerializerOptions.cs 100.00% <100.00%> (ø)
...imalLambda.Abstractions/Options/EnvelopeOptions.cs 100.00% <100.00%> (+5.88%) ⬆️
src/MinimalLambda.Testing/DictionaryExtensions.cs 100.00% <100.00%> (ø)
...inimalLambda.Testing/LambdaTestServerExtensions.cs 100.00% <100.00%> (ø)
src/MinimalLambda.Testing/Models/InitResponse.cs 100.00% <100.00%> (ø)
...nimalLambda.Testing/Models/InvocationCompletion.cs 100.00% <100.00%> (ø)
...MinimalLambda.Testing/Models/InvocationResponse.cs 100.00% <100.00%> (ø)
...inimalLambda.Testing/Models/InvocationResponseT.cs 100.00% <100.00%> (ø)
...imalLambda.Testing/Models/LambdaHttpTransaction.cs 100.00% <100.00%> (ø)
.../MinimalLambda.Testing/Models/PendingInvocation.cs 100.00% <100.00%> (ø)
... and 10 more

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2c0f99d...3ed0672. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/MinimalLambda.Testing/LambdaTestServer.cs Outdated
Comment thread src/MinimalLambda.Testing/LambdaTestServer.cs
- Introduced new extension methods for `LambdaTestServer` to simplify invocation patterns.
  - `InvokeAsync<TEvent, TResponse>`: Simplifies invoking Lambda with an event and expecting a response.
  - `InvokeNoEventAsync<TResponse>`: Allows invocation without providing an event.
  - `InvokeNoResponseAsync<TEvent>`: Enables invocation with event but no response expectation.
- Added comprehensive XML documentation to assist with usage and clarify behavior.
- Improved usability by wrapping `LambdaTestServer.InvokeAsync` for common scenarios.
- Extracted `InvocationResponse<TResponse>` from `InvocationResponse` to enable typed responses.
- Updated `InvocationResponse` to serve as the non-generic base class.
- Added XML documentation for improved clarity of usage and behavior.
- Marked all mutable properties on `InvocationResponse` classes with `init` for immutability.
- Replaced switch-case disposal logic with direct calls to `StopAsync` and `Dispose`.
- Improved code clarity by replacing null check with `is not null`.
- Ensured consistent async disposal flow for `_host` and `_server`.
…licationFactory`

- Updated LINQ query to use discard (`_`) for unused `projects` variable.
- Improved code readability and reduced unnecessary variable declaration.
…unit tests

- Replaced all instances of `WithCancelationToken` with `WithCancellationToken`.
- Ensured consistency in method naming throughout unit test implementations.
- Updated XML documentation in `LambdaApplicationFactory` to reflect the corrected method name.
….WithHostBuilder`

- Updated XML documentation to reference `LambdaApplicationFactory{TEntryPoint}.WithHostBuilder`.
- Removed redundant `<see>` reference for `CreateHostBuilder` in the remarks section.
- Enhanced documentation clarity by aligning with standardized conventions.
… unused instance reference

- Updated `LambdaRuntimeRouteManager` class to be static.
- Removed unused `_routeManager` instance from `LambdaTestServer`.
- Refactored code to use `LambdaRuntimeRouteManager.TryMatch` as a static method.
… `ErrorResponse`

- Added `Causes` property to represent underlying causes in errors.
- Updated `ErrorType` property with `required` modifier for stricter validation.
- Included XML documentation for the new `Causes` property to enhance clarity.
…xtensionsTest`

- Replaced explicit type declaration with target-typed `new()` for `_dictionary`.
- Improved code readability and adhered to modern C# conventions.
- Added `_pendingInvocations.TryRemove` call to ensure request ID is removed after processing.
- Prevents potential memory leaks and ensures proper cleanup of pending invocations.
…`StartAsync`

- Added a check to ensure `_startSemaphore.Release` is only called if the semaphore was acquired.
- Prevents potential release errors when the semaphore was not successfully acquired.
- Moved `StopAsync` call in `LambdaTestServer.DisposeAsync` to align with proper server state handling.
- Replaced `StartAsync` with synchronous `_host.Start` to prevent unnecessary token usage.
- Updated `WhenAll` call in `StopAsync` to handle null `_processingTask` safely.
- Simplified host disposal in `LambdaApplicationFactory` by removing redundant `StopAsync`.
… inline

- Removed `_server` instance and replaced with inline factory instance in `SimpleLambdaTests`.
- Improved resource management by using `await using` for factory disposal.
- Enhanced code maintainability by avoiding explicit constructor setup for factory.
…ests`

- Replaced `_server` field with inline `LambdaApplicationFactory` instances.
- Improved resource management using `await using` for factory disposal.
- Simplified test setup by avoiding a constructor-based factory initialization.
…d `NoEventLambdaTests`

- Replaced `_server` field with inline `LambdaApplicationFactory` instances.
- Enhanced resource management by utilizing `await using` for factory disposal.
- Simplified test setup by removing constructor-based factory initialization.
- Replaced `_host.Start()` with `await _host.StartAsync(cts.Token)` for asynchronous operation.
- Ensures proper task handling and respects cancellation tokens during host startup.
…stApplicationLifetime`

- Ensures `IHostApplicationLifetime` is resolved reliably during host startup.
- Removes `AssemblyInfo.cs`, disabling test parallelization.
…explicitly

- Added linked cancellation token source with timeout for `DiLambdaTests` to enhance reliability.
- Updated `LambdaApplicationFactory` and `LambdaTestServer` to use `GetRequiredService` for resolving `IHostApplicationLifetime`.
- Refactored `DeferredHost` to explicitly pass `IHostApplicationLifetime` and avoid redundant retrievals.
- Simplified task completion logic in `DeferredHost` to ensure proper exception handling and cleanup.
@sonarqubecloud

Copy link
Copy Markdown

@j-d-ha
j-d-ha merged commit 7c0129e into main Dec 13, 2025
7 of 8 checks passed
@j-d-ha
j-d-ha deleted the feature/#185-add-testing-package branch December 13, 2025 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(testing): add AwsLambda.Host.Testing package for integration testing

2 participants