Beispiel #1
0
    public static async Task Function_Can_Process_Multiple_Requests_On_Different_Threads()
    {
        // Arrange
        int messageCount = 1_000;
        int expected     = Enumerable.Range(0, messageCount).Sum();

        using var server = new LambdaTestServer();
        using var cts    = new CancellationTokenSource();

        await server.StartAsync(cts.Token);

        using var httpClient = server.CreateClient();

        // Enqueue the requests to process in the background
        var completedAdding = EnqueueInParallel(messageCount, server);

        // Start a task to consume the responses in the background
        var completedProcessing = Assert(completedAdding, messageCount, cts);

        // Act - Start the function processing
        await ReverseFunction.RunAsync(httpClient, cts.Token);

        // Assert
        int actual = await completedProcessing.Task;

        actual.ShouldBe(expected);
    }
Beispiel #2
0
    public async Task Function_Can_Handle_Failed_Initialization()
    {
        // Arrange
        void Configure(IServiceCollection services)
        {
            services.AddLogging((builder) => builder.AddXUnit(this));
        }

        using var server = new LambdaTestServer(Configure);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        var context = await server.EnqueueAsync(@"{""Values"": null}");

        _ = Task.Run(async() =>
        {
            await context.Response.WaitToReadAsync(cts.Token);

            if (!cts.IsCancellationRequested)
            {
                cts.Cancel();
            }
        });

        using var httpClient = server.CreateClient();

        // Act
        await Assert.ThrowsAsync <InvalidOperationException>(
            () => FunctionRunner.RunAsync <MyFailedInitializationHandler>(httpClient, cts.Token));
    }
Beispiel #3
0
    public static async Task Function_Reverses_Numbers_With_Mobile_Sdk()
    {
        // Arrange
        using var server = new LambdaTestServer();
        using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));

        await server.StartAsync(cancellationTokenSource.Token);

        int[]  value   = new[] { 1, 2, 3 };
        byte[] content = JsonSerializer.SerializeToUtf8Bytes(value);

        var request = new LambdaTestRequest(content)
        {
            ClientContext   = JsonSerializer.Serialize(new { client = new { app_title = "my-app" } }),
            CognitoIdentity = JsonSerializer.Serialize(new { identityId = "my-identity" }),
        };

        LambdaTestContext context = await server.EnqueueAsync(content);

        using var httpClient = server.CreateClient();

        // Act
        await ReverseFunction.RunAsync(httpClient, cancellationTokenSource.Token);

        // Assert
        Assert.True(context.Response.TryRead(out LambdaTestResponse? response));
        Assert.NotNull(response);
        Assert.True(response !.IsSuccessful);

        var actual = JsonSerializer.Deserialize <int[]>(response.Content);

        Assert.NotNull(actual);
        Assert.Equal(new[] { 3, 2, 1 }, actual);
    }
Beispiel #4
0
    public static async Task Function_Reverses_Numbers()
    {
        // Arrange
        using var server = new LambdaTestServer();
        using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));

        await server.StartAsync(cancellationTokenSource.Token);

        int[]  value = new[] { 1, 2, 3 };
        byte[] json  = JsonSerializer.SerializeToUtf8Bytes(value);

        LambdaTestContext context = await server.EnqueueAsync(json);

        using var httpClient = server.CreateClient();

        // Act
        await ReverseFunction.RunAsync(httpClient, cancellationTokenSource.Token);

        // Assert
        Assert.True(context.Response.TryRead(out LambdaTestResponse? response));
        Assert.True(response !.IsSuccessful);
        Assert.NotNull(response.Content);

        var actual = JsonSerializer.Deserialize <int[]>(response.Content);

        Assert.NotNull(actual);
        Assert.Equal(new[] { 3, 2, 1 }, actual);
    }
Beispiel #5
0
    public static async Task Function_Computes_Results(double left, string op, double right, double expected)
    {
        // Arrange
        using var server = new LambdaTestServer();
        using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));

        await server.StartAsync(cancellationTokenSource.Token);

        var value = new MathsRequest()
        {
            Left = left, Operator = op, Right = right
        };
        string json = JsonSerializer.Serialize(value);

        var context = await server.EnqueueAsync(json);

        using var httpClient = server.CreateClient();

        // Act
        await MathsFunction.RunAsync(httpClient, cancellationTokenSource.Token);

        // Assert
        Assert.True(context.Response.TryRead(out var response));
        Assert.NotNull(response);
        Assert.True(response !.IsSuccessful);

        json = await response.ReadAsStringAsync();

        var actual = JsonSerializer.Deserialize <MathsResponse>(json);

        Assert.NotNull(actual);
        Assert.Equal(expected, actual !.Result);
    }
