Skip to content

Commit c09e6b0

Browse files
committed
add unit tests
1 parent 34bc241 commit c09e6b0

File tree

2 files changed

+273
-1
lines changed

2 files changed

+273
-1
lines changed

Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRuntimeAPI.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
using System.Collections.Concurrent;
5+
using System.Runtime.CompilerServices;
56
using System.Text;
67
using Amazon.Lambda.TestTool.Models;
78
using Microsoft.AspNetCore.Mvc;
9+
[assembly: InternalsVisibleTo("Amazon.Lambda.TestTool.UnitTests")]
810

911
namespace Amazon.Lambda.TestTool.Services;
1012

@@ -15,7 +17,7 @@ public class LambdaRuntimeApi
1517

1618
private readonly IRuntimeApiDataStoreManager _runtimeApiDataStoreManager;
1719

18-
private LambdaRuntimeApi(WebApplication app)
20+
internal LambdaRuntimeApi(WebApplication app)
1921
{
2022
_runtimeApiDataStoreManager = app.Services.GetRequiredService<IRuntimeApiDataStoreManager>();
2123

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
using System.Text;
2+
using Amazon.Lambda.TestTool.Models;
3+
using Amazon.Lambda.TestTool.Services;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Moq;
8+
using Xunit;
9+
10+
namespace Amazon.Lambda.TestTool.UnitTests.Services;
11+
12+
public class LambdaRuntimeApiTests
13+
{
14+
private readonly Mock<IRuntimeApiDataStore> _mockRuntimeDataStore;
15+
private readonly WebApplication _app;
16+
private readonly Mock<RuntimeApiDataStore> _mockRuntimeApiDataStore;
17+
18+
public LambdaRuntimeApiTests()
19+
{
20+
Mock<IRuntimeApiDataStoreManager> mockDataStoreManager = new();
21+
_mockRuntimeDataStore = new Mock<IRuntimeApiDataStore>();
22+
_mockRuntimeApiDataStore = new Mock<RuntimeApiDataStore>();
23+
24+
var builder = WebApplication.CreateBuilder();
25+
builder.Services.AddSingleton(mockDataStoreManager.Object);
26+
_app = builder.Build();
27+
28+
mockDataStoreManager
29+
.Setup(x => x.GetLambdaRuntimeDataStore(It.IsAny<string>()))
30+
.Returns(_mockRuntimeDataStore.Object);
31+
32+
LambdaRuntimeApi.SetupLambdaRuntimeApiEndpoints(_app);
33+
}
34+
35+
[Fact]
36+
public async Task PostEvent_RequestResponse_Success()
37+
{
38+
// Arrange
39+
var functionName = "testFunction";
40+
var testEvent = "{\"key\":\"value\"}";
41+
var response = "{\"result\":\"success\"}";
42+
43+
var eventContainer = new EventContainer(_mockRuntimeApiDataStore.Object, 1, testEvent, true);
44+
eventContainer.ReportSuccessResponse(response);
45+
46+
_mockRuntimeDataStore
47+
.Setup(x => x.QueueEvent(testEvent, true))
48+
.Returns(eventContainer);
49+
50+
var context = new DefaultHttpContext();
51+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(testEvent));
52+
context.Response.Body = new MemoryStream();
53+
54+
// Act
55+
await new LambdaRuntimeApi(_app).PostEvent(context, functionName);
56+
57+
// Assert
58+
Assert.Equal(200, context.Response.StatusCode);
59+
Assert.Equal("application/json", context.Response.Headers.ContentType);
60+
Assert.Equal(response.Length, context.Response.ContentLength);
61+
62+
context.Response.Body.Position = 0;
63+
var responseBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
64+
Assert.Equal(response, responseBody);
65+
}
66+
67+
[Fact]
68+
public async Task PostEvent_Event_Async()
69+
{
70+
// Arrange
71+
var functionName = "testFunction";
72+
var testEvent = "{\"key\":\"value\"}";
73+
74+
var context = new DefaultHttpContext();
75+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(testEvent));
76+
context.Request.Headers["X-Amz-Invocation-Type"] = "Event";
77+
context.Response.Body = new MemoryStream();
78+
79+
// Act
80+
await new LambdaRuntimeApi(_app).PostEvent(context, functionName);
81+
82+
// Assert
83+
Assert.Equal(202, context.Response.StatusCode);
84+
}
85+
86+
[Fact]
87+
public async Task PostEvent_RequestResponse_Error()
88+
{
89+
// Arrange
90+
var functionName = "testFunction";
91+
var testEvent = "{\"key\":\"value\"}";
92+
var errorType = "Function.Error";
93+
var errorResponse = "{\"errorMessage\":\"Something went wrong\"}";
94+
95+
var eventContainer = new EventContainer(_mockRuntimeApiDataStore.Object, 1, testEvent, true);
96+
eventContainer.ReportErrorResponse(errorType, errorResponse);
97+
98+
_mockRuntimeDataStore
99+
.Setup(x => x.QueueEvent(testEvent, true))
100+
.Returns(eventContainer);
101+
102+
var context = new DefaultHttpContext();
103+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(testEvent));
104+
context.Response.Body = new MemoryStream();
105+
106+
// Act
107+
await new LambdaRuntimeApi(_app).PostEvent(context, functionName);
108+
109+
// Assert
110+
Assert.Equal(200, context.Response.StatusCode);
111+
Assert.Equal(errorType, context.Response.Headers["X-Amz-Function-Error"]);
112+
Assert.Equal("application/json", context.Response.Headers.ContentType);
113+
Assert.Equal(errorResponse.Length, context.Response.ContentLength);
114+
115+
context.Response.Body.Position = 0;
116+
var responseBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
117+
Assert.Equal(errorResponse, responseBody);
118+
}
119+
120+
[Fact]
121+
public async Task PostEvent_RequestResponse_ErrorWithoutBody()
122+
{
123+
// Arrange
124+
var functionName = "testFunction";
125+
var testEvent = "{\"key\":\"value\"}";
126+
var errorType = "Function.Error";
127+
string? errorResponse = null;
128+
129+
var eventContainer = new EventContainer(_mockRuntimeApiDataStore.Object, 1, testEvent, true);
130+
eventContainer.ReportErrorResponse(errorType, errorResponse);
131+
132+
_mockRuntimeDataStore
133+
.Setup(x => x.QueueEvent(testEvent, true))
134+
.Returns(eventContainer);
135+
136+
var context = new DefaultHttpContext();
137+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(testEvent));
138+
context.Response.Body = new MemoryStream();
139+
140+
// Act
141+
await new LambdaRuntimeApi(_app).PostEvent(context, functionName);
142+
143+
// Assert
144+
Assert.Equal(200, context.Response.StatusCode);
145+
Assert.Equal(errorType, context.Response.Headers["X-Amz-Function-Error"]);
146+
Assert.Equal(0, context.Response.Headers.ContentType.Count);
147+
Assert.Null(context.Response.ContentLength);
148+
149+
context.Response.Body.Position = 0;
150+
var responseBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
151+
Assert.Empty(responseBody);
152+
}
153+
154+
155+
156+
[Fact]
157+
public async Task GetNextInvocation_Returns_Event()
158+
{
159+
// Arrange
160+
var functionName = "testFunction";
161+
var testEvent = "{\"key\":\"value\"}";
162+
var eventContainer = new EventContainer(_mockRuntimeApiDataStore.Object, 1, testEvent, true);
163+
164+
_mockRuntimeDataStore
165+
.Setup(x => x.TryActivateEvent(out eventContainer))
166+
.Returns(true);
167+
168+
var context = new DefaultHttpContext();
169+
var memoryStream = new NonClosingMemoryStream(); // Using custom non closing memory stream because if we use regular memory stream its closed after GetBextInvocation and we can't read it.
170+
context.Response.Body = memoryStream;
171+
172+
// Act
173+
await new LambdaRuntimeApi(_app).GetNextInvocation(context, functionName);
174+
175+
// Assert
176+
Assert.Equal(200, context.Response.StatusCode);
177+
Assert.True(context.Response.Headers.ContainsKey("Lambda-Runtime-Aws-Request-Id"));
178+
Assert.True(context.Response.Headers.ContainsKey("Lambda-Runtime-Trace-Id"));
179+
Assert.True(context.Response.Headers.ContainsKey("Lambda-Runtime-Invoked-Function-Arn"));
180+
Assert.Equal("application/json", context.Response.Headers["Content-Type"]);
181+
182+
memoryStream.Position = 0;
183+
using (var reader = new StreamReader(memoryStream, leaveOpen: true))
184+
{
185+
var responseBody = await reader.ReadToEndAsync();
186+
Assert.Equal(testEvent, responseBody);
187+
}
188+
}
189+
190+
[Fact]
191+
public void PostInitError_Logs_Error()
192+
{
193+
// Arrange
194+
var functionName = "testFunction";
195+
var errorType = "InitializationError";
196+
var error = "Failed to initialize";
197+
198+
// Act
199+
var result = new LambdaRuntimeApi(_app).PostInitError(functionName, errorType, error);
200+
201+
// Assert
202+
Assert.NotNull(result);
203+
var statusResponse = Assert.IsType<StatusResponse>((result as IValueHttpResult)?.Value);
204+
Assert.Equal("success", statusResponse.Status);
205+
}
206+
207+
[Fact]
208+
public async Task PostInvocationResponse_Reports_Success()
209+
{
210+
// Arrange
211+
var functionName = "testFunction";
212+
var awsRequestId = "request123";
213+
var response = "{\"result\":\"success\"}";
214+
215+
var context = new DefaultHttpContext();
216+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(response));
217+
context.Response.Body = new MemoryStream();
218+
219+
_mockRuntimeDataStore
220+
.Setup(x => x.ReportSuccess(awsRequestId, response));
221+
222+
// Act
223+
var result = await new LambdaRuntimeApi(_app).PostInvocationResponse(context, functionName, awsRequestId);
224+
225+
// Assert
226+
Assert.NotNull(result);
227+
var statusResponse = Assert.IsType<StatusResponse>((result as IValueHttpResult)?.Value);
228+
Assert.Equal("success", statusResponse.Status);
229+
230+
_mockRuntimeDataStore.Verify(x => x.ReportSuccess(awsRequestId, response), Times.Once);
231+
}
232+
233+
[Fact]
234+
public async Task PostError_Reports_Error()
235+
{
236+
// Arrange
237+
var functionName = "testFunction";
238+
var awsRequestId = "request123";
239+
var errorType = "HandlerError";
240+
var errorBody = "Function execution failed";
241+
242+
var context = new DefaultHttpContext();
243+
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(errorBody));
244+
context.Response.Body = new MemoryStream();
245+
246+
_mockRuntimeDataStore
247+
.Setup(x => x.ReportError(awsRequestId, errorType, errorBody));
248+
249+
// Act
250+
var result = await new LambdaRuntimeApi(_app).PostError(context, functionName, awsRequestId, errorType);
251+
252+
// Assert
253+
Assert.NotNull(result);
254+
var statusResponse = Assert.IsType<StatusResponse>((result as IValueHttpResult)?.Value);
255+
Assert.Equal("success", statusResponse.Status);
256+
257+
_mockRuntimeDataStore.Verify(x => x.ReportError(awsRequestId, errorType, errorBody), Times.Once);
258+
}
259+
260+
261+
262+
}
263+
264+
// Helper class to prevent stream from being closed
265+
public class NonClosingMemoryStream : MemoryStream
266+
{
267+
public override void Close() { }
268+
protected override void Dispose(bool disposing) { }
269+
}
270+

0 commit comments

Comments
 (0)