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); }
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)); }
public async Task EnqueueAsync_Validates_Parameters() { // Arrange using var target = new LambdaTestServer(); LambdaTestRequest request = null !; // Act await Assert.ThrowsAsync <ArgumentNullException>("request", () => target.EnqueueAsync(request)); }
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)); }
/// <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)); }
/// <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)); }
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)); }
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(); }
/// <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); }
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); }
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); }
/// <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; }