Beispiel #6
0
    public void Finalizer_Does_Not_Throw()
    {
#pragma warning disable CA2000
        // Act (no Assert)
        _ = new LambdaTestServer();
#pragma warning restore CA2000
    }
Beispiel #7
0
    public void CreateClient_Throws_If_Not_Started()
    {
        // Arrange
        using var target = new LambdaTestServer();

        // Act
        Assert.Throws <InvalidOperationException>(() => target.CreateClient());
    }
Beispiel #8
0
    public async Task EnqueueAsync_Throws_If_Not_Started()
    {
        // Arrange
        using var target = new LambdaTestServer();
        var request = new LambdaTestRequest(Array.Empty <byte>());

        // Act and Assert
        await Assert.ThrowsAsync <InvalidOperationException>(async() => await target.EnqueueAsync(request));
    }
Beispiel #9
0
    public async Task EnqueueAsync_Validates_Parameters()
    {
        // Arrange
        using var target = new LambdaTestServer();
        LambdaTestRequest request = null !;

        // Act
        await Assert.ThrowsAsync <ArgumentNullException>("request", () => target.EnqueueAsync(request));
    }
Beispiel #10
0
    public async Task Function_Can_Process_Multiple_Requests()
    {
        // Arrange
        void Configure(IServiceCollection services)
        {
            services.AddLogging((builder) => builder.AddXUnit(this));
        }

        using var server = new LambdaTestServer(Configure);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        var channels = new List <(int Expected, LambdaTestContext Context)>();

        for (int i = 0; i < 10; i++)
        {
            var request = new MyRequest()
            {
                Values = Enumerable.Range(1, i + 1).ToArray(),
            };

            channels.Add((request.Values.Sum(), await server.EnqueueAsync(request)));
        }

        _ = Task.Run(async() =>
        {
            foreach ((var _, var context) in channels)
            {
                await context.Response.WaitToReadAsync(cts.Token);
            }

            if (!cts.IsCancellationRequested)
            {
                cts.Cancel();
            }
        });

        using var httpClient = server.CreateClient();

        // Act
        await MyFunctionEntrypoint.RunAsync(httpClient, cts.Token);

        // Assert
        foreach ((int expected, var context) in channels)
        {
            context.Response.TryRead(out var response).ShouldBeTrue();

            response.ShouldNotBeNull();
            response !.IsSuccessful.ShouldBeTrue();
            response.Content.ShouldNotBeNull();

            var deserialized = response.ReadAs <MyResponse>();
            deserialized.Sum.ShouldBe(expected);
        }
    }
Beispiel #11
0
    public void CreateClient_Throws_If_Disposed()
    {
        // Arrange
        var target = new LambdaTestServer();

        target.Dispose();

        // Act
        Assert.Throws <ObjectDisposedException>(() => target.CreateClient());
    }
Beispiel #12
0
    public static async Task Function_Can_Process_Request()
    {
        // Arrange - Create a test server for the Lambda runtime to use
        using var server = new LambdaTestServer();

        // Create a cancellation token that stops the server listening for new requests.
        // Auto-cancel the server after 2 seconds in case something goes wrong and the request is not handled.
        using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        // Start the test server so it is ready to listen for requests from the Lambda runtime
        await server.StartAsync(cancellationTokenSource.Token);

        // Create a test request for the Lambda function being tested
        var value = new MyRequest()
        {
            Values = new[] { 1, 2, 3 }, // The function returns the sum of the specified numbers
        };

        string requestJson = JsonSerializer.Serialize(value);

        // Queue the request with the server to invoke the Lambda function and
        // store the ChannelReader into a variable to use to read the response.
        LambdaTestContext context = await server.EnqueueAsync(requestJson);

        // Queue a task to stop the test server from listening as soon as the response is available
        _ = Task.Run(async() =>
        {
            await context.Response.WaitToReadAsync(cancellationTokenSource.Token);

            if (!cancellationTokenSource.IsCancellationRequested)
            {
                cancellationTokenSource.Cancel();
            }
        });

        // Create an HttpClient for the Lambda to use with LambdaBootstrap
        using var httpClient = server.CreateClient();

        // Act - Start the Lambda runtime and run until the cancellation token is signalled
        await MyFunctionEntrypoint.RunAsync(httpClient, cancellationTokenSource.Token);

        // Assert - The channel reader should have the response available
        context.Response.TryRead(out LambdaTestResponse? response).ShouldBeTrue("No Lambda response is available.");

        response !.IsSuccessful.ShouldBeTrue("The Lambda function failed to handle the request.");
        response.Content.ShouldNotBeEmpty("The Lambda function did not return any content.");

        string responseJson = await response.ReadAsStringAsync();

        var actual = JsonSerializer.Deserialize <MyResponse>(responseJson);

        actual.ShouldNotBeNull();
        actual.Sum.ShouldBe(6, "The Lambda function returned an incorrect response.");
    }
