Skip to content

Commit b54c735

Browse files
committed
Added unit tests
1 parent 95917b7 commit b54c735

19 files changed

+834
-1
lines changed

dotnet-test-samples/.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -402,4 +402,6 @@ FodyWeavers.xsd
402402
### VisualStudio Patch ###
403403
# Additional files built by Visual Studio
404404

405-
# End of https://www.toptal.com/developers/gitignore/api/visualstudio
405+
# End of https://www.toptal.com/developers/gitignore/api/visualstudio
406+
407+
.DS_Store
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# AWS Lambda Simple S3 Function Project
2+
3+
This starter project consists of:
4+
* serverless.template - an AWS CloudFormation Serverless Application Model template file for declaring your Serverless functions and other AWS resources
5+
* Function.cs - class file containing a class with a single function handler method
6+
* aws-lambda-tools-defaults.json - default argument settings for use with Visual Studio and command line deployment tools for AWS
7+
8+
You may also have a test project depending on the options selected.
9+
10+
The generated function handler responds to events on an Amazon S3 bucket. The handler receives the bucket and object key details in an S3Event instance and returns the content type of the object as the function output. Replace the body of this method, and parameters, to suit your needs.
11+
12+
After deploying your function you must configure an Amazon S3 bucket as an event source to trigger your Lambda function.
13+
14+
## Here are some steps to follow from Visual Studio:
15+
16+
To deploy your Serverless application, right click the project in Solution Explorer and select *Publish to AWS Lambda*.
17+
18+
To view your deployed application open the Stack View window by double-clicking the stack name shown beneath the AWS CloudFormation node in the AWS Explorer tree. The Stack View also displays the root URL to your published application.
19+
20+
## Here are some steps to follow to get started from the command line:
21+
22+
Once you have edited your template and code you can deploy your application using the [Amazon.Lambda.Tools Global Tool](https://github.com/aws/aws-extensions-for-dotnet-cli#aws-lambda-amazonlambdatools) from the command line.
23+
24+
Install Amazon.Lambda.Tools Global Tools if not already installed.
25+
```
26+
dotnet tool install -g Amazon.Lambda.Tools
27+
```
28+
29+
If already installed check if new version is available.
30+
```
31+
dotnet tool update -g Amazon.Lambda.Tools
32+
```
33+
34+
Execute unit tests
35+
```
36+
cd "async-lambda-sqs/test/async-lambda-sqs.Tests"
37+
dotnet test
38+
```
39+
40+
Deploy function to AWS Lambda
41+
```
42+
cd "async-lambda-sqs/src/async-lambda-sqs"
43+
dotnet lambda deploy-serverless
44+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.6.33815.320
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{521DD4B5-B08C-4AFC-8D93-3E5552DE2C2D}"
7+
ProjectSection(SolutionItems) = preProject
8+
serverless.template = serverless.template
9+
EndProjectSection
10+
EndProject
11+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5E136BA6-1662-401B-8D0D-0BA0595A942D}"
12+
EndProject
13+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{81458A7E-41B1-4553-AE5D-227E4E9A7D47}"
14+
EndProject
15+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S3Notifications", "src\S3Notifications\S3Notifications.csproj", "{0645487F-46F6-4BA7-8334-2997E9CC70B9}"
16+
EndProject
17+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S3Notificaztions.E2ETests", "tests\S3Notifications.E2ETests\S3Notificaztions.E2ETests.csproj", "{AE0182DD-A9F0-419D-89F5-F346AF920CD5}"
18+
EndProject
19+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S3Notifications.UnitTests", "tests\S3Notifications.UnitTests\S3Notifications.UnitTests.csproj", "{C3905C57-8269-41C1-AB11-B5BA39577C66}"
20+
EndProject
21+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S3Notificatrions.TestUtilities", "tests\S3Notifications.TestUtilities\S3Notificatrions.TestUtilities.csproj", "{33A91439-4C1F-4F97-9BC2-F3003008F010}"
22+
EndProject
23+
Global
24+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
25+
Debug|Any CPU = Debug|Any CPU
26+
Release|Any CPU = Release|Any CPU
27+
EndGlobalSection
28+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
29+
{0645487F-46F6-4BA7-8334-2997E9CC70B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30+
{0645487F-46F6-4BA7-8334-2997E9CC70B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
31+
{0645487F-46F6-4BA7-8334-2997E9CC70B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
32+
{0645487F-46F6-4BA7-8334-2997E9CC70B9}.Release|Any CPU.Build.0 = Release|Any CPU
33+
{AE0182DD-A9F0-419D-89F5-F346AF920CD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34+
{AE0182DD-A9F0-419D-89F5-F346AF920CD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
35+
{AE0182DD-A9F0-419D-89F5-F346AF920CD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
36+
{AE0182DD-A9F0-419D-89F5-F346AF920CD5}.Release|Any CPU.Build.0 = Release|Any CPU
37+
{C3905C57-8269-41C1-AB11-B5BA39577C66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38+
{C3905C57-8269-41C1-AB11-B5BA39577C66}.Debug|Any CPU.Build.0 = Debug|Any CPU
39+
{C3905C57-8269-41C1-AB11-B5BA39577C66}.Release|Any CPU.ActiveCfg = Release|Any CPU
40+
{C3905C57-8269-41C1-AB11-B5BA39577C66}.Release|Any CPU.Build.0 = Release|Any CPU
41+
{33A91439-4C1F-4F97-9BC2-F3003008F010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42+
{33A91439-4C1F-4F97-9BC2-F3003008F010}.Debug|Any CPU.Build.0 = Debug|Any CPU
43+
{33A91439-4C1F-4F97-9BC2-F3003008F010}.Release|Any CPU.ActiveCfg = Release|Any CPU
44+
{33A91439-4C1F-4F97-9BC2-F3003008F010}.Release|Any CPU.Build.0 = Release|Any CPU
45+
EndGlobalSection
46+
GlobalSection(SolutionProperties) = preSolution
47+
HideSolutionNode = FALSE
48+
EndGlobalSection
49+
GlobalSection(NestedProjects) = preSolution
50+
{0645487F-46F6-4BA7-8334-2997E9CC70B9} = {5E136BA6-1662-401B-8D0D-0BA0595A942D}
51+
{AE0182DD-A9F0-419D-89F5-F346AF920CD5} = {81458A7E-41B1-4553-AE5D-227E4E9A7D47}
52+
{C3905C57-8269-41C1-AB11-B5BA39577C66} = {81458A7E-41B1-4553-AE5D-227E4E9A7D47}
53+
{33A91439-4C1F-4F97-9BC2-F3003008F010} = {81458A7E-41B1-4553-AE5D-227E4E9A7D47}
54+
EndGlobalSection
55+
GlobalSection(ExtensibilityGlobals) = postSolution
56+
SolutionGuid = {6BE69204-644C-49EC-AE6A-E08CA582876F}
57+
EndGlobalSection
58+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"AWSTemplateFormatVersion": "2010-09-09",
3+
"Transform": "AWS::Serverless-2016-10-31",
4+
"Description": "Template that creates a S3 bucket and a Lambda function that will be invoked when new objects are upload to the bucket.",
5+
"Parameters": {
6+
"BucketName": {
7+
"Type": "String",
8+
"Description": "Name of S3 bucket to be created. The Lambda function will be invoked when new objects are upload to the bucket. If left blank a name will be generated.",
9+
"MinLength": "0"
10+
}
11+
},
12+
"Conditions": {
13+
"BucketNameGenerated": {
14+
"Fn::Equals": [
15+
{
16+
"Ref": "BucketName"
17+
},
18+
""
19+
]
20+
}
21+
},
22+
"Resources": {
23+
"Bucket": {
24+
"Type": "AWS::S3::Bucket",
25+
"Properties": {
26+
"BucketName": {
27+
"Fn::If": [
28+
"BucketNameGenerated",
29+
{
30+
"Ref": "AWS::NoValue"
31+
},
32+
{
33+
"Ref": "BucketName"
34+
}
35+
]
36+
}
37+
}
38+
},
39+
"S3Function": {
40+
"Type": "AWS::Serverless::Function",
41+
"Properties": {
42+
"Handler": "async-lambda-sqs::async_lambda_sqs.Function::FunctionHandler",
43+
"Runtime": "dotnet6",
44+
"CodeUri": "",
45+
"Description": "Default function",
46+
"MemorySize": 256,
47+
"Timeout": 30,
48+
"Role": null,
49+
"Policies": [
50+
"AWSLambda_FullAccess",
51+
"AmazonS3ReadOnlyAccess"
52+
],
53+
"Events": {
54+
"NewImagesBucket": {
55+
"Type": "S3",
56+
"Properties": {
57+
"Bucket": {
58+
"Ref": "Bucket"
59+
},
60+
"Events": [
61+
"s3:ObjectCreated:*"
62+
]
63+
}
64+
}
65+
}
66+
}
67+
}
68+
},
69+
"Outputs": {
70+
"Bucket": {
71+
"Value": {
72+
"Ref": "Bucket"
73+
},
74+
"Description": "Bucket that will invoke the lambda function when new objects are created."
75+
}
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System.Text.Json;
2+
using Amazon.Lambda.Core;
3+
using Amazon.Lambda.S3Events;
4+
using Amazon.SQS;
5+
using Amazon.SQS.Model;
6+
7+
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
8+
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
9+
10+
namespace S3Notifications;
11+
12+
public class Function
13+
{
14+
private const string QueueNameEnvKey = "QUEUE_NAME";
15+
private IAmazonSQS SqsClient { get; }
16+
17+
/// <summary>
18+
/// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
19+
/// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
20+
/// region the Lambda function is executed in.
21+
/// </summary>
22+
public Function()
23+
{
24+
SqsClient = new AmazonSQSClient();
25+
}
26+
27+
/// <summary>
28+
/// Constructs an instance with a preconfigured SQS client. This can be used for testing the outside of the Lambda environment.
29+
/// </summary>
30+
31+
public Function(IAmazonSQS sqsClient)
32+
{
33+
SqsClient = sqsClient;
34+
}
35+
36+
/// <summary>
37+
/// This method is called for every Lambda invocation. This method takes in an S3 event object and can be used
38+
/// to respond to S3 notifications.
39+
/// </summary>
40+
/// <param name="evnt"></param>
41+
/// <param name="context"></param>
42+
/// <returns></returns>
43+
public async Task<string?> FunctionHandler(S3Event evnt, ILambdaContext context)
44+
{
45+
if (evnt.Records?.Count is null or 0)
46+
{
47+
return null;
48+
}
49+
50+
var s3Event = evnt.Records[0];
51+
52+
var message = new S3NotificationMessage(s3Event.S3.Bucket.Name, s3Event.S3.Object.Key, s3Event.EventName);
53+
54+
var queueName = Environment.GetEnvironmentVariable(QueueNameEnvKey);
55+
56+
if (queueName == null)
57+
{
58+
throw new ApplicationException($"{QueueNameEnvKey} was not set");
59+
}
60+
61+
return await SendMessageToQueue(queueName, message);
62+
}
63+
64+
private async Task<string> SendMessageToQueue(string? queueName, S3NotificationMessage message)
65+
{
66+
var getQueueUrlResponse = await SqsClient.GetQueueUrlAsync(queueName);
67+
68+
var jsonString = JsonSerializer.Serialize(message);
69+
70+
var sendMessageRequest = new SendMessageRequest(getQueueUrlResponse.QueueUrl, jsonString);
71+
var sendMessageResponse = await SqsClient.SendMessageAsync(sendMessageRequest);
72+
73+
return sendMessageResponse.MessageId;
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace S3Notifications;
2+
3+
public record S3NotificationMessage(string BucketName, string ObjectKey, string EventName);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net6.0</TargetFramework>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<Nullable>enable</Nullable>
6+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
7+
<AWSProjectType>Lambda</AWSProjectType>
8+
<!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
9+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
10+
<!-- Generate ready to run images during publishing to improve cold start time. -->
11+
<PublishReadyToRun>true</PublishReadyToRun>
12+
</PropertyGroup>
13+
<ItemGroup>
14+
<None Remove=".DS_Store" />
15+
</ItemGroup>
16+
<ItemGroup>
17+
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
18+
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.1" />
19+
<PackageReference Include="Amazon.Lambda.S3Events" Version="3.0.1" />
20+
<PackageReference Include="AWS.Lambda.Powertools.Logging" Version="1.1.1" />
21+
<PackageReference Include="AWSSDK.S3" Version="3.7.107.5" />
22+
<PackageReference Include="AWSSDK.SQS" Version="3.7.103.10" />
23+
</ItemGroup>
24+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"Information": [
3+
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
4+
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
5+
"dotnet lambda help",
6+
"All the command line options for the Lambda command can be specified in this file."
7+
],
8+
"profile": "dhelper@isenguard",
9+
"region": "us-east-1",
10+
"configuration": "Release",
11+
"s3-prefix": "async-lambda-sqs/",
12+
"template": "serverless.template",
13+
"template-parameters": ""
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// using Xunit;
2+
// using Amazon.Lambda;
3+
// using Amazon.Lambda.Core;
4+
// using Amazon.Lambda.TestUtilities;
5+
// using Amazon.Lambda.S3Events;
6+
//
7+
// using Amazon;
8+
// using Amazon.S3;
9+
// using Amazon.S3.Model;
10+
// using Amazon.S3.Util;
11+
// using System.Collections.Generic;
12+
//
13+
// namespace async_lambda_sqs.Tests;
14+
//
15+
// public class FunctionTest
16+
// {
17+
// [Fact]
18+
// public async Task TestS3EventLambdaFunction()
19+
// {
20+
// IAmazonS3 s3Client = new AmazonS3Client(RegionEndpoint.USWest2);
21+
//
22+
// var bucketName = "lambda-async-lambda-sqs-".ToLower() + DateTime.Now.Ticks;
23+
// var key = "text.txt";
24+
//
25+
// // Create a bucket an object to setup a test data.
26+
// await s3Client.PutBucketAsync(bucketName);
27+
// try
28+
// {
29+
// await s3Client.PutObjectAsync(new PutObjectRequest
30+
// {
31+
// BucketName = bucketName,
32+
// Key = key,
33+
// ContentBody = "sample data"
34+
// });
35+
//
36+
// // Setup the S3 event object that S3 notifications would create with the fields used by the Lambda function.
37+
// var s3Event = new S3Event
38+
// {
39+
// Records = new List<S3Event.S3EventNotificationRecord>
40+
// {
41+
// new S3Event.S3EventNotificationRecord
42+
// {
43+
// S3 = new S3Event.S3Entity
44+
// {
45+
// Bucket = new S3Event.S3BucketEntity {Name = bucketName },
46+
// Object = new S3Event.S3ObjectEntity {Key = key }
47+
// }
48+
// }
49+
// }
50+
// };
51+
//
52+
// // Invoke the lambda function and confirm the content type was returned.
53+
// var function = new Function(s3Client);
54+
// var contentType = await function.FunctionHandler(s3Event,new TestLambdaContext());
55+
//
56+
// Assert.Equal("text/plain", contentType);
57+
//
58+
// }
59+
// finally
60+
// {
61+
// // Clean up the test data
62+
// await AmazonS3Util.DeleteS3BucketWithObjectsAsync(s3Client, bucketName);
63+
// }
64+
// }
65+
// }

0 commit comments

Comments
 (0)