diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayHttpApiV2ProxyRequestTranslator.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayHttpApiV2ProxyRequestTranslator.cs deleted file mode 100644 index 0892c923c..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayHttpApiV2ProxyRequestTranslator.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; -using static Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest; - -namespace Amazon.Lambda.TestTool -{ - /// - /// Translates ASP.NET Core HTTP requests to API Gateway HTTP API v2 Proxy Requests. - /// - public class ApiGatewayHttpApiV2ProxyRequestTranslator : IApiGatewayRequestTranslator - { - private readonly IHttpRequestUtility _httpRequestUtility; - - /// - /// Initializes a new instance of the class. - /// - /// The HTTP request utility used for extracting request details. - public ApiGatewayHttpApiV2ProxyRequestTranslator(IHttpRequestUtility httpRequestUtility) - { - _httpRequestUtility = httpRequestUtility; - } - - /// - /// Translates an ASP.NET Core HTTP request to an API Gateway HTTP API v2 Proxy Request. - /// - /// The ASP.NET Core HTTP request to translate. - /// The path parameters extracted from the request URL. - /// The API Gateway resource path. - /// An object representing the translated request. - public object TranslateFromHttpRequest(HttpRequest request, IDictionary pathParameters, string resource) - { - var (headers, _) = _httpRequestUtility.ExtractHeaders(request.Headers); - var (queryStringParameters, _) = _httpRequestUtility.ExtractQueryStringParameters(request.Query); - - var httpApiV2ProxyRequest = new APIGatewayHttpApiV2ProxyRequest - { - RouteKey = $"{request.Method} {resource}", - RawPath = request.Path, - RawQueryString = request.QueryString.Value, - Cookies = request.Cookies.Select(c => $"{c.Key}={c.Value}").ToArray(), - Headers = headers, - QueryStringParameters = queryStringParameters, - PathParameters = pathParameters ?? new Dictionary(), - Body = _httpRequestUtility.ReadRequestBody(request), - IsBase64Encoded = false, - RequestContext = new ProxyRequestContext - { - Http = new HttpDescription - { - Method = request.Method, - Path = request.Path, - Protocol = request.Protocol - }, - RouteKey = $"{request.Method} {resource}" - }, - Version = "2.0" - }; - - if (_httpRequestUtility.IsBinaryContent(request.ContentType)) - { - httpApiV2ProxyRequest.Body = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(httpApiV2ProxyRequest.Body)); - httpApiV2ProxyRequest.IsBase64Encoded = true; - } - - return httpApiV2ProxyRequest; - } - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayMode.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayMode.cs deleted file mode 100644 index 5979621ae..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Amazon.Lambda.TestTool -{ - /// - /// Represents the different API Gateway modes. - /// - public enum ApiGatewayMode - { - /// - /// Represents the REST API Gateway mode. - /// - REST, - - /// - /// Represents the HTTP API v1 Gateway mode. - /// - HTTPV1, - - /// - /// Represents the HTTP API v2 Gateway mode. - /// - HTTPV2 - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayProxyRequestTranslator.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayProxyRequestTranslator.cs deleted file mode 100644 index 4053645e2..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayProxyRequestTranslator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using System; -using System.Collections.Generic; -using System.Text; -using System.Web; -using Microsoft.AspNetCore.Http; - -namespace Amazon.Lambda.TestTool -{ - /// - /// Translates ASP.NET Core HTTP requests to API Gateway Proxy Requests for REST API and HTTP API v1. - /// - public class ApiGatewayProxyRequestTranslator : IApiGatewayRequestTranslator - { - private readonly IHttpRequestUtility _httpRequestUtility; - - /// - /// Initializes a new instance of the class. - /// - /// The HTTP request utility used for extracting request details. - public ApiGatewayProxyRequestTranslator(IHttpRequestUtility httpRequestUtility) - { - _httpRequestUtility = httpRequestUtility; - } - - /// - /// Translates an ASP.NET Core HTTP request to an API Gateway Proxy Request. - /// - /// The ASP.NET Core HTTP request to translate. - /// The path parameters extracted from the request URL. - /// The API Gateway resource path. - /// An object representing the translated request. - public object TranslateFromHttpRequest(HttpRequest request, IDictionary pathParameters, string resource) - { - var (headers, multiValueHeaders) = _httpRequestUtility.ExtractHeaders(request.Headers); - var (queryStringParameters, multiValueQueryStringParameters) = _httpRequestUtility.ExtractQueryStringParameters(request.Query); - - var proxyRequest = new APIGatewayProxyRequest - { - Resource = resource, - Path = HttpUtility.UrlEncode(request.Path), - HttpMethod = request.Method, - Headers = headers, - MultiValueHeaders = multiValueHeaders, - QueryStringParameters = queryStringParameters, - MultiValueQueryStringParameters = multiValueQueryStringParameters, - PathParameters = pathParameters ?? new Dictionary(), - Body = _httpRequestUtility.ReadRequestBody(request), - IsBase64Encoded = false - }; - - if (_httpRequestUtility.IsBinaryContent(request.ContentType)) - { - proxyRequest.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(proxyRequest.Body)); - proxyRequest.IsBase64Encoded = true; - } - - return proxyRequest; - } - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRequestTranslatorFactory.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRequestTranslatorFactory.cs deleted file mode 100644 index 72d93034f..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRequestTranslatorFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Amazon.Lambda.TestTool -{ - - /// - /// Factory class for creating API Gateway request translators based on the specified API Gateway mode. - /// - public class ApiGatewayRequestTranslatorFactory : IApiGatewayRequestTranslatorFactory - { - private readonly IServiceProvider _serviceProvider; - - /// - /// Initializes a new instance of the class. - /// - /// The service provider used to resolve dependencies. - public ApiGatewayRequestTranslatorFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - /// - /// Creates an API Gateway request translator based on the specified API Gateway mode. - /// - /// The API Gateway mode. - /// An instance of appropriate for the specified mode. - public IApiGatewayRequestTranslator Create(ApiGatewayMode apiGatewayMode) - { - return apiGatewayMode switch - { - ApiGatewayMode.REST => _serviceProvider.GetRequiredService(), - ApiGatewayMode.HTTPV1 => _serviceProvider.GetRequiredService(), - ApiGatewayMode.HTTPV2 => _serviceProvider.GetRequiredService(), - _ => throw new NotImplementedException() - }; - } - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRouteConfig.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRouteConfig.cs new file mode 100644 index 000000000..36e932f87 --- /dev/null +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/ApiGatewayRouteConfig.cs @@ -0,0 +1,10 @@ +namespace Amazon.Lambda.TestTool +{ + public class ApiGatewayRouteConfig + { + public string LambdaResourceName { get; set; } + public string Endpoint { get; set; } + public string HttpMethod { get; set; } + public string Path { get; set; } + } +} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/HttpContextExtensions.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/HttpContextExtensions.cs new file mode 100644 index 000000000..c557a8d7b --- /dev/null +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/HttpContextExtensions.cs @@ -0,0 +1,114 @@ +using Amazon.Lambda.APIGatewayEvents; +using System.Text; +using System.Web; +using static Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest; + +namespace Amazon.Lambda.TestTool +{ + /// + /// Provides extension methods to translate an to different types of API Gateway requests. + /// + public static class HttpContextExtensions + { + private static IHttpRequestUtility _httpRequestUtility = new HttpRequestUtility(); + private static IRouteConfigurationParser _routeConfigurationParser; + + public static void SetHttpRequestUtility(IHttpRequestUtility httpRequestUtility) + { + _httpRequestUtility = httpRequestUtility ?? throw new ArgumentNullException(nameof(httpRequestUtility)); + } + + public static void SetRouteConfigurationParser(IRouteConfigurationParser routeConfigurationParser) + { + _routeConfigurationParser = routeConfigurationParser ?? throw new ArgumentNullException(nameof(routeConfigurationParser)); + } + + /// + /// Translates an to an . + /// + /// The to be translated. + /// An object representing the translated request. + public static APIGatewayHttpApiV2ProxyRequest ToApiGatewayHttpV2Request( + this HttpContext context) + { + var request = context.Request; + + var matchedConfig = _routeConfigurationParser.GetRouteConfig(request.Method, request.Path); + var pathParameters = _routeConfigurationParser.ExtractPathParameters(matchedConfig, request.Path); + + var (headers, _) = _httpRequestUtility.ExtractHeaders(request.Headers); + var (queryStringParameters, _) = _httpRequestUtility.ExtractQueryStringParameters(request.Query); + + var httpApiV2ProxyRequest = new APIGatewayHttpApiV2ProxyRequest + { + RouteKey = $"{request.Method} {matchedConfig.Path}", + RawPath = request.Path, + RawQueryString = request.QueryString.Value, + Cookies = request.Cookies.Select(c => $"{c.Key}={c.Value}").ToArray(), + Headers = headers, + QueryStringParameters = queryStringParameters, + PathParameters = pathParameters ?? new Dictionary(), + Body = _httpRequestUtility.ReadRequestBody(request), + IsBase64Encoded = false, + RequestContext = new ProxyRequestContext + { + Http = new HttpDescription + { + Method = request.Method, + Path = request.Path, + Protocol = request.Protocol + }, + RouteKey = $"{request.Method} {matchedConfig.Path}" + }, + Version = "2.0" + }; + + if (_httpRequestUtility.IsBinaryContent(request.ContentType)) + { + httpApiV2ProxyRequest.Body = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(httpApiV2ProxyRequest.Body)); + httpApiV2ProxyRequest.IsBase64Encoded = true; + } + + return httpApiV2ProxyRequest; + } + + /// + /// Translates an to an . + /// + /// The to be translated. + /// An object representing the translated request. + public static APIGatewayProxyRequest ToApiGatewayRequest( + this HttpContext context) + { + var request = context.Request; + + var matchedConfig = _routeConfigurationParser.GetRouteConfig(request.Method, request.Path); + var pathParameters = _routeConfigurationParser.ExtractPathParameters(matchedConfig, request.Path); + + var (headers, multiValueHeaders) = _httpRequestUtility.ExtractHeaders(request.Headers); + var (queryStringParameters, multiValueQueryStringParameters) = _httpRequestUtility.ExtractQueryStringParameters(request.Query); + + var proxyRequest = new APIGatewayProxyRequest + { + Resource = matchedConfig.Path, + Path = HttpUtility.UrlEncode(request.Path), + HttpMethod = request.Method, + Headers = headers, + MultiValueHeaders = multiValueHeaders, + QueryStringParameters = queryStringParameters, + MultiValueQueryStringParameters = multiValueQueryStringParameters, + PathParameters = pathParameters ?? new Dictionary(), + Body = _httpRequestUtility.ReadRequestBody(request), + IsBase64Encoded = false + }; + + if (_httpRequestUtility.IsBinaryContent(request.ContentType)) + { + proxyRequest.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(proxyRequest.Body)); + proxyRequest.IsBase64Encoded = true; + } + + return proxyRequest; + } + } +} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslator.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslator.cs deleted file mode 100644 index efec2951a..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslator.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Amazon.Lambda.TestTool -{ - /// - /// Defines the contract for translating an ASP.NET Core HTTP request to an API Gateway proxy request. - /// - public interface IApiGatewayRequestTranslator - { - /// - /// Translates an ASP.NET Core HTTP request to an API Gateway proxy request. - /// - /// The ASP.NET Core HTTP request to translate. - /// The path parameters extracted from the request URL. For example, if the resource is "/users/{userId}/orders/{orderId}" and the actual path is "/users/123/orders/456", then pathParameters would be { "userId": "123", "orderId": "456" }. - /// The API Gateway resource path. This is the template path defined in API Gateway, e.g., "/users/{userId}/orders/{orderId}". - /// An object representing the API Gateway proxy request (either APIGatewayProxyRequest or APIGatewayHttpApiV2ProxyRequest). - /// - /// For a request to "https://api.example.com/users/123/orders/456?status=pending": - /// - request: The HttpRequest object representing this request - /// - pathParameters: { "userId": "123", "orderId": "456" } - /// - resource: "/users/{userId}/orders/{orderId}" - /// - object TranslateFromHttpRequest(HttpRequest request, IDictionary pathParameters, string resource); - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslatorFactory.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslatorFactory.cs deleted file mode 100644 index 227ba584e..000000000 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IApiGatewayRequestTranslatorFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Amazon.Lambda.TestTool -{ - /// - /// Defines the contract for a factory that creates API Gateway request translators. - /// - public interface IApiGatewayRequestTranslatorFactory - { - /// - /// Creates an API Gateway request translator based on the specified API Gateway mode. - /// - /// The API Gateway mode. - /// An instance of appropriate for the specified mode. - IApiGatewayRequestTranslator Create(ApiGatewayMode apiGatewayMode); - } -} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IRouteConfigurationParser.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IRouteConfigurationParser.cs new file mode 100644 index 000000000..066dd4f12 --- /dev/null +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/IRouteConfigurationParser.cs @@ -0,0 +1,8 @@ +namespace Amazon.Lambda.TestTool +{ + public interface IRouteConfigurationParser + { + ApiGatewayRouteConfig GetRouteConfig(string httpMethod, string path); + IDictionary ExtractPathParameters(ApiGatewayRouteConfig routeConfig, string requestPath); + } +} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayHttpApiV2ProxyRequestTranslatorTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayHttpApiV2ProxyRequestTranslatorTests.cs deleted file mode 100644 index 240301eb8..000000000 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayHttpApiV2ProxyRequestTranslatorTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Xunit; -using Microsoft.AspNetCore.Http; -using System.Collections.Generic; -using Amazon.Lambda.APIGatewayEvents; -using System.IO; -using System.Text; - -namespace Amazon.Lambda.TestTool.UnitTests -{ - public class ApiGatewayHttpApiV2ProxyRequestTranslatorTests - { - private readonly ApiGatewayHttpApiV2ProxyRequestTranslator _translator; - private readonly IHttpRequestUtility _httpRequestUtility; - - public ApiGatewayHttpApiV2ProxyRequestTranslatorTests() - { - _httpRequestUtility = new HttpRequestUtility(); - _translator = new ApiGatewayHttpApiV2ProxyRequestTranslator(_httpRequestUtility); - } - - [Fact] - public void TranslateFromHttpRequest_ShouldReturnValidApiGatewayHttpApiV2ProxyRequest() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "GET"; - request.Scheme = "https"; - request.Host = new HostString("api.example.com"); - request.Path = "/api/users/123/orders"; - request.QueryString = new QueryString("?status=pending"); - request.Headers["User-Agent"] = "TestAgent"; - request.Headers["Accept"] = "application/json"; - request.Headers["Cookie"] = "session=abc123; theme=dark"; - - var pathParameters = new Dictionary { { "userId", "123" } }; - var resource = "/api/users/{userId}/orders"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayHttpApiV2ProxyRequest; - - Assert.NotNull(result); - Assert.Equal("2.0", result.Version); - Assert.Equal($"GET {resource}", result.RouteKey); - Assert.Equal("/api/users/123/orders", result.RawPath); - Assert.Equal("?status=pending", result.RawQueryString); - Assert.Equal(2, result.Cookies.Length); - Assert.Contains("session=abc123", result.Cookies); - Assert.Contains("theme=dark", result.Cookies); - Assert.Equal("TestAgent", result.Headers["User-Agent"]); - Assert.Equal("application/json", result.Headers["Accept"]); - Assert.Equal("pending", result.QueryStringParameters["status"]); - Assert.Equal("123", result.PathParameters["userId"]); - Assert.True(string.IsNullOrEmpty(result.Body)); - Assert.False(result.IsBase64Encoded); - Assert.Equal("GET", result.RequestContext.Http.Method); - Assert.Equal("/api/users/123/orders", result.RequestContext.Http.Path); - Assert.Equal($"GET {resource}", result.RequestContext.RouteKey); - } - - [Fact] - public void TranslateFromHttpRequest_WithBinaryContent_ShouldBase64EncodeBody() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "POST"; - request.Path = "/api/users/123/avatar"; - request.ContentType = "application/octet-stream"; - var bodyContent = new byte[] { 1, 2, 3, 4, 5 }; - request.Body = new MemoryStream(bodyContent); - - var pathParameters = new Dictionary { { "userId", "123" } }; - var resource = "/api/users/{userId}/avatar"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayHttpApiV2ProxyRequest; - - Assert.NotNull(result); - Assert.True(result.IsBase64Encoded); - Assert.Equal(Convert.ToBase64String(bodyContent), result.Body); - Assert.Equal("123", result.PathParameters["userId"]); - Assert.Equal($"POST {resource}", result.RouteKey); - } - [Fact] - public void TranslateFromHttpRequest_WithEmptyPathParameters_ShouldReturnValidRequest() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "GET"; - request.Path = "/api/health"; - - var pathParameters = new Dictionary(); - var resource = "/api/health"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayHttpApiV2ProxyRequest; - - Assert.NotNull(result); - Assert.Empty(result.PathParameters); - Assert.Equal($"GET {resource}", result.RouteKey); - } - } -} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayProxyRequestTranslatorTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayProxyRequestTranslatorTests.cs deleted file mode 100644 index b67c2feea..000000000 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayProxyRequestTranslatorTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Xunit; -using Microsoft.AspNetCore.Http; -using System.Collections.Generic; -using Amazon.Lambda.APIGatewayEvents; -using System.Web; -using System.IO; -using System.Text; - -namespace Amazon.Lambda.TestTool.UnitTests -{ - public class ApiGatewayProxyRequestTranslatorTests - { - private readonly ApiGatewayProxyRequestTranslator _translator; - private readonly IHttpRequestUtility _httpRequestUtility; - - public ApiGatewayProxyRequestTranslatorTests() - { - _httpRequestUtility = new HttpRequestUtility(); - _translator = new ApiGatewayProxyRequestTranslator(_httpRequestUtility); - } - - [Fact] - public void TranslateFromHttpRequest_ShouldReturnValidApiGatewayProxyRequest() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "GET"; - request.Scheme = "https"; - request.Host = new HostString("api.example.com"); - request.Path = "/api/users/123/orders"; - request.QueryString = new QueryString("?status=pending&tag=important&tag=urgent"); - request.Headers["User-Agent"] = "TestAgent"; - request.Headers["Accept"] = new Microsoft.Extensions.Primitives.StringValues(new[] { "text/html", "application/json" }); - request.Headers["Cookie"] = "session=abc123; theme=dark"; - request.Headers["X-Custom-Header"] = "value1"; - - var pathParameters = new Dictionary { { "userId", "123" } }; - var resource = "/api/users/{userId}/orders"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayProxyRequest; - - Assert.NotNull(result); - Assert.Equal(resource, result.Resource); - Assert.Equal(HttpUtility.UrlEncode("/api/users/123/orders"), result.Path); - Assert.Equal("GET", result.HttpMethod); - - // Check single-value headers - Assert.Equal("TestAgent", result.Headers["User-Agent"]); - Assert.Equal("application/json", result.Headers["Accept"]); - Assert.Equal("session=abc123; theme=dark", result.Headers["Cookie"]); - Assert.Equal("value1", result.Headers["X-Custom-Header"]); - - // Check multi-value headers - Assert.Equal(new List { "TestAgent" }, result.MultiValueHeaders["User-Agent"]); - Assert.Equal(new List { "text/html", "application/json" }, result.MultiValueHeaders["Accept"]); - Assert.Equal(new List { "session=abc123; theme=dark" }, result.MultiValueHeaders["Cookie"]); - Assert.Equal(new List { "value1" }, result.MultiValueHeaders["X-Custom-Header"]); - - // Check query string parameters - Assert.Equal("pending", result.QueryStringParameters["status"]); - Assert.Equal("urgent", result.QueryStringParameters["tag"]); - - // Check multi-value query string parameters - Assert.Equal(new List { "pending" }, result.MultiValueQueryStringParameters["status"]); - Assert.Equal(new List { "important", "urgent" }, result.MultiValueQueryStringParameters["tag"]); - - Assert.Equal("123", result.PathParameters["userId"]); - Assert.Equal(string.Empty, result.Body); - Assert.False(result.IsBase64Encoded); - } - - - [Fact] - public void TranslateFromHttpRequest_WithBinaryContent_ShouldBase64EncodeBody() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "POST"; - request.Path = "/api/users/123/avatar"; - request.ContentType = "application/octet-stream"; - var bodyContent = new byte[] { 1, 2, 3, 4, 5 }; - request.Body = new MemoryStream(bodyContent); - - var pathParameters = new Dictionary { { "userId", "123" } }; - var resource = "/api/users/{userId}/avatar"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayProxyRequest; - - Assert.NotNull(result); - Assert.True(result.IsBase64Encoded); - Assert.Equal(Convert.ToBase64String(bodyContent), result.Body); - Assert.Equal("123", result.PathParameters["userId"]); - } - - [Fact] - public void TranslateFromHttpRequest_WithEmptyPathParameters_ShouldReturnValidRequest() - { - var context = new DefaultHttpContext(); - var request = context.Request; - request.Method = "GET"; - request.Path = "/api/health"; - - var pathParameters = new Dictionary(); - var resource = "/api/health"; - - var result = _translator.TranslateFromHttpRequest(request, pathParameters, resource) as APIGatewayProxyRequest; - - Assert.NotNull(result); - Assert.Empty(result.PathParameters); - Assert.Equal(resource, result.Resource); - } - } -} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayRequestTranslatorFactoryTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayRequestTranslatorFactoryTests.cs deleted file mode 100644 index 58b8118f1..000000000 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/ApiGatewayRequestTranslatorFactoryTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Amazon.Lambda.TestTool.UnitTests -{ - using System; - using Xunit; - using Microsoft.Extensions.DependencyInjection; - using Amazon.Lambda.TestTool; - - public class ApiGatewayRequestTranslatorFactoryTests - { - [Fact] - public void Create_WithRESTMode_ReturnsApiGatewayProxyRequestTranslator() - { - var serviceProvider = CreateMockServiceProvider(); - var factory = new ApiGatewayRequestTranslatorFactory(serviceProvider); - - var result = factory.Create(ApiGatewayMode.REST); - - Assert.IsType(result); - } - - [Fact] - public void Create_WithHTTPV1Mode_ReturnsApiGatewayProxyRequestTranslator() - { - var serviceProvider = CreateMockServiceProvider(); - var factory = new ApiGatewayRequestTranslatorFactory(serviceProvider); - - var result = factory.Create(ApiGatewayMode.HTTPV1); - - Assert.IsType(result); - } - - [Fact] - public void Create_WithHTTPV2Mode_ReturnsApiGatewayHttpApiV2ProxyRequestTranslator() - { - var serviceProvider = CreateMockServiceProvider(); - var factory = new ApiGatewayRequestTranslatorFactory(serviceProvider); - - var result = factory.Create(ApiGatewayMode.HTTPV2); - - Assert.IsType(result); - } - - private IServiceProvider CreateMockServiceProvider() - { - var services = new ServiceCollection(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - return services.BuildServiceProvider(); - } - } - -} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpContextExtensionsTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpContextExtensionsTests.cs new file mode 100644 index 000000000..cdd6d57e2 --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpContextExtensionsTests.cs @@ -0,0 +1,189 @@ +using Microsoft.AspNetCore.Http; +using System.Web; +using Xunit; +using Moq; +using System.Text; + +namespace Amazon.Lambda.TestTool.UnitTests +{ + public class HttpContextExtensionsTests + { + private readonly Mock _mockRouteConfigParser; + private readonly Mock _mockHttpRequestUtility; + + public HttpContextExtensionsTests() + { + _mockRouteConfigParser = new Mock(); + _mockHttpRequestUtility = new Mock(); + + _mockRouteConfigParser.Setup(x => x.GetRouteConfig(It.IsAny(), It.IsAny())) + .Returns((string method, string path) => new ApiGatewayRouteConfig + { + LambdaResourceName = "TestLambdaFunction", + Endpoint = $"{method} {path}", + HttpMethod = method, + Path = "/api/users/{userId}/orders" // This is now the resource + }); + _mockRouteConfigParser.Setup(x => x.ExtractPathParameters(It.IsAny(), It.IsAny())) + .Returns(new Dictionary { { "userId", "123" } }); + + _mockHttpRequestUtility.Setup(x => x.ExtractHeaders(It.IsAny())) + .Returns((new Dictionary(), new Dictionary>())); + _mockHttpRequestUtility.Setup(x => x.ExtractQueryStringParameters(It.IsAny())) + .Returns((new Dictionary(), new Dictionary>())); + _mockHttpRequestUtility.Setup(x => x.ReadRequestBody(It.IsAny())) + .Returns(string.Empty); + _mockHttpRequestUtility.Setup(x => x.IsBinaryContent(It.IsAny())) + .Returns(false); + + HttpContextExtensions.SetRouteConfigurationParser(_mockRouteConfigParser.Object); + HttpContextExtensions.SetHttpRequestUtility(_mockHttpRequestUtility.Object); + } + + [Fact] + public void ToApiGatewayHttpV2Request_ShouldReturnValidApiGatewayHttpApiV2ProxyRequest() + { + var context = new DefaultHttpContext(); + var request = context.Request; + request.Method = "GET"; + request.Scheme = "https"; + request.Host = new HostString("api.example.com"); + request.Path = "/api/users/123/orders"; + request.QueryString = new QueryString("?status=pending"); + request.Headers["User-Agent"] = "TestAgent"; + request.Headers["Accept"] = "application/json"; + request.Headers["Cookie"] = "session=abc123; theme=dark"; + + var result = context.ToApiGatewayHttpV2Request(); + + Assert.NotNull(result); + Assert.Equal("2.0", result.Version); + Assert.Equal("GET /api/users/{userId}/orders", result.RouteKey); + Assert.Equal("/api/users/123/orders", result.RawPath); + Assert.Equal("?status=pending", result.RawQueryString); + Assert.Equal(2, result.Cookies.Length); + Assert.Contains("session=abc123", result.Cookies); + Assert.Contains("theme=dark", result.Cookies); + Assert.Equal("123", result.PathParameters["userId"]); + Assert.Equal("GET", result.RequestContext.Http.Method); + Assert.Equal("/api/users/123/orders", result.RequestContext.Http.Path); + Assert.Equal("GET /api/users/{userId}/orders", result.RequestContext.RouteKey); + + _mockRouteConfigParser.Verify(x => x.GetRouteConfig("GET", "/api/users/123/orders"), Times.Once); + _mockRouteConfigParser.Verify(x => x.ExtractPathParameters(It.IsAny(), "/api/users/123/orders"), Times.Once); + } + + [Fact] + public void ToApiGatewayHttpV2Request_WithBinaryContent_ShouldBase64EncodeBody() + { + var context = new DefaultHttpContext(); + var request = context.Request; + request.Method = "POST"; + request.Path = "/api/users/123/avatar"; + request.ContentType = "application/octet-stream"; + var bodyContent = new byte[] { 1, 2, 3, 4, 5 }; + request.Body = new MemoryStream(bodyContent); + + _mockHttpRequestUtility.Setup(x => x.IsBinaryContent(It.IsAny())).Returns(true); + _mockHttpRequestUtility.Setup(x => x.ReadRequestBody(It.IsAny())).Returns(Encoding.UTF8.GetString(bodyContent)); + + _mockRouteConfigParser.Setup(x => x.GetRouteConfig("POST", "/api/users/123/avatar")) + .Returns(new ApiGatewayRouteConfig + { + LambdaResourceName = "UploadAvatarFunction", + Endpoint = "POST /api/users/{userId}/avatar", + HttpMethod = "POST", + Path = "/api/users/{userId}/avatar" + }); + + var result = context.ToApiGatewayHttpV2Request(); + + Assert.NotNull(result); + Assert.True(result.IsBase64Encoded); + Assert.Equal(Convert.ToBase64String(bodyContent), result.Body); + Assert.Equal("123", result.PathParameters["userId"]); + Assert.Equal("POST /api/users/{userId}/avatar", result.RouteKey); + + _mockRouteConfigParser.Verify(x => x.GetRouteConfig("POST", "/api/users/123/avatar"), Times.Once); + _mockRouteConfigParser.Verify(x => x.ExtractPathParameters(It.IsAny(), "/api/users/123/avatar"), Times.Once); + } + + [Fact] + public void ToApiGatewayRequest_ShouldReturnValidApiGatewayProxyRequest() + { + var context = new DefaultHttpContext(); + var request = context.Request; + request.Method = "GET"; + request.Scheme = "https"; + request.Host = new HostString("api.example.com"); + request.Path = "/api/users/123/orders"; + request.QueryString = new QueryString("?status=pending&tag=important&tag=urgent"); + request.Headers["User-Agent"] = "TestAgent"; + request.Headers["Accept"] = new Microsoft.Extensions.Primitives.StringValues(new[] { "text/html", "application/json" }); + request.Headers["Cookie"] = "session=abc123; theme=dark"; + request.Headers["X-Custom-Header"] = "value1"; + + _mockHttpRequestUtility.Setup(x => x.ExtractHeaders(It.IsAny())) + .Returns(( + new Dictionary + { + { "User-Agent", "TestAgent" }, + { "Accept", "application/json" }, + { "Cookie", "session=abc123; theme=dark" }, + { "X-Custom-Header", "value1" } + }, + new Dictionary> + { + { "User-Agent", new List { "TestAgent" } }, + { "Accept", new List { "text/html", "application/json" } }, + { "Cookie", new List { "session=abc123; theme=dark" } }, + { "X-Custom-Header", new List { "value1" } } + } + )); + + _mockHttpRequestUtility.Setup(x => x.ExtractQueryStringParameters(It.IsAny())) + .Returns(( + new Dictionary + { + { "status", "pending" }, + { "tag", "urgent" } + }, + new Dictionary> + { + { "status", new List { "pending" } }, + { "tag", new List { "important", "urgent" } } + } + )); + + var result = context.ToApiGatewayRequest(); + + Assert.NotNull(result); + Assert.Equal("/api/users/{userId}/orders", result.Resource); + Assert.Equal(HttpUtility.UrlEncode("/api/users/123/orders"), result.Path); + Assert.Equal("GET", result.HttpMethod); + + Assert.Equal("TestAgent", result.Headers["User-Agent"]); + Assert.Equal("application/json", result.Headers["Accept"]); + Assert.Equal("session=abc123; theme=dark", result.Headers["Cookie"]); + Assert.Equal("value1", result.Headers["X-Custom-Header"]); + + Assert.Equal(new List { "TestAgent" }, result.MultiValueHeaders["User-Agent"]); + Assert.Equal(new List { "text/html", "application/json" }, result.MultiValueHeaders["Accept"]); + Assert.Equal(new List { "session=abc123; theme=dark" }, result.MultiValueHeaders["Cookie"]); + Assert.Equal(new List { "value1" }, result.MultiValueHeaders["X-Custom-Header"]); + + Assert.Equal("pending", result.QueryStringParameters["status"]); + Assert.Equal("urgent", result.QueryStringParameters["tag"]); + + Assert.Equal(new List { "pending" }, result.MultiValueQueryStringParameters["status"]); + Assert.Equal(new List { "important", "urgent" }, result.MultiValueQueryStringParameters["tag"]); + + Assert.Equal("123", result.PathParameters["userId"]); + Assert.Equal(string.Empty, result.Body); + Assert.False(result.IsBase64Encoded); + + _mockRouteConfigParser.Verify(x => x.GetRouteConfig("GET", "/api/users/123/orders"), Times.Once); + _mockRouteConfigParser.Verify(x => x.ExtractPathParameters(It.IsAny(), "/api/users/123/orders"), Times.Once); + } + } +} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpRequestUtilityTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpRequestUtilityTests.cs index 3afddfe0c..2c70ce232 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpRequestUtilityTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/HttpRequestUtilityTests.cs @@ -1,97 +1,86 @@ -using System; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using Moq; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Amazon.Lambda.TestTool.UnitTests +namespace Amazon.Lambda.TestTool.UnitTests; + +public class HttpRequestUtilityTests { - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Primitives; - using Xunit; - using Moq; + private readonly HttpRequestUtility _utility; - namespace Amazon.Lambda.TestTool.UnitTests + public HttpRequestUtilityTests() { - public class HttpRequestUtilityTests - { - private readonly HttpRequestUtility _utility; - - public HttpRequestUtilityTests() - { - _utility = new HttpRequestUtility(); - } + _utility = new HttpRequestUtility(); + } - [Theory] - [InlineData("image/jpeg", true)] - [InlineData("audio/mpeg", true)] - [InlineData("video/mp4", true)] - [InlineData("application/octet-stream", true)] - [InlineData("text/plain", false)] - [InlineData("application/json", false)] - [InlineData(null, false)] - [InlineData("", false)] - public void IsBinaryContent_ReturnsExpectedResult(string contentType, bool expected) - { - var result = _utility.IsBinaryContent(contentType); - Assert.Equal(expected, result); - } + [Theory] + [InlineData("image/jpeg", true)] + [InlineData("audio/mpeg", true)] + [InlineData("video/mp4", true)] + [InlineData("application/octet-stream", true)] + [InlineData("text/plain", false)] + [InlineData("application/json", false)] + [InlineData(null, false)] + [InlineData("", false)] + public void IsBinaryContent_ReturnsExpectedResult(string contentType, bool expected) + { + var result = _utility.IsBinaryContent(contentType); + Assert.Equal(expected, result); + } - [Fact] - public void ReadRequestBody_ReturnsCorrectContent() - { - var content = "Test body content"; - var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); - var request = new Mock(); - request.Setup(r => r.Body).Returns(stream); + [Fact] + public void ReadRequestBody_ReturnsCorrectContent() + { + var content = "Test body content"; + var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); + var request = new Mock(); + request.Setup(r => r.Body).Returns(stream); - var result = _utility.ReadRequestBody(request.Object); + var result = _utility.ReadRequestBody(request.Object); - Assert.Equal(content, result); - } + Assert.Equal(content, result); + } - [Fact] - public void ExtractHeaders_ReturnsCorrectDictionaries() - { - var headers = new HeaderDictionary - { - { "Single", new StringValues("Value") }, - { "Multi", new StringValues(new[] { "Value1", "Value2" }) } - }; + [Fact] + public void ExtractHeaders_ReturnsCorrectDictionaries() + { + var headers = new HeaderDictionary + { + { "Single", new StringValues("Value") }, + { "Multi", new StringValues(new[] { "Value1", "Value2" }) } + }; - var (singleValueHeaders, multiValueHeaders) = _utility.ExtractHeaders(headers); + var (singleValueHeaders, multiValueHeaders) = _utility.ExtractHeaders(headers); - Assert.Equal(2, singleValueHeaders.Count); - Assert.Equal(2, multiValueHeaders.Count); - Assert.Equal("Value", singleValueHeaders["Single"]); - Assert.Equal("Value2", singleValueHeaders["Multi"]); - Assert.Equal(new List { "Value" }, multiValueHeaders["Single"]); - Assert.Equal(new List { "Value1", "Value2" }, multiValueHeaders["Multi"]); - } + Assert.Equal(2, singleValueHeaders.Count); + Assert.Equal(2, multiValueHeaders.Count); + Assert.Equal("Value", singleValueHeaders["Single"]); + Assert.Equal("Value2", singleValueHeaders["Multi"]); + Assert.Equal(new List { "Value" }, multiValueHeaders["Single"]); + Assert.Equal(new List { "Value1", "Value2" }, multiValueHeaders["Multi"]); + } - [Fact] - public void ExtractQueryStringParameters_ReturnsCorrectDictionaries() - { - var query = new QueryCollection(new Dictionary - { - { "Single", new StringValues("Value") }, - { "Multi", new StringValues(new[] { "Value1", "Value2" }) } - }); + [Fact] + public void ExtractQueryStringParameters_ReturnsCorrectDictionaries() + { + var query = new QueryCollection(new Dictionary + { + { "Single", new StringValues("Value") }, + { "Multi", new StringValues(new[] { "Value1", "Value2" }) } + }); - var (singleValueParams, multiValueParams) = _utility.ExtractQueryStringParameters(query); + var (singleValueParams, multiValueParams) = _utility.ExtractQueryStringParameters(query); - Assert.Equal(2, singleValueParams.Count); - Assert.Equal(2, multiValueParams.Count); - Assert.Equal("Value", singleValueParams["Single"]); - Assert.Equal("Value2", singleValueParams["Multi"]); - Assert.Equal(new List { "Value" }, multiValueParams["Single"]); - Assert.Equal(new List { "Value1", "Value2" }, multiValueParams["Multi"]); - } - } + Assert.Equal(2, singleValueParams.Count); + Assert.Equal(2, multiValueParams.Count); + Assert.Equal("Value", singleValueParams["Single"]); + Assert.Equal("Value2", singleValueParams["Multi"]); + Assert.Equal(new List { "Value" }, multiValueParams["Single"]); + Assert.Equal(new List { "Value1", "Value2" }, multiValueParams["Multi"]); } - }