Skip to content

Commit 3db2f32

Browse files
committed
feat: added Approvals
1 parent 7d73fd6 commit 3db2f32

42 files changed

Lines changed: 2563 additions & 30 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Unicorn.Approvals/.gitignore

Lines changed: 405 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<AssemblyName>Unicorn.Approvals.ApprovalsService.Tests</AssemblyName>
5+
<RootNamespace>Unicorn.Approvals.ApprovalsService.Tests</RootNamespace>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<PackageReference Include="Amazon.Lambda.Core" Version="2.6.0" />
9+
<PackageReference Include="Amazon.Lambda.TestUtilities" Version="3.0.0" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
11+
<PackageReference Include="NSubstitute" Version="5.3.0" />
12+
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
13+
<PrivateAssets>all</PrivateAssets>
14+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
15+
</PackageReference>
16+
<PackageReference Include="xunit" Version="2.9.3" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.0">
18+
<PrivateAssets>all</PrivateAssets>
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
</PackageReference>
21+
</ItemGroup>
22+
<ItemGroup>
23+
<ProjectReference Include="..\ApprovalsService\ApprovalsService.csproj" />
24+
</ItemGroup>
25+
<ItemGroup>
26+
<None Update="events\DynamoDbStreamEvents\contract_status_changed_draft.json">
27+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
28+
</None>
29+
<None Update="events\DynamoDbStreamEvents\contract_status_draft_waiting_for_approval.json">
30+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
31+
</None>
32+
<None Update="events\DynamoDbStreamEvents\contract_status_changed_approved_waiting_for_approval.json">
33+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
34+
</None>
35+
<None Update="events\DynamoDbStreamEvents\contract_status_changed_approved.json">
36+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
37+
</None>
38+
<None Update="events\StreamEvents\contract_status_changed_approved.json">
39+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
40+
</None>
41+
<None Update="events\StreamEvents\contract_status_changed_approved_waiting_for_approval.json">
42+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
43+
</None>
44+
<None Update="events\StreamEvents\contract_status_changed_draft.json">
45+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
46+
</None>
47+
<None Update="events\StreamEvents\contract_status_draft_waiting_for_approval.json">
48+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
49+
</None>
50+
</ItemGroup>
51+
</Project>
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Amazon.DynamoDBv2.DataModel;
5+
using Amazon.StepFunctions;
6+
using Amazon.StepFunctions.Model;
7+
using NSubstitute;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
11+
namespace Unicorn.Approvals.ApprovalsService.Tests;
12+
13+
[Collection("Sequential")]
14+
public class PropertiesApprovalSyncFunctionTest
15+
{
16+
private readonly ITestOutputHelper _testOutputHelper;
17+
18+
private const string Token = "AQDIAAAAKgAAAAMAAAAAAAAAAapnwdMR3Z7RAg3IavSq2hbHt+CZPIQYaakFO6Em9Ik00VsGcaznxo" +
19+
"tIUGB2t7kihvuu/ffeoF+Z4yg4dggTxtzVwvRJwUDQr33/s/LhJyvEfNS57PXCv/CYFssJZ+28FRCAYb" +
20+
"GekKaopYhaUlvq0taLGMaEfIbRBeLUmLHInDkPPDbwTA==N0LRrCP3bIFW1MPkMQC3kd35p8yflvpThH" +
21+
"Ceviqe3qeyKBX03+ziocuyvNHVpktMuECnHL3MN9a6BfpSM1KItYI+qdIC74ls83ALSjjOs8G8pOz4Ou" +
22+
"dcliLYAvmZPRQXvFaw6aSMLfJJ7xRpEFSBwj1zDzbadMLtfudG78pmZX1m75/idMU5gz0UPkC87bVQN6" +
23+
"Kyjl7obxAeUO4aoqGJeNz6WbJQtrsUhiQWVEH/AwjUAj9Q0DwRqRPqeWyrv4MIMKea/Xu9vhXbcS+zPWP" +
24+
"q8onN9fyAqoMNh64K6wSGWxtAbaaByKxNpQu7o9ho/Iu/ME0KOAqyUK6vcnXOpIwIoMAZiG34KF4UnQsD0" +
25+
"gIokQcGLbehMGRixvEJMDZIloLxkuH0jvpIvD5xokGxpHwiVMISi2XRJ92nnGmWTCLWqsqJlsg4We6snJp" +
26+
"0Akw2w1Nt41lgY8kWkjxHNWEHDIMjzx1zeWiVa9b9aDDcckb71ouknJCN4gbxVs+yP30M97qnCEmMrc25" +
27+
"Yq7cEXLhu5Dh";
28+
29+
public PropertiesApprovalSyncFunctionTest(ITestOutputHelper testOutputHelper)
30+
{
31+
_testOutputHelper = testOutputHelper;
32+
// Set env variable for Powertools Metrics
33+
Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE", "ContractService");
34+
}
35+
36+
[Fact]
37+
public void StatusIsDraftSyncShouldNotSendTaskSuccess()
38+
{
39+
// Setup
40+
var ddbEvent = TestHelpers.LoadDynamoDbEventSource("./events/StreamEvents/contract_status_changed_draft.json");
41+
42+
var mockDynamoDbContext = Substitute.For<IDynamoDBContext>();
43+
44+
mockDynamoDbContext.LoadAsync<ContractStatusItem>(Arg.Any<string>(), Arg.Is(CancellationToken.None))
45+
.Returns(new ContractStatusItem
46+
{
47+
PropertyId = "usa/anytown/main-street/999",
48+
ContractId = Guid.NewGuid(),
49+
ContractStatus = "DRAFT",
50+
ContractLastModifiedOn = DateTime.Today,
51+
SfnWaitApprovedTaskToken = null
52+
});
53+
54+
var mockStepFunctionsClient = Substitute.ForPartsOf<AmazonStepFunctionsClient>();
55+
mockStepFunctionsClient.Received(0)
56+
.SendTaskSuccessAsync(Arg.Any<SendTaskSuccessRequest>(),
57+
Arg.Any<CancellationToken>());
58+
59+
var context = TestHelpers.NewLambdaContext();
60+
61+
var function =
62+
new PropertiesApprovalSyncFunction(mockStepFunctionsClient, mockDynamoDbContext);
63+
64+
var handler = function.FunctionHandler(ddbEvent, context);
65+
66+
67+
}
68+
69+
70+
[Fact]
71+
public Task StatusIsApprovedNoTokenSyncShouldNotSendTaskSuccess()
72+
{
73+
var ddbEvent =
74+
TestHelpers.LoadDynamoDbEventSource("./events/StreamEvents/contract_status_changed_approved.json");
75+
76+
var mockDynamoDbContext = Substitute.For<IDynamoDBContext>();
77+
mockDynamoDbContext.LoadAsync<ContractStatusItem>(Arg.Any<string>(), Arg.Any<CancellationToken>())
78+
.Returns(new ContractStatusItem
79+
{
80+
PropertyId = "usa/anytown/main-street/999",
81+
ContractId = Guid.NewGuid(),
82+
ContractStatus = "APPROVED",
83+
ContractLastModifiedOn = DateTime.Today,
84+
SfnWaitApprovedTaskToken = null
85+
});
86+
87+
var mockStepFunctionsClient = Substitute.ForPartsOf<AmazonStepFunctionsClient>();
88+
mockStepFunctionsClient.Received(0).
89+
SendTaskSuccessAsync(Arg.Any<SendTaskSuccessRequest>(),
90+
Arg.Any<CancellationToken>());
91+
92+
var context = TestHelpers.NewLambdaContext();
93+
94+
var function =
95+
new PropertiesApprovalSyncFunction(mockStepFunctionsClient, mockDynamoDbContext);
96+
97+
function.FunctionHandler(ddbEvent, context);
98+
99+
mockStepFunctionsClient.Received(0).
100+
SendTaskSuccessAsync(Arg.Any<SendTaskSuccessRequest>(),
101+
Arg.Any<CancellationToken>());
102+
103+
return Task.CompletedTask;
104+
}
105+
106+
107+
[Fact]
108+
public Task StatusIsDraftWithTokenSyncShouldNotSendTaskSuccess()
109+
{
110+
var ddbEvent =
111+
TestHelpers.LoadDynamoDbEventSource(
112+
"./events/StreamEvents/contract_status_draft_waiting_for_approval.json");
113+
114+
var mockDynamoDbContext = Substitute.For<IDynamoDBContext>();
115+
116+
mockDynamoDbContext.LoadAsync<ContractStatusItem>(Arg.Any<string>(), CancellationToken.None)
117+
.Returns(new ContractStatusItem
118+
{
119+
PropertyId = "usa/anytown/main-street/999",
120+
ContractId = Guid.NewGuid(),
121+
ContractStatus = "DRAFT",
122+
ContractLastModifiedOn = DateTime.Today,
123+
SfnWaitApprovedTaskToken = Token
124+
});
125+
126+
var mockStepFunctionsClient = Substitute.ForPartsOf<AmazonStepFunctionsClient>();
127+
128+
var context = TestHelpers.NewLambdaContext();
129+
130+
var function =
131+
new PropertiesApprovalSyncFunction(mockStepFunctionsClient, mockDynamoDbContext);
132+
133+
var handler = function.FunctionHandler(ddbEvent, context);
134+
135+
mockStepFunctionsClient.Received(0).
136+
SendTaskSuccessAsync(Arg.Any<SendTaskSuccessRequest>(),
137+
Arg.Any<CancellationToken>());
138+
139+
return Task.CompletedTask;
140+
}
141+
142+
143+
[Fact]
144+
public Task StatusIsApprovedWithTokenSyncShouldSendTaskSuccess()
145+
{
146+
var ddbEvent =
147+
TestHelpers.LoadDynamoDbEventSource(
148+
"./events/StreamEvents/contract_status_changed_approved_waiting_for_approval.json");
149+
150+
var mockStepFunctionsClient = Substitute.ForPartsOf<AmazonStepFunctionsClient>();
151+
var mockDynamoDbContext = Substitute.For<IDynamoDBContext>();
152+
153+
mockDynamoDbContext.LoadAsync<ContractStatusItem>(Arg.Any<string>(), Arg.Is(CancellationToken.None))
154+
.Returns(new ContractStatusItem
155+
{
156+
PropertyId = "usa/anytown/main-street/999",
157+
ContractId = Guid.NewGuid(),
158+
ContractStatus = "APPROVED",
159+
ContractLastModifiedOn = DateTime.Today,
160+
SfnWaitApprovedTaskToken = Token
161+
});
162+
var context = TestHelpers.NewLambdaContext();
163+
164+
var function =
165+
new PropertiesApprovalSyncFunction(mockStepFunctionsClient, mockDynamoDbContext);
166+
var handler = function.FunctionHandler(ddbEvent, context);
167+
168+
mockStepFunctionsClient.Received(1).
169+
SendTaskSuccessAsync(Arg.Any<SendTaskSuccessRequest>(),
170+
Arg.Any<CancellationToken>());
171+
172+
return Task.CompletedTask;
173+
}
174+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: MIT-0
3+
4+
using System;
5+
using System.IO;
6+
using System.Text;
7+
using Amazon.Lambda.Core;
8+
using Amazon.Lambda.DynamoDBEvents;
9+
using Amazon.Lambda.Serialization.SystemTextJson;
10+
using Amazon.Lambda.TestUtilities;
11+
12+
namespace Unicorn.Approvals.ApprovalsService.Tests;
13+
14+
public static class TestHelpers
15+
{
16+
public static DynamoDBEvent LoadDynamoDbEventSource(string filename)
17+
{
18+
var serializer = Activator.CreateInstance(typeof(DefaultLambdaJsonSerializer)) as ILambdaSerializer;
19+
20+
DynamoDBEvent ddbEvent = null;
21+
22+
using (var fileStream = LoadJsonTestFile(filename))
23+
{
24+
if (serializer != null)
25+
{
26+
ddbEvent = serializer.Deserialize<DynamoDBEvent>(fileStream);
27+
}
28+
}
29+
30+
return ddbEvent;
31+
}
32+
33+
// This utility method takes care of removing the BOM that System.Text.Json doesn't like.
34+
public static MemoryStream LoadJsonTestFile(string filename)
35+
{
36+
var json = File.ReadAllText(filename);
37+
return new MemoryStream(Encoding.UTF8.GetBytes(json));
38+
}
39+
40+
/// <summary>
41+
/// Creates new Lambda context
42+
/// </summary>
43+
/// <returns>TestLambdaContext</returns>
44+
public static TestLambdaContext NewLambdaContext()
45+
{
46+
return new TestLambdaContext
47+
{
48+
FunctionName = "uni-prop-local-properties-PropertiesApprovalSyncFu-JWaDc4Gm3SgL",
49+
FunctionVersion = "1",
50+
MemoryLimitInMB = 215,
51+
AwsRequestId = Guid.NewGuid().ToString("D")
52+
};
53+
}
54+
55+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"DetailType": "ContractStatusChanged",
4+
"Source": "unicorn.contracts",
5+
"EventBusName": "UnicornPropertiesBus-local",
6+
"Detail": "{ \"ContractUpdatedOn\": \"10/08/2022 19:56:30\", \"ContractId\": \"f2bedc80-3dc8-4544-9140-9b606d71a6ee\", \"PropertyId\": \"usa/anytown/main-street/111\", \"ContractStatus\": \"APPROVED\" }"
7+
}
8+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"DetailType": "ContractStatusChanged",
4+
"Source": "unicorn.contracts",
5+
"EventBusName": "UnicornPropertiesBus-local",
6+
"Detail": "{ \"ContractUpdatedOn\": \"10/08/2022 19:56:30\", \"ContractId\": \"f2bedc80-3dc8-4544-9140-9b606d71a6ee\", \"PropertyId\": \"usa/anytown/main-street/111\", \"ContractStatus\": \"DRAFT\" }"
7+
}
8+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"DetailType": "ContractStatusChanged",
4+
"Source": "unicorn.contracts",
5+
"EventBusName": "UnicornPropertiesBus-local",
6+
"Detail": "{ \"ContractUpdatedOn\": \"10/08/2022 19:56:30\", \"ContractId\": \"9183453b-d284-4466-a2d9-f00b1d569ad7\", \"PropertyId\": \"usa/anytown/main-street/222\", \"ContractStatus\": \"APPROVED\" }"
7+
}
8+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"DetailType": "ContractStatusChanged",
4+
"Source": "unicorn.contracts",
5+
"EventBusName": "UnicornPropertiesBus-local",
6+
"Detail": "{ \"ContractUpdatedOn\": \"10/08/2022 19:56:30\", \"ContractId\": \"9183453b-d284-4466-a2d9-f00b1d569ad7\", \"PropertyId\": \"usa/anytown/main-street/222\", \"ContractStatus\": \"DRAFT\" }"
7+
}
8+
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"version": "0",
3+
"id": "f849f683-76e1-1c84-669d-544a9828dfef",
4+
"detail-type": "PublicationApprovalRequested",
5+
"source": "unicorn.web",
6+
"account": "123456789012",
7+
"time": "2022-08-16T06:33:05Z",
8+
"region": "ap-southeast-2",
9+
"resources": [],
10+
"detail": {
11+
"PropertyId": "usa/anytown/main-street/123",
12+
"Address": {
13+
"Country": "USA",
14+
"City": "Anytown",
15+
"Street": "Main Street",
16+
"Number": 123
17+
},
18+
"Description": "This classic Anytown estate comes with a covetable lake view. The romantic and comfortable backyard is the perfect setting for unicorn get-togethers. The open concept Main Stable is fully equipped with all the desired amenities. Second floor features 6 straw bales including large Rainbow Suite with private training pool terrace and Jr Sparkles Suite.",
19+
"Contract": "sale",
20+
"ListPrice": 200,
21+
"Currency": "SPL",
22+
"Images": [
23+
"property_images/prop1_exterior1.jpg",
24+
"property_images/prop1_interior1.jpg",
25+
"property_images/prop1_interior2.jpg",
26+
"property_images/prop1_interior3.jpg",
27+
"property_images/prop1_interior4-bad.jpg"
28+
]
29+
}
30+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"DetailType": "PublicationApprovalRequested",
4+
"Source": "unicorn.web",
5+
"EventBusName": "UnicornPropertiesBus-local",
6+
"Detail": "{\"PropertyId\":\"usa/anytown/main-street/222\",\"Address\":{\"Country\":\"USA\",\"City\":\"Anytown\",\"Street\":\"Main Street\",\"Number\":222},\"Description\":\"This classic Anytown estate comes with a covetable lake view. The romantic and comfortable backyard is the perfect setting for unicorn get-togethers. The open concept Main Stable is fully equipped with all the desired amenities. Second floor features 6 straw bales including large Rainbow Suite with private training pool terrace and Jr Sparkles Suite.\",\"Contract\":\"sale\",\"ListPrice\":200,\"Currency\":\"SPL\",\"Images\":[\"property_images/prop1_exterior1.jpg\",\"property_images/prop1_interior1.jpg\",\"property_images/prop1_interior2.jpg\",\"property_images/prop1_interior3.jpg\"]}"
7+
}
8+
]

0 commit comments

Comments
 (0)