Added unit testing with xunit
parent
919ab053bc
commit
3f0412e7c5
|
@ -8,6 +8,7 @@ namespace Tuitio.Domain.Data.DbContexts
|
|||
{
|
||||
public class TuitioDbContext : DbContext
|
||||
{
|
||||
public DbSet<UserStatus> UserStatuses{ get; set; }
|
||||
public DbSet<AppUser> Users { get; set; }
|
||||
public DbSet<UserToken> UserTokens { get; set; }
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ namespace Tuitio.Extensions
|
|||
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>));
|
||||
|
||||
// AutoMapper
|
||||
services.AddAutoMapper(
|
||||
typeof(Application.Mappings.MappingProfile).Assembly);
|
||||
services.AddAutoMapper(typeof(Application.Mappings.MappingProfile).Assembly);
|
||||
|
||||
// Swagger
|
||||
services.AddSwagger("Tuitio API", AuthorizationType.None);
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
using MediatR;
|
||||
using MediatR.Pipeline;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Data.Common;
|
||||
using Tuitio.Domain.Data.DbContexts;
|
||||
using Xunit;
|
||||
|
||||
namespace Tuitio.Application.Tests.Fixtures
|
||||
{
|
||||
public class DependencyInjectionFixture : IAsyncLifetime
|
||||
{
|
||||
private readonly Guid _prefix;
|
||||
private readonly List<SqliteConnection> _connections;
|
||||
public readonly IServiceProvider ServiceProvider;
|
||||
|
||||
public DependencyInjectionFixture()
|
||||
{
|
||||
_prefix = Guid.NewGuid();
|
||||
_connections = new List<SqliteConnection>();
|
||||
ServiceProvider = BuildServiceProvider();
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
foreach (var con in _connections)
|
||||
await con.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await PrepareDbAsync();
|
||||
SeedData();
|
||||
}
|
||||
|
||||
private IServiceProvider BuildServiceProvider()
|
||||
{
|
||||
var configurationBuilder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.tuitio.json", optional: true, reloadOnChange: false);
|
||||
var configuration = configurationBuilder.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
builder.SetMinimumLevel(LogLevel.Warning);
|
||||
builder.AddFilter("Microsoft.*", LogLevel.Warning);
|
||||
});
|
||||
|
||||
// MediatR
|
||||
services.AddMediatR(typeof(Application.CommandHandlers.AccountLoginHandler).Assembly);
|
||||
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
|
||||
|
||||
// AutoMapper
|
||||
services.AddAutoMapper(typeof(Application.Mappings.MappingProfile).Assembly);
|
||||
|
||||
services.AddDbContext<TuitioDbContext>(options =>
|
||||
{
|
||||
options.UseSqlite(GetOrAddInMemoryDatabase("TuitioDbContext"));
|
||||
options.EnableSensitiveDataLogging();
|
||||
options.EnableDetailedErrors();
|
||||
});
|
||||
|
||||
services.AddApplicationServices();
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
return provider;
|
||||
}
|
||||
|
||||
private DbConnection GetOrAddInMemoryDatabase(string code)
|
||||
{
|
||||
var connection = new SqliteConnection($"Data Source={_prefix}{code};Mode=Memory;Cache=shared");
|
||||
_connections.Add(connection);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
private async Task PrepareDbAsync()
|
||||
{
|
||||
var result = await ServiceProvider.GetRequiredService<TuitioDbContext>().Database.EnsureCreatedAsync();
|
||||
if (!result) throw new Exception("TuitioDbContext exists");
|
||||
|
||||
var script = await File.ReadAllTextAsync("seed.sql");
|
||||
await ServiceProvider.GetRequiredService<TuitioDbContext>().Database.ExecuteSqlRawAsync(script);
|
||||
}
|
||||
|
||||
private void SeedData()
|
||||
{
|
||||
using var dbContext = ServiceProvider.GetRequiredService<TuitioDbContext>();
|
||||
|
||||
dbContext.UserStatuses.AddRange(new Domain.Entities.UserStatus[]
|
||||
{
|
||||
new Domain.Entities.UserStatus()
|
||||
{
|
||||
StatusId = 1,
|
||||
StatusCode = "ACTIVE",
|
||||
StatusName = "Active"
|
||||
},
|
||||
new Domain.Entities.UserStatus()
|
||||
{
|
||||
StatusId = 2,
|
||||
StatusCode = "INACTIVE",
|
||||
StatusName = "Inactive"
|
||||
},
|
||||
new Domain.Entities.UserStatus()
|
||||
{
|
||||
StatusId = 3,
|
||||
StatusCode = "BLOCKED",
|
||||
StatusName = "Blocked"
|
||||
}
|
||||
});
|
||||
|
||||
dbContext.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Tuitio.Application.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace Tuitio.Application.Tests
|
||||
{
|
||||
public class HashingServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void HashSha256_ShouldReturnCorrectHash()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "9B8769A4A742959A2D0298C36FB70623F2DFACDA8436237DF08D8DFD5B37374C";
|
||||
var hashingService = new HashingService();
|
||||
|
||||
// Act
|
||||
var actual = hashingService.HashSha256("pass123");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Tuitio.Application.Tests.Fixtures;
|
||||
using Tuitio.Domain.Data.DbContexts;
|
||||
using Xunit;
|
||||
|
||||
namespace Tuitio.Application.Tests
|
||||
{
|
||||
public class LocalSqliteDatabaseTests : IClassFixture<DependencyInjectionFixture>, IDisposable
|
||||
{
|
||||
private readonly IServiceScope _tuitioScope;
|
||||
|
||||
public LocalSqliteDatabaseTests(DependencyInjectionFixture fixture)
|
||||
{
|
||||
_tuitioScope = fixture.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_tuitioScope.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureExpectedDataInLocalDbTest()
|
||||
{
|
||||
// Arrange
|
||||
var _dbContext = _tuitioScope.ServiceProvider.GetRequiredService<TuitioDbContext>();
|
||||
|
||||
// Act
|
||||
var statuses = await _dbContext.UserStatuses.ToArrayAsync();
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(statuses);
|
||||
Assert.True(statuses.All(z => !string.IsNullOrWhiteSpace(z.StatusCode)));
|
||||
Assert.Contains(statuses, z => z.StatusCode == "ACTIVE");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.Application.Tests.Fixtures;
|
||||
using Tuitio.Domain.Entities;
|
||||
using Xunit;
|
||||
|
||||
namespace Tuitio.Application.Tests
|
||||
{
|
||||
public class TokenServiceTests : IClassFixture<DependencyInjectionFixture>, IDisposable
|
||||
{
|
||||
private readonly IServiceScope _tuitioScope;
|
||||
private readonly AppUser _userMock;
|
||||
|
||||
public TokenServiceTests(DependencyInjectionFixture fixture)
|
||||
{
|
||||
_tuitioScope = fixture.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||
_userMock = MockAppUser();
|
||||
}
|
||||
|
||||
private AppUser MockAppUser()
|
||||
{
|
||||
var user = new AppUser()
|
||||
{
|
||||
UserId = 0,
|
||||
UserName = "tuitio.test",
|
||||
Password = "9B8769A4A742959A2D0298C36FB70623F2DFACDA8436237DF08D8DFD5B37374C", //pass123
|
||||
Email = "tuitio.test@test.test",
|
||||
FirstName = "tuitio",
|
||||
LastName = "test",
|
||||
StatusId = 1,
|
||||
FailedLoginAttempts = 0,
|
||||
SecurityStamp = "A93650FF-1FC4-4999-BAB6-3EEB174F6892",
|
||||
CreationDate = DateTime.Now
|
||||
};
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_tuitioScope.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TokenGenerationTest()
|
||||
{
|
||||
// Arrange
|
||||
var _tokenService = _tuitioScope.ServiceProvider.GetRequiredService<ITokenService>();
|
||||
|
||||
// Act
|
||||
var token = _tokenService.GenerateToken(_userMock);
|
||||
var raw = _tokenService.GenerateTokenRaw(token);
|
||||
var extracted = _tokenService.ExtractToken(raw);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(token);
|
||||
Assert.NotNull(raw);
|
||||
Assert.NotNull(extracted);
|
||||
|
||||
Assert.True(_userMock.UserName == extracted.UserName);
|
||||
Assert.True(_userMock.SecurityStamp == extracted.SecurityStamp);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
|
@ -7,7 +7,23 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.tuitio.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.tuitio.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="$(AutoMapperExtensionsPackageVersion)" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="$(MediatRPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.14" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@ -17,6 +33,13 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Tuitio.Application\Tuitio.Application.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Tuitio.Domain.Data\Tuitio.Domain.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="seed.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Restrictions": {
|
||||
"MaxFailedLoginAttempts": 5
|
||||
},
|
||||
"Token": {
|
||||
"ValidityInMinutes": 43800
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
SELECT 'empty'
|
||||
|
Loading…
Reference in New Issue