Skip to content

feat: update invalid route handling based on emulator mode #1909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A class for common API Gateway responses.
/// </summary>
public static class ApiGatewayResults
{
/// <summary>
/// Returns a 'Not Found' for HTTP API Gateway mode and 'Missing Authentication Token' for Rest.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to update.</param>
/// <param name="emulatorMode">The API Gateway Emulator mode.</param>
/// <returns></returns>
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" });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ namespace Amazon.Lambda.TestTool.Models;
/// <summary>
/// Represents a base exception that is thrown by the test tool.
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
/// <param name="message">The message used in the exception.</param>
/// <param name="innerException">The inner exception, if any.</param>
public abstract class TestToolException(string message, Exception? innerException = null)
: Exception(message, innerException);

/// <summary>
/// Thrown if the API Gateway Emulator mode was not provided,
/// </summary>
/// <param name="message">The message used in the exception.</param>
/// <param name="innerException">The inner exception, if any.</param>
public class InvalidApiGatewayModeException(string message, Exception? innerException = null)
: TestToolException(message, innerException);
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class ApiGatewayEmulatorProcess
/// </summary>
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();
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ internal static async Task<bool> WaitForApiToStartAsync(string url, int maxRetri
}
}

internal static async Task<HttpResponseMessage> SendRequest(string url)
{
using (var client = new HttpClient())
{
return await client.GetAsync(url);
}
}

internal static async Task CancelAndWaitAsync(Task executeTask)
{
await Task.Delay(1000);
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading