예제 #1
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);
    }
예제 #2
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));
    }
예제 #3
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));
    }
예제 #4
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));
    }
예제 #5
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));
    }
예제 #6
0
    /// <summary>
    /// Enqueues a request for the Lambda function to process as an asynchronous operation.
    /// </summary>
    /// <param name="request">The request to invoke the function with.</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="request"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// A request with the Id specified by <paramref name="request"/> is currently in-flight or the test server has not been started.
    /// </exception>
    /// <exception cref="ObjectDisposedException">
    /// The instance has been disposed.
    /// </exception>
    public async Task <LambdaTestContext> EnqueueAsync(LambdaTestRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        ThrowIfDisposed();
        ThrowIfNotStarted();

        var reader = await _handler !.EnqueueAsync(request, _onStopped !.Token).ConfigureAwait(false);

        return(new LambdaTestContext(request, reader));
    }
예제 #7
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));
    }
예제 #8
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();
    }
예제 #9
0
    /// <summary>
    /// Enqueues a request for the Lambda function to process as an asynchronous operation.
    /// </summary>
    /// <param name="request">The request to invoke the function with.</param>
    /// <param name="cancellationToken">The cancellation token to use when enqueuing the item.</param>
    /// <returns>
    /// A <see cref="Task"/> representing the asynchronous operation to enqueue the request
    /// which returns a channel reader which completes once the request is processed by the function.
    /// </returns>
    /// <exception cref="InvalidOperationException">
    /// A request with the Id specified by <paramref name="request"/> is currently in-flight.
    /// </exception>
    internal async Task <ChannelReader <LambdaTestResponse> > EnqueueAsync(
        LambdaTestRequest request,
        CancellationToken cancellationToken)
    {
        // There is only one response per request, so the channel is bounded to one item
        var channel = Channel.CreateBounded <LambdaTestResponse>(1);
        var context = new ResponseContext(channel);

        if (!_responses.TryAdd(request.AwsRequestId, context))
        {
            throw new InvalidOperationException($"A request with AWS request Id '{request.AwsRequestId}' is currently in-flight.");
        }

        // Enqueue the request for the Lambda runtime to process
        await _requests.Writer.WriteAsync(request, cancellationToken).ConfigureAwait(false);

        // Return the reader to the caller to await the function being handled
        return(channel.Reader);
    }
예제 #10
0
    public static void Constructor_Sets_Properties()
    {
        // Arrange
        byte[] content = new[] { (byte)1 };

        // Act
        var actual = new LambdaTestRequest(content);

        // Assert
        actual.Content.ShouldBeSameAs(content);
        actual.AwsRequestId.ShouldNotBeNullOrEmpty();
        Guid.TryParse(actual.AwsRequestId, out var requestId).ShouldBeTrue();
        requestId.ShouldNotBe(Guid.Empty);

        // Arrange
        string awsRequestId = "my-request-id";

        // Act
        actual = new LambdaTestRequest(content, awsRequestId);

        // Assert
        actual.Content.ShouldBeSameAs(content);
        actual.AwsRequestId.ShouldBe(awsRequestId);
    }
예제 #11
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);
    }
예제 #12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LambdaTestContext"/> class.
 /// </summary>
 /// <param name="request">The request to invoke the Lambda function with.</param>
 /// <param name="reader">The channel reader associated with the response.</param>
 internal LambdaTestContext(LambdaTestRequest request, ChannelReader <LambdaTestResponse> reader)
 {
     Request  = request;
     Response = reader;
 }