Skip to content
This repository was archived by the owner on Aug 18, 2021. It is now read-only.

Commit ba0c280

Browse files
committed
feat: adds unit tests for LightWorker
See #198 about the failing tests.
1 parent d437ed2 commit ba0c280

File tree

7 files changed

+301
-6
lines changed

7 files changed

+301
-6
lines changed

Liquid.All.sln

+13-6
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Domain.Tests", "test
4949
EndProject
5050
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Tests", "test\Liquid.Tests\Liquid.Tests.csproj", "{6BA7DC4B-0F66-415A-AE74-5DEA5D3943D5}"
5151
EndProject
52-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.OnAzure.Tests", "test\Liquid.OnAzure.Tests\Liquid.OnAzure.Tests.csproj", "{9DEA4109-7542-40D5-9CCF-A673C9787E6D}"
52+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Activation.Tests", "test\Liquid.Activation.Tests\Liquid.Activation.Tests.csproj", "{E5FCB486-0C76-4714-8AC3-3E468FC1DB61}"
53+
EndProject
54+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.OnAzure.Tests", "test\Liquid.OnAzure.Tests\Liquid.OnAzure.Tests.csproj", "{CE80BBAE-350E-444B-A4FD-D9B58C5214E2}"
5355
EndProject
5456
Global
5557
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -113,10 +115,14 @@ Global
113115
{6BA7DC4B-0F66-415A-AE74-5DEA5D3943D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
114116
{6BA7DC4B-0F66-415A-AE74-5DEA5D3943D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
115117
{6BA7DC4B-0F66-415A-AE74-5DEA5D3943D5}.Release|Any CPU.Build.0 = Release|Any CPU
116-
{9DEA4109-7542-40D5-9CCF-A673C9787E6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
117-
{9DEA4109-7542-40D5-9CCF-A673C9787E6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
118-
{9DEA4109-7542-40D5-9CCF-A673C9787E6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
119-
{9DEA4109-7542-40D5-9CCF-A673C9787E6D}.Release|Any CPU.Build.0 = Release|Any CPU
118+
{E5FCB486-0C76-4714-8AC3-3E468FC1DB61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
119+
{E5FCB486-0C76-4714-8AC3-3E468FC1DB61}.Debug|Any CPU.Build.0 = Debug|Any CPU
120+
{E5FCB486-0C76-4714-8AC3-3E468FC1DB61}.Release|Any CPU.ActiveCfg = Release|Any CPU
121+
{E5FCB486-0C76-4714-8AC3-3E468FC1DB61}.Release|Any CPU.Build.0 = Release|Any CPU
122+
{CE80BBAE-350E-444B-A4FD-D9B58C5214E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
123+
{CE80BBAE-350E-444B-A4FD-D9B58C5214E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
124+
{CE80BBAE-350E-444B-A4FD-D9B58C5214E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
125+
{CE80BBAE-350E-444B-A4FD-D9B58C5214E2}.Release|Any CPU.Build.0 = Release|Any CPU
120126
EndGlobalSection
121127
GlobalSection(SolutionProperties) = preSolution
122128
HideSolutionNode = FALSE
@@ -136,7 +142,8 @@ Global
136142
{5279F425-0A2C-4889-968D-FFEB25975453} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
137143
{90CF0830-CD3C-4DD7-8C90-584772885BE0} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
138144
{6BA7DC4B-0F66-415A-AE74-5DEA5D3943D5} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
139-
{9DEA4109-7542-40D5-9CCF-A673C9787E6D} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
145+
{E5FCB486-0C76-4714-8AC3-3E468FC1DB61} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
146+
{CE80BBAE-350E-444B-A4FD-D9B58C5214E2} = {6A0B6B3D-15FE-4C0B-97A1-7897E31C0C4E}
140147
EndGlobalSection
141148
GlobalSection(ExtensibilityGlobals) = postSolution
142149
SolutionGuid = {1E0953B7-A30F-4440-9719-22CA017677B5}

src/Liquid.Activation/Liquid.Activation.csproj

+7
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@
1515
<DebugType>full</DebugType>
1616
<DebugSymbols>true</DebugSymbols>
1717
</PropertyGroup>
18+
<ItemGroup>
19+
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
20+
</ItemGroup>
1821
<ItemGroup>
1922
<Compile Remove="ServiceBus\**" />
2023
<EmbeddedResource Remove="ServiceBus\**" />
2124
<None Remove="ServiceBus\**" />
2225
</ItemGroup>
2326
<ItemGroup>
2427
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
28+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113">
29+
<PrivateAssets>all</PrivateAssets>
30+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
31+
</PackageReference>
2532
</ItemGroup>
2633
<ItemGroup>
2734
<ProjectReference Include="..\Liquid.Base\Liquid.Base.csproj" />

src/Liquid.Activation/Worker/LightWorker.cs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Collections;
1010
using System.Collections.Generic;
11+
using System.Dynamic;
1112
using System.Linq;
1213
using System.Reflection;
1314
using System.Text;

src/Liquid.Base/Workbench.cs

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.IO;
78
using Liquid.Base.Interfaces.Polly;
89
using Liquid.Interfaces;
@@ -14,6 +15,10 @@ namespace Liquid
1415
/// Provides a global way to configure a Liquid application.
1516
/// </summary>
1617
[Obsolete("Please use the correct spelled class, Liquid.Base.Workbench")]
18+
[SuppressMessage(
19+
"StyleCop.CSharp.MaintainabilityRules",
20+
"SA1402:File may only contain a single type",
21+
Justification = "Obsolete class will be removed.")]
1722
public static class WorkBench
1823
{
1924
public static ILightRepository Repository => Workbench.Instance.Repository;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright (c) Avanade Inc. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System;
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.IdentityModel.Tokens.Jwt;
7+
using System.Linq;
8+
using System.Security.Claims;
9+
using System.Security.Cryptography;
10+
using System.Security.Cryptography.X509Certificates;
11+
using System.Security.Principal;
12+
using System.Text;
13+
using Liquid.Base;
14+
using Liquid.Interfaces;
15+
using Microsoft.AspNetCore.Authorization;
16+
using Microsoft.IdentityModel.Tokens;
17+
using Newtonsoft.Json;
18+
using NSubstitute;
19+
using Xunit;
20+
21+
namespace Liquid.Activation.Tests
22+
{
23+
public class LightWorkerTests
24+
{
25+
public LightWorkerTests()
26+
{
27+
Workbench.Instance.Reset();
28+
Workbench.Instance.AddToCache(WorkbenchServiceType.Telemetry, Substitute.For<ILightTelemetry>());
29+
}
30+
31+
[Fact]
32+
public void InitializeWhenMockLightWorkerPresentThenQueueAndTopicsAreDiscovered()
33+
{
34+
var sut = new MockLightWorker();
35+
sut.Initialize();
36+
37+
Assert.Contains(
38+
MockLightWorker.TopicList,
39+
_ => _.MethodInfo.ReflectedType == typeof(MockLightWorker)
40+
&& _.MethodInfo.Name == nameof(MockLightWorker.TopicMethod));
41+
42+
Assert.Contains(
43+
MockLightWorker.QueueList,
44+
_ => _.MethodInfo.ReflectedType == typeof(MockLightWorker)
45+
&& _.MethodInfo.Name == nameof(MockLightWorker.QueueMethod));
46+
47+
// Given the static nature of LightWorker, we couldn't make this an isolated assertion
48+
// TODO: Refactor LightWorker and then make this isolated
49+
Assert.Throws<LightException>(() => new MockLightWorker().Initialize());
50+
}
51+
52+
[Fact]
53+
public void InvokeProcessWhenMessageIsntValidJsonThrows()
54+
{
55+
var actual = LightWorker.InvokeProcess(null, null);
56+
Assert.Null(actual);
57+
}
58+
59+
[Fact]
60+
public void InvokeProcessWhenMessageIsValidJsonParsesItCorrectly()
61+
{
62+
// ARRANGE
63+
var anonymous = new Foobar { Foo = "Bar" };
64+
var anonymousAsByteStream = ToJsonByteStream(anonymous);
65+
66+
var method = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.FoobarMethod));
67+
68+
// ACT
69+
var actual = (Foobar)LightWorker.InvokeProcess(method, anonymousAsByteStream);
70+
71+
// ASSERT
72+
Assert.Equal(anonymous.Foo, actual.Foo);
73+
}
74+
75+
[Fact]
76+
public void InvokeProcessWhenMethodHasZeroParametersDoesntParseMessage()
77+
{
78+
// ARRANGE
79+
var mi = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.ConstantMethod));
80+
81+
// ACT1
82+
var actual = (string)LightWorker.InvokeProcess(mi, null);
83+
84+
// ASSERT
85+
Assert.Equal(MethodsCollection.Value, actual);
86+
}
87+
88+
[Fact]
89+
public void InvokeProcessWhenMethodHasAuthorizeAndMessageHasNoContextThrows()
90+
{
91+
// ARRANGE
92+
var mi = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.AuthorizedRoleCustomer));
93+
var data = ToJsonByteStream(new Foobar { Context = null });
94+
95+
// ACT
96+
Assert.ThrowsAny<LightException>(() => LightWorker.InvokeProcess(mi, data));
97+
}
98+
99+
[Fact]
100+
public void InvokeProcessWhenMethodHasAuthorizeAndContextDoesntHaveAUserThrows()
101+
{
102+
// ARRANGE
103+
var mi = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.AuthorizedRoleCustomer));
104+
var data = ToJsonByteStream(new Foobar());
105+
106+
// ACT & ASSERT
107+
Assert.ThrowsAny<LightException>(() => LightWorker.InvokeProcess(mi, data));
108+
}
109+
110+
[Fact]
111+
public void InvokeProcessWhenMethodHasAuthorizeAndUserDoesntHaveRoleThrows()
112+
{
113+
// ARRANGE
114+
var mi = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.AuthorizedRoleCustomer));
115+
var data = ToJsonByteStream(new Foobar());
116+
117+
// ACT & ASSERT
118+
Assert.ThrowsAny<LightException>(() => LightWorker.InvokeProcess(mi, data));
119+
}
120+
121+
[Fact]
122+
public void InvokeProcessWhenMethodHasAuthorizeAndUserDoesntHaveRoleReturnsExpected()
123+
{
124+
// ARRANGE
125+
var mi = typeof(MethodsCollection).GetMethod(nameof(MethodsCollection.AuthorizedRoleAdmin));
126+
var obj = new Foobar();
127+
128+
var claims = new[] { new Claim(ClaimTypes.Role, "admin") };
129+
var key = HMAC.Create("HMACSHA1");
130+
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key.Key), "HMACSHA1");
131+
var jwt = new JwtSecurityToken(claims: claims, signingCredentials: signingCredentials);
132+
var headerAndPayload = $"{jwt.EncodedHeader}.{jwt.EncodedPayload}";
133+
var signature = Base64UrlEncoder.Encode(key.ComputeHash(Encoding.UTF8.GetBytes(headerAndPayload)));
134+
obj.TokenJwt = $"{headerAndPayload}.{signature}";
135+
136+
var data = ToJsonByteStream(obj);
137+
138+
// ACT
139+
var actual = LightWorker.InvokeProcess(mi, data);
140+
141+
// ASSERT
142+
Assert.Equal("Bar", actual);
143+
}
144+
145+
/// <summary>
146+
/// Serialize any object to a JSON string and then convert it to a bytestream.
147+
/// </summary>
148+
/// <param name="obj">The object to serialize.</param>
149+
/// <returns>A byestream containing the object as UTF8 bytes.</returns>
150+
private byte[] ToJsonByteStream(object obj)
151+
{
152+
var anonymousAsString = JsonConvert.SerializeObject(obj);
153+
var anonymousAsByteStream = Encoding.UTF8.GetBytes(anonymousAsString);
154+
155+
return anonymousAsByteStream;
156+
}
157+
158+
[SuppressMessage(
159+
"Design",
160+
"CA1034:Nested types should not be visible",
161+
Justification = "Must be public so LightWorker access the class")]
162+
public class Foobar : LightMessage<Foobar>
163+
{
164+
public string Foo { get; set; } = "Bar";
165+
166+
public override void Validate()
167+
{
168+
}
169+
}
170+
171+
private class MethodsCollection
172+
{
173+
public const string Value = "string";
174+
175+
public string ConstantMethod()
176+
{
177+
return Value;
178+
}
179+
180+
public Foobar FoobarMethod(Foobar foobar)
181+
{
182+
return foobar;
183+
}
184+
185+
[Authorize(Roles = "customer")]
186+
public string AuthorizedRoleCustomer(Foobar value)
187+
{
188+
return value.Foo;
189+
}
190+
191+
[Authorize(Roles = "admin")]
192+
public string AuthorizedRoleAdmin(Foobar value)
193+
{
194+
return value.Foo;
195+
}
196+
}
197+
}
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.0</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
8+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<Compile Remove="LightWorkerTests\**" />
13+
<EmbeddedResource Remove="LightWorkerTests\**" />
14+
<None Remove="LightWorkerTests\**" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<PackageReference Include="AutoFixture" Version="4.11.0" />
23+
<PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" />
24+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.6">
25+
<PrivateAssets>all</PrivateAssets>
26+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
27+
</PackageReference>
28+
<PackageReference Include="NSubstitute" Version="4.2.1" />
29+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113">
30+
<PrivateAssets>all</PrivateAssets>
31+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
32+
</PackageReference>
33+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
34+
<PackageReference Include="xunit" Version="2.4.0" />
35+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
36+
<PackageReference Include="coverlet.collector" Version="1.0.1" />
37+
</ItemGroup>
38+
39+
<ItemGroup>
40+
<ProjectReference Include="..\..\src\Liquid.Activation\Liquid.Activation.csproj" />
41+
</ItemGroup>
42+
43+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Avanade Inc. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Linq;
8+
using System.Reflection;
9+
using Xunit;
10+
11+
namespace Liquid.Activation.Tests
12+
{
13+
[MessageBus("asd")]
14+
public class MockLightWorker : LightWorker
15+
{
16+
public static List<(MethodInfo MethodInfo, TopicAttribute TopicAttribute)> TopicList => _topics
17+
.Select(kvp => (kvp.Key, kvp.Value))
18+
.ToList();
19+
20+
public static List<(MethodInfo MethodInfo, QueueAttribute QueueAttribute)> QueueList => _queues
21+
.Select(kvp => (kvp.Key, kvp.Value))
22+
.ToList();
23+
24+
[Topic("name", "subscriptionName", 10, true)]
25+
public static void TopicMethod()
26+
{
27+
}
28+
29+
[Queue("name")]
30+
public static void QueueMethod()
31+
{
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)