Beispiel #13
0
    public async Task EnqueueAsync_Throws_If_Disposed()
    {
        // Arrange
        var target = new LambdaTestServer();

        target.Dispose();

        var request = new LambdaTestRequest(Array.Empty <byte>());

        // Act
        await Assert.ThrowsAsync <ObjectDisposedException>(() => target.EnqueueAsync(request));
    }
Beispiel #14
0
    /// <summary>
    /// Enqueues a request for the Lambda function to process as an asynchronous operation.
    /// </summary>
    /// <param name="server">The server to enqueue the request with.</param>
    /// <param name="content">The request content to process.</param>
    /// <returns>
    /// A <see cref="Task{LambdaTestContext}"/> representing the asynchronous operation to
    /// enqueue the request which returns a context containg a <see cref="ChannelReader{LambdaTestResponse}"/>
    /// which completes once the request is processed by the function.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="server"/> or <paramref name="content"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ObjectDisposedException">
    /// The instance has been disposed.
    /// </exception>
    public static async Task <LambdaTestContext> EnqueueAsync(
        this LambdaTestServer server,
        byte[] content)
    {
        if (server == null)
        {
            throw new ArgumentNullException(nameof(server));
        }

        var request = new LambdaTestRequest(content);

        return(await server.EnqueueAsync(request).ConfigureAwait(false));
    }
Beispiel #15
0
    public static async Task EnqueueAsync_Validates_Parameters()
    {
        // Arrange
        using var server = new LambdaTestServer();
        using LambdaTestServer nullServer = null !;
        byte[] content = null !;
        string value   = null !;

        // Act
        await Assert.ThrowsAsync <ArgumentNullException>("content", () => server.EnqueueAsync(content));

        await Assert.ThrowsAsync <ArgumentNullException>("value", () => server.EnqueueAsync(value));

        await Assert.ThrowsAsync <ArgumentNullException>("server", () => nullServer.EnqueueAsync(Array.Empty <byte>()));

        await Assert.ThrowsAsync <ArgumentNullException>("server", () => nullServer.EnqueueAsync(string.Empty));
    }
Beispiel #16
0
    public async Task StartAsync_Throws_If_Already_Started()
    {
        // Act
        using var target = new LambdaTestServer();

        // Assert
        target.IsStarted.ShouldBeFalse();

        // Act
        await target.StartAsync();

        // Assert
        target.IsStarted.ShouldBeTrue();

        // Act and Assert
        await Assert.ThrowsAsync <InvalidOperationException>(async() => await target.StartAsync());
    }
Beispiel #17
0
    public async Task EnqueueAsync_Throws_If_Already_Enqueued()
    {
        // Arrange
        using var target = new LambdaTestServer();
        var request = new LambdaTestRequest(Array.Empty <byte>());

        await target.StartAsync();

        var context = await target.EnqueueAsync(request);

        context.ShouldNotBeNull();
        context.Request.ShouldBe(request);
        context.Response.ShouldNotBeNull();
        context.Response.Completion.IsCompleted.ShouldBeFalse();

        // Act and Assert
        await Assert.ThrowsAsync <InvalidOperationException>(async() => await target.EnqueueAsync(request));
    }
Beispiel #18
0
    /// <summary>
    /// Enqueues a request for the Lambda function to process as an asynchronous operation.
    /// </summary>
    /// <param name="server">The server to enqueue the request with.</param>
    /// <param name="value">The request content to process.</param>
    /// <returns>
    /// A <see cref="Task{LambdaTestContext}"/> representing the asynchronous operation to
    /// enqueue the request which returns a context containg a <see cref="ChannelReader{LambdaTestResponse}"/>
    /// which completes once the request is processed by the function.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="server"/> or <paramref name="value"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ObjectDisposedException">
    /// The instance has been disposed.
    /// </exception>
    public static async Task <LambdaTestContext> EnqueueAsync(
        this LambdaTestServer server,
        string value)
    {
        if (server == null)
        {
            throw new ArgumentNullException(nameof(server));
        }

        if (value == null)
        {
            throw new ArgumentNullException(nameof(value));
        }

        byte[] content = Encoding.UTF8.GetBytes(value);

        return(await server.EnqueueAsync(content).ConfigureAwait(false));
    }
