diff --git a/.editorconfig b/.editorconfig index 6e89449c1..c1f291525 100644 --- a/.editorconfig +++ b/.editorconfig @@ -202,7 +202,7 @@ dotnet_code_quality.ca2208.api_surface=public dotnet_diagnostic.ca1822.severity=none # License header -file_header_template= Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r\nSPDX-License-Identifier: Apache-2.0 +file_header_template= Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: Apache-2.0 # ReSharper properties resharper_braces_for_for=required diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Extensions/ResultExtensions.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Extensions/ResultExtensions.cs new file mode 100644 index 000000000..ab9e416ad --- /dev/null +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Extensions/ResultExtensions.cs @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Amazon.Lambda.TestTool.Models; + +namespace Amazon.Lambda.TestTool.Extensions; + +/// +/// A class for common API Gateway responses. +/// +public static class ApiGatewayResults +{ + /// + /// Returns a 'Not Found' for HTTP API Gateway mode and 'Missing Authentication Token' for Rest. + /// + /// The to update. + /// The API Gateway Emulator mode. + /// + public static IResult RouteNotFound(HttpContext context, ApiGatewayEmulatorMode emulatorMode) + { + if (emulatorMode == ApiGatewayEmulatorMode.Rest) + { + context.Response.StatusCode = StatusCodes.Status403Forbidden; + context.Response.Headers.Append("x-amzn-errortype", "MissingAuthenticationTokenException"); + return Results.Json(new { message = "Missing Authentication Token" }); + } + else + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + return Results.Json(new { message = "Not Found" }); + } + } +} diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Models/Exceptions.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Models/Exceptions.cs index 4ad1b75f2..1e2d31af1 100644 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Models/Exceptions.cs +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Models/Exceptions.cs @@ -6,7 +6,15 @@ namespace Amazon.Lambda.TestTool.Models; /// /// Represents a base exception that is thrown by the test tool. /// -/// -/// +/// The message used in the exception. +/// The inner exception, if any. public abstract class TestToolException(string message, Exception? innerException = null) : Exception(message, innerException); + +/// +/// Thrown if the API Gateway Emulator mode was not provided, +/// +/// The message used in the exception. +/// The inner exception, if any. +public class InvalidApiGatewayModeException(string message, Exception? innerException = null) + : TestToolException(message, innerException); diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Processes/ApiGatewayEmulatorProcess.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Processes/ApiGatewayEmulatorProcess.cs index 3fa3eeef8..5a1e1d1a3 100644 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Processes/ApiGatewayEmulatorProcess.cs +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Processes/ApiGatewayEmulatorProcess.cs @@ -33,6 +33,11 @@ public class ApiGatewayEmulatorProcess /// public static ApiGatewayEmulatorProcess Startup(RunCommandSettings settings, CancellationToken cancellationToken = default) { + if (settings.ApiGatewayEmulatorMode is null) + { + throw new InvalidApiGatewayModeException("The API Gateway emulator mode was not provided."); + } + var builder = WebApplication.CreateBuilder(); builder.Services.AddApiGatewayEmulatorServices(); @@ -61,9 +66,7 @@ public static ApiGatewayEmulatorProcess Startup(RunCommandSettings settings, Can { app.Logger.LogInformation("Unable to find a configured Lambda route for the specified method and path: {Method} {Path}", context.Request.Method, context.Request.Path); - context.Response.StatusCode = StatusCodes.Status403Forbidden; - context.Response.Headers.Append("x-amzn-errortype", "MissingAuthenticationTokenException"); - return Results.Json(new { message = "Missing Authentication Token" }); + return ApiGatewayResults.RouteNotFound(context, (ApiGatewayEmulatorMode) settings.ApiGatewayEmulatorMode); } if (settings.ApiGatewayEmulatorMode.Equals(ApiGatewayEmulatorMode.HttpV2)) diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs index 578379453..deba2f4fa 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs @@ -31,6 +31,14 @@ internal static async Task WaitForApiToStartAsync(string url, int maxRetri } } + internal static async Task SendRequest(string url) + { + using (var client = new HttpClient()) + { + return await client.GetAsync(url); + } + } + internal static async Task CancelAndWaitAsync(Task executeTask) { await Task.Delay(1000); diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs new file mode 100644 index 000000000..55cb7a6ef --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System.Net; +using Amazon.Lambda.TestTool.Commands.Settings; +using Amazon.Lambda.TestTool.Models; +using Amazon.Lambda.TestTool.Processes; +using Amazon.Lambda.TestTool.UnitTests.Helpers; + +namespace Amazon.Lambda.TestTool.UnitTests.Processes; + +public class ApiGatewayEmulatorProcessTests +{ + [Theory] + [InlineData(ApiGatewayEmulatorMode.Rest, HttpStatusCode.Forbidden, "{\"message\":\"Missing Authentication Token\"}")] + [InlineData(ApiGatewayEmulatorMode.HttpV1, HttpStatusCode.NotFound, "{\"message\":\"Not Found\"}")] + [InlineData(ApiGatewayEmulatorMode.HttpV2, HttpStatusCode.NotFound, "{\"message\":\"Not Found\"}")] + public async Task RouteNotFound(ApiGatewayEmulatorMode mode, HttpStatusCode statusCode, string body) + { + // Arrange + var cancellationSource = new CancellationTokenSource(); + cancellationSource.CancelAfter(5000); + var settings = new RunCommandSettings { ApiGatewayEmulatorPort = 9003, ApiGatewayEmulatorMode = mode, NoLaunchWindow = true}; + var apiUrl = $"http://{settings.Host}:{settings.ApiGatewayEmulatorPort}/__lambda_test_tool_apigateway_health__"; + + // Act + var process = ApiGatewayEmulatorProcess.Startup(settings, cancellationSource.Token); + var isApiRunning = await TestHelpers.WaitForApiToStartAsync(apiUrl); + var response = await TestHelpers.SendRequest($"{process.ServiceUrl}/invalid"); + await cancellationSource.CancelAsync(); + + // Assert + await process.RunningTask; + Assert.True(isApiRunning); + Assert.Equal(statusCode, response.StatusCode); + Assert.Equal(body, await response.Content.ReadAsStringAsync()); + } +}