Skip to content

Commit

Permalink
Unit Test Setups and samples added
Browse files Browse the repository at this point in the history
  • Loading branch information
babaktaremi committed Jul 24, 2023
1 parent 87fa9b5 commit 5ead995
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 6 deletions.
8 changes: 2 additions & 6 deletions CleanArcTemplate.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@
<metadata>

<id>Bobby.CleanArcTemplate</id>
<version>3.9.1</version>
<version>4.0.0</version>
<title>Clean Architecture Solution Template</title>
<authors>BabakTaremi</authors>
<description>Clean Architecture Solution Template for and .NET 7. With many features available out of the box</description>
<summary>
Ready to develop template based on clean architecture principles. Supports ASP NET Core Identity integrated with JWE tokens, OTP authentication, stand alone plugin development, CQRS pattern using MediatR library and dynamic permission management system out of the box
</summary>
<releaseNotes>
Solution Structure Got Cleaned Up For Better Project View
API Models Got Removed Because they were Redundant. (Command And Query Models can be used as API model can be validatable Objects instead)

UnAuthorized API Response Added.
Span Enricher added to Serilog and Request ID changed to hex (W3C logging model)
Unit Test Setups added. Also database is now easy to test with Sqlite in memory feature.
</releaseNotes>


Expand Down
23 changes: 23 additions & 0 deletions CleanArcTemplate.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArc.Web.Api", "src\API
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArc.WebFramework", "src\API\CleanArc.WebFramework\CleanArc.WebFramework.csproj", "{44DD0A96-BA65-476E-BC59-C8D2CFA703B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{77986571-8153-4120-AD08-36729310A56B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BaseSetup", "BaseSetup", "{34B1F72E-A991-4705-ACC5-08E65E46D26E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArc.Tests.Setup", "src\Tests\CleanArc.Tests.Setup\CleanArc.Tests.Setup.csproj", "{33AF382A-9E22-42F0-82E5-4F78BCFD40C1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{45FA88C0-9986-40E5-A2E2-7742302518D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArc.Test.Infrastructure.Identity", "src\Tests\CleanArc.Test.Infrastructure.Identity\CleanArc.Test.Infrastructure.Identity\CleanArc.Test.Infrastructure.Identity.csproj", "{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -75,6 +85,14 @@ Global
{44DD0A96-BA65-476E-BC59-C8D2CFA703B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44DD0A96-BA65-476E-BC59-C8D2CFA703B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44DD0A96-BA65-476E-BC59-C8D2CFA703B9}.Release|Any CPU.Build.0 = Release|Any CPU
{33AF382A-9E22-42F0-82E5-4F78BCFD40C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33AF382A-9E22-42F0-82E5-4F78BCFD40C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33AF382A-9E22-42F0-82E5-4F78BCFD40C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33AF382A-9E22-42F0-82E5-4F78BCFD40C1}.Release|Any CPU.Build.0 = Release|Any CPU
{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -94,6 +112,11 @@ Global
{8F7135E8-68C9-4DA8-AA06-04518EBB403B} = {542840FF-B0CC-4F8A-9F6E-1898BE0573D7}
{BE13FF32-B8D5-4AE7-B173-6CA96040B788} = {0E679B58-1D8A-4F5B-8838-6E4DD9258215}
{44DD0A96-BA65-476E-BC59-C8D2CFA703B9} = {0E679B58-1D8A-4F5B-8838-6E4DD9258215}
{77986571-8153-4120-AD08-36729310A56B} = {42CAB060-5D50-4E18-8F85-EBA5EB85B268}
{34B1F72E-A991-4705-ACC5-08E65E46D26E} = {77986571-8153-4120-AD08-36729310A56B}
{33AF382A-9E22-42F0-82E5-4F78BCFD40C1} = {34B1F72E-A991-4705-ACC5-08E65E46D26E}
{45FA88C0-9986-40E5-A2E2-7742302518D2} = {77986571-8153-4120-AD08-36729310A56B}
{54203B4F-3CE8-4EBA-B5E2-F7C985FACE60} = {45FA88C0-9986-40E5-A2E2-7742302518D2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {05C223B9-EA89-44B2-B9F5-D01181F85DFE}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\CleanArc.Tests.Setup\CleanArc.Tests.Setup.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using CleanArc.Domain.Entities.User;
using CleanArc.Tests.Setup.Setups;

namespace CleanArc.Test.Infrastructure.Identity
{
public class UserManagerTest: TestIdentitySetup
{
[Fact]
public async Task Duplicate_User_Names_Not_Allowed()
{
var user = new User()
{
UserName = "Test", Email = "Test@example.com"
};

var duplicateUser = new User()
{
UserName = "Test",
Email = "Test@example.com"
};

var createUserResult = await base.TestAppUserManager.CreateUser(user);

var duplicateCreateUserResult = await base.TestAppUserManager.CreateUser(duplicateUser);

Assert.False(duplicateCreateUserResult.Succeeded);
}

[Fact]
public async Task Create_Phone_Confirmation_Code_And_Verify()
{
var user = new User()
{
UserName = "Test",
Email = "test@example.com",
PhoneNumber = "09123456789"
};

var createUserResult = await base.TestAppUserManager.CreateUser(user);

var otpCode=await base.TestAppUserManager.GeneratePhoneNumberConfirmationToken(user,user.PhoneNumber);

var confirmPhoneNumberResult=await base.TestAppUserManager.ChangePhoneNumber(user,user.PhoneNumber,otpCode);


Assert.NotNull(otpCode);
Assert.True(confirmPhoneNumberResult.Succeeded);
}

[Fact]
public async Task Generate_And_Verify_Otp_Code()
{
var user = new User()
{
UserName = "Test",
Email = "test@example.com",
PhoneNumber = "09123456789"
};

var createUserResult = await base.TestAppUserManager.CreateUser(user);

var phoneNumberConfirmationCode = await base.TestAppUserManager.GeneratePhoneNumberConfirmationToken(user, user.PhoneNumber);

var confirmPhoneNumberResult = await base.TestAppUserManager.ChangePhoneNumber(user, user.PhoneNumber, phoneNumberConfirmationCode);

var otpCode = await base.TestAppUserManager.GenerateOtpCode(user);

var confirmOtpCode = await base.TestAppUserManager.VerifyUserCode(user, otpCode);

Assert.NotNull(otpCode);
Assert.True(confirmOtpCode.Succeeded);
}

[Fact]
public async Task Generate_Access_Token()
{
var user = new User()
{
UserName = "Test",
Email = "test@example.com",
PhoneNumber = "09123456789"
};

var createUserResult = await base.TestAppUserManager.CreateUser(user);

var otpCode = await base.TestAppUserManager.GeneratePhoneNumberConfirmationToken(user, user.PhoneNumber);

var confirmPhoneNumberResult = await base.TestAppUserManager.ChangePhoneNumber(user, user.PhoneNumber, otpCode);

var token=await base.JwtService.GenerateAsync(user);

Assert.NotNull(token.access_token);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
34 changes: 34 additions & 0 deletions src/Tests/CleanArc.Tests.Setup/CleanArc.Tests.Setup.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\CleanArc.Infrastructure.Identity\CleanArc.Infrastructure.Identity.csproj" />
<ProjectReference Include="..\..\Infrastructure\CleanArc.Infrastructure.Persistence\CleanArc.Infrastructure.Persistence.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Setups\" />
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions src/Tests/CleanArc.Tests.Setup/Setups/TestApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using CleanArc.Infrastructure.Persistence;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace CleanArc.Tests.Setup.Setups;

public abstract class TestApplicationDbContext
{
protected ApplicationDbContext UnitTestDbContext { get; }

protected TestApplicationDbContext()
{
var connectionStringBuilder =
new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connection = new SqliteConnection(connectionStringBuilder.ToString());

var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.Options;

UnitTestDbContext = new ApplicationDbContext(options);
}
}
92 changes: 92 additions & 0 deletions src/Tests/CleanArc.Tests.Setup/Setups/TestIdentitySetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity;
using CleanArc.Infrastructure.Identity.Identity.Dtos;
using CleanArc.Infrastructure.Identity.Identity.Extensions;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using CleanArc.Infrastructure.Identity.Identity.Store;
using CleanArc.Infrastructure.Identity.Jwt;
using CleanArc.Infrastructure.Identity.UserManager;
using CleanArc.Infrastructure.Persistence;
using CleanArc.Infrastructure.Persistence.Repositories.Common;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;

namespace CleanArc.Tests.Setup.Setups;

public abstract class TestIdentitySetup
{
protected IAppUserManager TestAppUserManager { get; }
protected AppRoleManager TestAppRoleManager { get; }
public AppSignInManager TestSignInManager { get; }
public IAppUserManager User { get; }
public IJwtService JwtService { get; }

protected TestIdentitySetup()
{
var serviceCollection = new ServiceCollection();

var connection = new SqliteConnection("DataSource=:memory:");

serviceCollection.AddLogging();

serviceCollection.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connection));

var context = serviceCollection.BuildServiceProvider().GetService<ApplicationDbContext>();
context.Database.OpenConnection();
context.Database.EnsureCreated();


serviceCollection.AddIdentity<User, Role>(options =>
{
options.Stores.ProtectPersonalData = false;
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireUppercase = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = true;
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = false;
options.User.RequireUniqueEmail = false;
}).AddUserStore<AppUserStore>()
.AddRoleStore<RoleStore>().
AddUserManager<AppUserManager>().
AddRoleManager<AppRoleManager>().
AddErrorDescriber<AppErrorDescriber>()
.AddDefaultTokenProviders().
AddSignInManager<AppSignInManager>()
.AddDefaultTokenProviders()
.AddPasswordlessLoginTotpTokenProvider();

serviceCollection.Configure<IdentitySettings>(settings =>
{
settings.Audience = "CleanArc.Unit.Tests";
settings.ExpirationMinutes = 5;
settings.Issuer = "CleanArc.Unit.Tests";
settings.NotBeforeMinutes = 0;
settings.SecretKey = "LongerThan-16Char-SecretKey";
settings.Encryptkey = "16CharEncryptKey";
});

serviceCollection.AddScoped<IUnitOfWork, UnitOfWork>();
serviceCollection.AddScoped<IJwtService, JwtService>();
serviceCollection.AddScoped<IAppUserManager, AppUserManagerImplementation>();

TestAppUserManager = serviceCollection.BuildServiceProvider().GetRequiredService<IAppUserManager>();
TestAppRoleManager = serviceCollection.BuildServiceProvider().GetRequiredService<AppRoleManager>();
TestSignInManager = serviceCollection.BuildServiceProvider().GetRequiredService<AppSignInManager>();
JwtService = serviceCollection.BuildServiceProvider().GetRequiredService<IJwtService>();
}
}
1 change: 1 addition & 0 deletions src/Tests/CleanArc.Tests.Setup/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;

0 comments on commit 5ead995

Please sign in to comment.