Beispiel #19
0
    public async Task Function_Can_Process_Request_With_Mobile_Sdk_Headers()
    {
        // Arrange
        void Configure(IServiceCollection services)
        {
            services.AddLogging((builder) => builder.AddXUnit(this));
        }

        using var server = new LambdaTestServer(Configure);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        byte[] content = Encoding.UTF8.GetBytes(@"{""Values"": [ 1, 2, 3 ]}");

        var request = new LambdaTestRequest(content)
        {
            ClientContext   = "{}",
            CognitoIdentity = "{}",
        };

        var context = await server.EnqueueAsync(request);

        _ = Task.Run(async() =>
        {
            await context.Response.WaitToReadAsync(cts.Token);

            if (!cts.IsCancellationRequested)
            {
                cts.Cancel();
            }
        });

        using var httpClient = server.CreateClient();

        // Act
        await MyFunctionEntrypoint.RunAsync(httpClient, cts.Token);

        // Assert
        context.Response.TryRead(out var response).ShouldBeTrue();

        response.ShouldNotBeNull();
        response !.IsSuccessful.ShouldBeTrue();
    }
Beispiel #20
0
    public async Task Function_Returns_If_No_Requests_Within_Timeout()
    {
        // Arrange
        void Configure(IServiceCollection services)
        {
            services.AddLogging((builder) => builder.AddXUnit(this));
        }

        using var server = new LambdaTestServer(Configure);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        using var httpClient = server.CreateClient();

        // Act
        await MyFunctionEntrypoint.RunAsync(httpClient, cts.Token);

        // Assert
        cts.IsCancellationRequested.ShouldBeTrue();
    }
Beispiel #21
0
    public async Task Function_Can_Process_Request()
    {
        // Arrange
        void Configure(IServiceCollection services)
        {
            services.AddLogging((builder) => builder.AddXUnit(this));
        }

        using var server = new LambdaTestServer(Configure);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        var context = await server.EnqueueAsync(@"{""Values"": [ 1, 2, 3 ]}");

        _ = Task.Run(async() =>
        {
            await context.Response.WaitToReadAsync(cts.Token);

            if (!cts.IsCancellationRequested)
            {
                cts.Cancel();
            }
        });

        using var httpClient = server.CreateClient();

        // Act
        await MyFunctionEntrypoint.RunAsync(httpClient, cts.Token);

        // Assert
        context.Response.TryRead(out var response).ShouldBeTrue();

        response.ShouldNotBeNull();
        response !.IsSuccessful.ShouldBeTrue();
        response.Content.ShouldNotBeNull();
        response.Duration.ShouldBeGreaterThan(TimeSpan.Zero);
        Encoding.UTF8.GetString(response.Content).ShouldBe(@"{""Sum"":6}");
    }
Beispiel #22
0
    private static TaskCompletionSource <IReadOnlyCollection <LambdaTestContext> > EnqueueInParallel(
        int count,
        LambdaTestServer server)
    {
        var collection       = new ConcurrentBag <LambdaTestContext>();
        var completionSource = new TaskCompletionSource <IReadOnlyCollection <LambdaTestContext> >();

        int queued = 0;

        // Enqueue the specified number of items in parallel
        Parallel.For(0, count, async(i) =>
        {
            var context = await server.EnqueueAsync(new[] { i });

            collection.Add(context);

            if (Interlocked.Increment(ref queued) == count)
            {
                completionSource.SetResult(collection);
            }
        });

        return(completionSource);
    }
Beispiel #23
0
        public async Task Alexa_Function_Can_Process_Request()
        {
            // Arrange
            SkillRequest request = CreateRequest <LaunchRequest>();

            request.Request.Type = "LaunchRequest";

            string json = JsonConvert.SerializeObject(request);

            void Configure(IServiceCollection services)
            {
                services.AddLogging((builder) => builder.AddXUnit(this));
            }

            using var server = new LambdaTestServer(Configure);
            using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));

            await server.StartAsync(cancellationTokenSource.Token);

            LambdaTestContext context = await server.EnqueueAsync(json);

            // Queue a task to stop the Lambda function as soon as the response is processed
            _ = Task.Run(async() =>
            {
                await context.Response.WaitToReadAsync(cancellationTokenSource.Token);

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            });

            using var httpClient = server.CreateClient();

            // Act
            await FunctionEntrypoint.RunAsync(httpClient, cancellationTokenSource.Token);

            // Assert
            context.Response.TryRead(out LambdaTestResponse result).ShouldBeTrue();

            result.ShouldNotBeNull();
            result.IsSuccessful.ShouldBeTrue();
            result.Duration.ShouldBeInRange(TimeSpan.FromTicks(1), TimeSpan.FromSeconds(1));
            result.Content.ShouldNotBeEmpty();

            json = await result.ReadAsStringAsync();

            var actual = JsonConvert.DeserializeObject <SkillResponse>(json);

            actual.ShouldNotBeNull();

            ResponseBody response = AssertResponse(actual, shouldEndSession: false);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Welcome to London Travel. You can ask me about disruption or for the status of any tube line, London Overground, the D.L.R. or T.F.L. Rail.</speak>");
        }
Beispiel #24
0
    public async Task Can_Use_Custom_Function_Variables()
    {
        // Arrange
        LambdaTestServer.ClearLambdaEnvironmentVariables();

        var options = new LambdaTestServerOptions()
        {
            FunctionArn        = "my-custom-arn",
            FunctionHandler    = "my-custom-handler",
            FunctionMemorySize = 1024,
            FunctionName       = "my-function-name",
            FunctionTimeout    = TimeSpan.FromSeconds(119),
            FunctionVersion    = 42,
            LogGroupName       = "my-log-group",
            LogStreamName      = "my-log-stream",
        };

        using var server = new LambdaTestServer(options);
        using var cts    = new CancellationTokenSource(TimeSpan.FromSeconds(2));

        await server.StartAsync(cts.Token);

        var request = new LambdaTestRequest(Array.Empty <byte>(), "my-request-id")
        {
            ClientContext   = @"{""client"":{""app_title"":""my-app""}}",
            CognitoIdentity = @"{""cognitoIdentityId"":""my-identity""}",
        };

        var context = await server.EnqueueAsync(request);

        _ = Task.Run(async() =>
        {
            await context.Response.WaitToReadAsync(cts.Token);

            if (!cts.IsCancellationRequested)
            {
                cts.Cancel();
            }
        });

        using var httpClient = server.CreateClient();

        // Act
        await CustomFunction.RunAsync(httpClient, cts.Token);

        // Assert
        context.Response.TryRead(out var response).ShouldBeTrue();

        response.ShouldNotBeNull();
        response !.IsSuccessful.ShouldBeTrue();
        response.Content.ShouldNotBeNull();

        var lambdaContext = response.ReadAs <IDictionary <string, string> >();

        lambdaContext.ShouldContainKeyAndValue("AwsRequestId", request.AwsRequestId);
        lambdaContext.ShouldContainKeyAndValue("ClientContext", "my-app");
        lambdaContext.ShouldContainKeyAndValue("FunctionName", options.FunctionName);
        lambdaContext.ShouldContainKeyAndValue("FunctionVersion", "42");
        lambdaContext.ShouldContainKeyAndValue("IdentityId", "my-identity");
        lambdaContext.ShouldContainKeyAndValue("InvokedFunctionArn", options.FunctionArn);
        lambdaContext.ShouldContainKeyAndValue("LogGroupName", options.LogGroupName);
        lambdaContext.ShouldContainKeyAndValue("LogStreamName", options.LogStreamName);
        lambdaContext.ShouldContainKeyAndValue("MemoryLimitInMB", "1024");

        lambdaContext.ShouldContainKey("RemainingTime");
        string remainingTimeString = lambdaContext["RemainingTime"];

        TimeSpan.TryParse(remainingTimeString, out var remainingTime).ShouldBeTrue();

        remainingTime.Minutes.ShouldBe(options.FunctionTimeout.Minutes);
    }
Beispiel #25
0
 internal static async Task <LambdaTestContext> EnqueueAsync <T>(this LambdaTestServer server, T value)
     where T : class
 {
     byte[] json = JsonSerializer.SerializeToUtf8Bytes(value);
     return(await server.EnqueueAsync(json));
 }