mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 02:31:08 +00:00
Full Cleanup
This commit is contained in:
parent
492ea93b0d
commit
df5530c726
@ -7,22 +7,22 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -62,7 +62,7 @@ public class ApiController : ControllerBase
|
|||||||
{
|
{
|
||||||
return HttpStatusCode.NotFound;
|
return HttpStatusCode.NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_notifications.GetNotifications().Any(n => n.Code == ErrorCodes.InsufficientPermissions))
|
if (_notifications.GetNotifications().Any(n => n.Code == ErrorCodes.InsufficientPermissions))
|
||||||
{
|
{
|
||||||
return HttpStatusCode.Forbidden;
|
return HttpStatusCode.Forbidden;
|
||||||
|
@ -46,7 +46,7 @@ public class UserController : ApiController
|
|||||||
var user = await _userService.GetUserByUserIdAsync(id, isDeleted);
|
var user = await _userService.GetUserByUserIdAsync(id, isDeleted);
|
||||||
return Response(user);
|
return Response(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpGet("me")]
|
[HttpGet("me")]
|
||||||
[SwaggerOperation("Get the current active user")]
|
[SwaggerOperation("Get the current active user")]
|
||||||
@ -56,7 +56,7 @@ public class UserController : ApiController
|
|||||||
var user = await _userService.GetCurrentUserAsync();
|
var user = await _userService.GetCurrentUserAsync();
|
||||||
return Response(user);
|
return Response(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[SwaggerOperation("Create a new user")]
|
[SwaggerOperation("Create a new user")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
||||||
|
@ -28,37 +28,36 @@ builder.Services.AddSwaggerGen(c =>
|
|||||||
{
|
{
|
||||||
Title = "CleanArchitecture",
|
Title = "CleanArchitecture",
|
||||||
Version = "v1",
|
Version = "v1",
|
||||||
Description = "A clean architecture API",
|
Description = "A clean architecture API"
|
||||||
});
|
});
|
||||||
|
|
||||||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
Description = "JWT Authorization header using the Bearer scheme. " +
|
Description = "JWT Authorization header using the Bearer scheme. " +
|
||||||
"Use the /auth/azureLogin endpoint to generate a token (use the id_token here), " +
|
"Use the /api/v1/user/login endpoint to generate a token",
|
||||||
"or create a personal access token in centralhub.",
|
|
||||||
Name = "Authorization",
|
Name = "Authorization",
|
||||||
In = ParameterLocation.Header,
|
In = ParameterLocation.Header,
|
||||||
Type = SecuritySchemeType.Http,
|
Type = SecuritySchemeType.Http,
|
||||||
Scheme = "bearer"
|
Scheme = "bearer"
|
||||||
});
|
});
|
||||||
|
|
||||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
{
|
{
|
||||||
new OpenApiSecurityScheme
|
Type = ReferenceType.SecurityScheme,
|
||||||
{
|
Id = "Bearer"
|
||||||
Reference = new OpenApiReference
|
},
|
||||||
{
|
Scheme = "oauth2",
|
||||||
Type = ReferenceType.SecurityScheme,
|
Name = "Bearer",
|
||||||
Id = "Bearer"
|
In = ParameterLocation.Header
|
||||||
},
|
},
|
||||||
Scheme = "oauth2",
|
new List<string>()
|
||||||
Name = "Bearer",
|
}
|
||||||
In = ParameterLocation.Header,
|
});
|
||||||
},
|
|
||||||
new List<string>()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddHealthChecks();
|
builder.Services.AddHealthChecks();
|
||||||
@ -72,15 +71,9 @@ builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddAuthentication(
|
builder.Services.AddAuthentication(
|
||||||
options =>
|
options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
|
||||||
{
|
|
||||||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
})
|
|
||||||
.AddJwtBearer(
|
.AddJwtBearer(
|
||||||
jwtOptions =>
|
jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(); });
|
||||||
{
|
|
||||||
jwtOptions.TokenValidationParameters = CreateTokenValidationParameters();
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddInfrastructure();
|
builder.Services.AddInfrastructure();
|
||||||
builder.Services.AddQueryHandlers();
|
builder.Services.AddQueryHandlers();
|
||||||
@ -94,10 +87,7 @@ builder.Services
|
|||||||
.Bind(builder.Configuration.GetSection("Auth"))
|
.Bind(builder.Configuration.GetSection("Auth"))
|
||||||
.ValidateOnStart();
|
.ValidateOnStart();
|
||||||
|
|
||||||
builder.Services.AddMediatR(cfg =>
|
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
|
||||||
{
|
|
||||||
cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly);
|
|
||||||
});
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
@ -116,10 +106,10 @@ app.MapControllers();
|
|||||||
app.MapHealthChecks("/health");
|
app.MapHealthChecks("/health");
|
||||||
app.MapGrpcService<UsersApiImplementation>();
|
app.MapGrpcService<UsersApiImplementation>();
|
||||||
|
|
||||||
using (IServiceScope scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var services = scope.ServiceProvider;
|
var services = scope.ServiceProvider;
|
||||||
ApplicationDbContext appDbContext = services.GetRequiredService<ApplicationDbContext>();
|
var appDbContext = services.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
||||||
appDbContext.EnsureMigrationsApplied();
|
appDbContext.EnsureMigrationsApplied();
|
||||||
}
|
}
|
||||||
@ -137,8 +127,8 @@ TokenValidationParameters CreateTokenValidationParameters()
|
|||||||
ValidIssuer = builder.Configuration["Auth:Issuer"],
|
ValidIssuer = builder.Configuration["Auth:Issuer"],
|
||||||
ValidAudience = builder.Configuration["Auth:Audience"],
|
ValidAudience = builder.Configuration["Auth:Audience"],
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(
|
IssuerSigningKey = new SymmetricSecurityKey(
|
||||||
Encoding.UTF8.GetBytes(
|
Encoding.UTF8.GetBytes(
|
||||||
builder.Configuration["Auth:Secret"]!)),
|
builder.Configuration["Auth:Secret"]!)),
|
||||||
RequireSignedTokens = false
|
RequireSignedTokens = false
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,4 +136,6 @@ TokenValidationParameters CreateTokenValidationParameters()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Needed for integration tests webapplication factory
|
// Needed for integration tests webapplication factory
|
||||||
public partial class Program { }
|
public partial class Program
|
||||||
|
{
|
||||||
|
}
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
|
||||||
<PackageReference Include="MockQueryable.Moq" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.Moq" Version="7.0.0"/>
|
||||||
<PackageReference Include="Moq" Version="4.18.4" />
|
<PackageReference Include="Moq" Version="4.18.4"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,8 +24,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -7,7 +7,7 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries;
|
|||||||
public class QueryHandlerBaseFixture
|
public class QueryHandlerBaseFixture
|
||||||
{
|
{
|
||||||
public Mock<IMediatorHandler> Bus { get; } = new();
|
public Mock<IMediatorHandler> Bus { get; } = new();
|
||||||
|
|
||||||
public QueryHandlerBaseFixture VerifyExistingNotification(string key, string errorCode, string message)
|
public QueryHandlerBaseFixture VerifyExistingNotification(string key, string errorCode, string message)
|
||||||
{
|
{
|
||||||
Bus.Verify(
|
Bus.Verify(
|
||||||
|
@ -11,30 +11,30 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
|||||||
|
|
||||||
public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
||||||
{
|
{
|
||||||
|
public GetAllUsersTestFixture()
|
||||||
|
{
|
||||||
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
|
Handler = new GetAllUsersQueryHandler(UserRepository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
private Mock<IUserRepository> UserRepository { get; }
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
public GetAllUsersQueryHandler Handler { get; }
|
public GetAllUsersQueryHandler Handler { get; }
|
||||||
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
public GetAllUsersTestFixture()
|
|
||||||
{
|
|
||||||
UserRepository = new();
|
|
||||||
|
|
||||||
Handler = new(UserRepository.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetupUserAsync()
|
public void SetupUserAsync()
|
||||||
{
|
{
|
||||||
var user = new Mock<User>(() =>
|
var user = new Mock<User>(() =>
|
||||||
new User(
|
new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User));
|
UserRole.User));
|
||||||
|
|
||||||
var query = new[] { user.Object }.AsQueryable().BuildMock();
|
var query = new[] { user.Object }.AsQueryable().BuildMock();
|
||||||
|
|
||||||
UserRepository
|
UserRepository
|
||||||
.Setup(x => x.GetAllNoTracking())
|
.Setup(x => x.GetAllNoTracking())
|
||||||
.Returns(query);
|
.Returns(query);
|
||||||
@ -43,13 +43,13 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
|||||||
public void SetupDeletedUserAsync()
|
public void SetupDeletedUserAsync()
|
||||||
{
|
{
|
||||||
var user = new Mock<User>(() =>
|
var user = new Mock<User>(() =>
|
||||||
new User(
|
new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User));
|
UserRole.User));
|
||||||
|
|
||||||
user.Object.Delete();
|
user.Object.Delete();
|
||||||
|
|
||||||
|
@ -11,30 +11,30 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
|||||||
|
|
||||||
public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
|
public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
|
||||||
{
|
{
|
||||||
|
public GetUserByIdTestFixture()
|
||||||
|
{
|
||||||
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
|
Handler = new GetUserByIdQueryHandler(UserRepository.Object, Bus.Object);
|
||||||
|
}
|
||||||
|
|
||||||
private Mock<IUserRepository> UserRepository { get; }
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
public GetUserByIdQueryHandler Handler { get; }
|
public GetUserByIdQueryHandler Handler { get; }
|
||||||
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
public GetUserByIdTestFixture()
|
|
||||||
{
|
|
||||||
UserRepository = new();
|
|
||||||
|
|
||||||
Handler = new(UserRepository.Object, Bus.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetupUserAsync()
|
public void SetupUserAsync()
|
||||||
{
|
{
|
||||||
var user = new Mock<User>(() =>
|
var user = new Mock<User>(() =>
|
||||||
new User(
|
new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User));
|
UserRole.User));
|
||||||
|
|
||||||
var query = new[] { user.Object }.AsQueryable().BuildMock();
|
var query = new[] { user.Object }.AsQueryable().BuildMock();
|
||||||
|
|
||||||
UserRepository
|
UserRepository
|
||||||
.Setup(x => x.GetAllNoTracking())
|
.Setup(x => x.GetAllNoTracking())
|
||||||
.Returns(query);
|
.Returns(query);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Application.Queries.Users.GetAll;
|
||||||
using CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
using CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -16,11 +17,11 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
_fixture.SetupUserAsync();
|
_fixture.SetupUserAsync();
|
||||||
|
|
||||||
var result = await _fixture.Handler.Handle(
|
var result = await _fixture.Handler.Handle(
|
||||||
new(),
|
new GetAllUsersQuery(),
|
||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyNoDomainNotification();
|
_fixture.VerifyNoDomainNotification();
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result.Should().ContainSingle();
|
result.Should().ContainSingle();
|
||||||
result.FirstOrDefault()!.Id.Should().Be(_fixture.ExistingUserId);
|
result.FirstOrDefault()!.Id.Should().Be(_fixture.ExistingUserId);
|
||||||
@ -32,7 +33,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
_fixture.SetupDeletedUserAsync();
|
_fixture.SetupDeletedUserAsync();
|
||||||
|
|
||||||
var result = await _fixture.Handler.Handle(
|
var result = await _fixture.Handler.Handle(
|
||||||
new(),
|
new GetAllUsersQuery(),
|
||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyNoDomainNotification();
|
_fixture.VerifyNoDomainNotification();
|
||||||
|
@ -18,15 +18,15 @@ public sealed class GetUserByIdQueryHandlerTests
|
|||||||
_fixture.SetupUserAsync();
|
_fixture.SetupUserAsync();
|
||||||
|
|
||||||
var result = await _fixture.Handler.Handle(
|
var result = await _fixture.Handler.Handle(
|
||||||
new(_fixture.ExistingUserId, false),
|
new GetUserByIdQuery(_fixture.ExistingUserId, false),
|
||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyNoDomainNotification();
|
_fixture.VerifyNoDomainNotification();
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result!.Id.Should().Be(_fixture.ExistingUserId);
|
result!.Id.Should().Be(_fixture.ExistingUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Raise_Notification_For_No_User()
|
public async Task Should_Raise_Notification_For_No_User()
|
||||||
{
|
{
|
||||||
@ -36,7 +36,7 @@ public sealed class GetUserByIdQueryHandlerTests
|
|||||||
var result = await _fixture.Handler.Handle(
|
var result = await _fixture.Handler.Handle(
|
||||||
request,
|
request,
|
||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyExistingNotification(
|
_fixture.VerifyExistingNotification(
|
||||||
nameof(GetUserByIdQuery),
|
nameof(GetUserByIdQuery),
|
||||||
ErrorCodes.ObjectNotFound,
|
ErrorCodes.ObjectNotFound,
|
||||||
@ -51,7 +51,7 @@ public sealed class GetUserByIdQueryHandlerTests
|
|||||||
_fixture.SetupDeletedUserAsync();
|
_fixture.SetupDeletedUserAsync();
|
||||||
|
|
||||||
var result = await _fixture.Handler.Handle(
|
var result = await _fixture.Handler.Handle(
|
||||||
new(_fixture.ExistingUserId, false),
|
new GetUserByIdQuery(_fixture.ExistingUserId, false),
|
||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyExistingNotification(
|
_fixture.VerifyExistingNotification(
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -14,15 +14,15 @@ public static class ServiceCollectionExtension
|
|||||||
public static IServiceCollection AddServices(this IServiceCollection services)
|
public static IServiceCollection AddServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IUserService, UserService>();
|
services.AddScoped<IUserService, UserService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddQueryHandlers(this IServiceCollection services)
|
public static IServiceCollection AddQueryHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IRequestHandler<GetUserByIdQuery, UserViewModel?>, GetUserByIdQueryHandler>();
|
services.AddScoped<IRequestHandler<GetUserByIdQuery, UserViewModel?>, GetUserByIdQueryHandler>();
|
||||||
services.AddScoped<IRequestHandler<GetAllUsersQuery, IEnumerable<UserViewModel>>, GetAllUsersQueryHandler>();
|
services.AddScoped<IRequestHandler<GetAllUsersQuery, IEnumerable<UserViewModel>>, GetAllUsersQueryHandler>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.ViewModels.Users;
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.Interfaces;
|
namespace CleanArchitecture.Application.Interfaces;
|
||||||
|
@ -4,4 +4,4 @@ using MediatR;
|
|||||||
|
|
||||||
namespace CleanArchitecture.Application.Queries.Users.GetAll;
|
namespace CleanArchitecture.Application.Queries.Users.GetAll;
|
||||||
|
|
||||||
public sealed record GetAllUsersQuery : IRequest<IEnumerable<UserViewModel>>;
|
public sealed record GetAllUsersQuery : IRequest<IEnumerable<UserViewModel>>;
|
@ -4,4 +4,4 @@ using MediatR;
|
|||||||
|
|
||||||
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||||
|
|
||||||
public sealed record GetUserByIdQuery(Guid UserId, bool IsDeleted) : IRequest<UserViewModel?>;
|
public sealed record GetUserByIdQuery(Guid UserId, bool IsDeleted) : IRequest<UserViewModel?>;
|
@ -13,8 +13,8 @@ namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
|||||||
public sealed class GetUserByIdQueryHandler :
|
public sealed class GetUserByIdQueryHandler :
|
||||||
IRequestHandler<GetUserByIdQuery, UserViewModel?>
|
IRequestHandler<GetUserByIdQuery, UserViewModel?>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IMediatorHandler _bus;
|
private readonly IMediatorHandler _bus;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public GetUserByIdQueryHandler(IUserRepository userRepository, IMediatorHandler bus)
|
public GetUserByIdQueryHandler(IUserRepository userRepository, IMediatorHandler bus)
|
||||||
{
|
{
|
||||||
@ -26,7 +26,7 @@ public sealed class GetUserByIdQueryHandler :
|
|||||||
{
|
{
|
||||||
var user = _userRepository
|
var user = _userRepository
|
||||||
.GetAllNoTracking()
|
.GetAllNoTracking()
|
||||||
.FirstOrDefault(x =>
|
.FirstOrDefault(x =>
|
||||||
x.Id == request.UserId &&
|
x.Id == request.UserId &&
|
||||||
x.Deleted == request.IsDeleted);
|
x.Deleted == request.IsDeleted);
|
||||||
|
|
||||||
@ -42,4 +42,4 @@ public sealed class GetUserByIdQueryHandler :
|
|||||||
|
|
||||||
return UserViewModel.FromUser(user);
|
return UserViewModel.FromUser(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,7 +29,7 @@ public sealed class UserService : IUserService
|
|||||||
{
|
{
|
||||||
return await _bus.QueryAsync(new GetUserByIdQuery(userId, isDeleted));
|
return await _bus.QueryAsync(new GetUserByIdQuery(userId, isDeleted));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserViewModel?> GetCurrentUserAsync()
|
public async Task<UserViewModel?> GetCurrentUserAsync()
|
||||||
{
|
{
|
||||||
return await _bus.QueryAsync(new GetUserByIdQuery(_user.GetUserId(), false));
|
return await _bus.QueryAsync(new GetUserByIdQuery(_user.GetUserId(), false));
|
||||||
@ -39,7 +39,7 @@ public sealed class UserService : IUserService
|
|||||||
{
|
{
|
||||||
return await _bus.QueryAsync(new GetAllUsersQuery());
|
return await _bus.QueryAsync(new GetAllUsersQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Guid> CreateUserAsync(CreateUserViewModel user)
|
public async Task<Guid> CreateUserAsync(CreateUserViewModel user)
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
@ -53,7 +53,7 @@ public sealed class UserService : IUserService
|
|||||||
|
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateUserAsync(UpdateUserViewModel user)
|
public async Task UpdateUserAsync(UpdateUserViewModel user)
|
||||||
{
|
{
|
||||||
await _bus.SendCommandAsync(new UpdateUserCommand(
|
await _bus.SendCommandAsync(new UpdateUserCommand(
|
||||||
@ -63,7 +63,7 @@ public sealed class UserService : IUserService
|
|||||||
user.GivenName,
|
user.GivenName,
|
||||||
user.Role));
|
user.Role));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteUserAsync(Guid userId)
|
public async Task DeleteUserAsync(Guid userId)
|
||||||
{
|
{
|
||||||
await _bus.SendCommandAsync(new DeleteUserCommand(userId));
|
await _bus.SendCommandAsync(new DeleteUserCommand(userId));
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
namespace CleanArchitecture.Application.ViewModels.Users;
|
namespace CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
public sealed record ChangePasswordViewModel(string Password, string NewPassword);
|
public sealed record ChangePasswordViewModel(string Password, string NewPassword);
|
@ -1,3 +1,3 @@
|
|||||||
namespace CleanArchitecture.Application.ViewModels.Users;
|
namespace CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
public sealed record LoginUserViewModel(string Email, string Password);
|
public sealed record LoginUserViewModel(string Email, string Password);
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
|
||||||
<PackageReference Include="Moq" Version="4.18.4" />
|
<PackageReference Include="Moq" Version="4.18.4"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -60,4 +60,4 @@ public sealed class ChangePasswordCommandHandlerTests
|
|||||||
DomainErrorCodes.UserPasswordIncorrect,
|
DomainErrorCodes.UserPasswordIncorrect,
|
||||||
"The password is incorrect");
|
"The password is incorrect");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,14 +9,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.ChangePassword;
|
|||||||
|
|
||||||
public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public ChangePasswordCommandHandler CommandHandler { get; set; }
|
|
||||||
public Mock<IUserRepository> UserRepository { get; set; }
|
|
||||||
|
|
||||||
public ChangePasswordCommandTestFixture()
|
public ChangePasswordCommandTestFixture()
|
||||||
{
|
{
|
||||||
UserRepository = new Mock<IUserRepository>();
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new ChangePasswordCommandHandler(
|
||||||
Bus.Object,
|
Bus.Object,
|
||||||
UnitOfWork.Object,
|
UnitOfWork.Object,
|
||||||
NotificationHandler.Object,
|
NotificationHandler.Object,
|
||||||
@ -24,6 +21,9 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
User.Object);
|
User.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChangePasswordCommandHandler CommandHandler { get; set; }
|
||||||
|
public Mock<IUserRepository> UserRepository { get; set; }
|
||||||
|
|
||||||
public Entities.User SetupUser()
|
public Entities.User SetupUser()
|
||||||
{
|
{
|
||||||
var user = new Entities.User(
|
var user = new Entities.User(
|
||||||
@ -49,4 +49,4 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
User.Setup(x => x.GetUserId()).Returns(id);
|
User.Setup(x => x.GetUserId()).Returns(id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,15 +12,15 @@ public sealed class ChangePasswordCommandValidationTests :
|
|||||||
public ChangePasswordCommandValidationTests() : base(new ChangePasswordCommandValidation())
|
public ChangePasswordCommandValidationTests() : base(new ChangePasswordCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand();
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Password()
|
public void Should_Be_Invalid_For_Empty_Password()
|
||||||
{
|
{
|
||||||
@ -35,60 +35,63 @@ public sealed class ChangePasswordCommandValidationTests :
|
|||||||
DomainErrorCodes.UserUppercaseLetterPassword,
|
DomainErrorCodes.UserUppercaseLetterPassword,
|
||||||
DomainErrorCodes.UserShortPassword
|
DomainErrorCodes.UserShortPassword
|
||||||
};
|
};
|
||||||
|
|
||||||
ShouldHaveExpectedErrors(command, errors.ToArray());
|
ShouldHaveExpectedErrors(command, errors.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Special_Character()
|
public void Should_Be_Invalid_For_Missing_Special_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand("z8tnayvd5FNLU9AQm");
|
var command = CreateTestCommand("z8tnayvd5FNLU9AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Number()
|
public void Should_Be_Invalid_For_Missing_Number()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand("z]tnayvdFNLU:]AQm");
|
var command = CreateTestCommand("z]tnayvdFNLU:]AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand("Z8]TNAYVDFNLU:]AQM");
|
var command = CreateTestCommand("Z8]TNAYVDFNLU:]AQM");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand("z8]tnayvd5fnlu9:]aqm");
|
var command = CreateTestCommand("z8]tnayvd5fnlu9:]aqm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Short()
|
public void Should_Be_Invalid_For_Password_Too_Short()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand("zA6{");
|
var command = CreateTestCommand("zA6{");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Long()
|
public void Should_Be_Invalid_For_Password_Too_Long()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
var command = CreateTestCommand(string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangePasswordCommand CreateTestCommand(
|
private ChangePasswordCommand CreateTestCommand(
|
||||||
string? password = null, string? newPassword = null) => new(
|
string? password = null, string? newPassword = null)
|
||||||
password ?? "z8]tnayvd5FNLU9:]AQm",
|
{
|
||||||
|
return new(
|
||||||
|
password ?? "z8]tnayvd5FNLU9:]AQm",
|
||||||
newPassword ?? "z8]tnayvd5FNLU9:]AQw");
|
newPassword ?? "z8]tnayvd5FNLU9:]AQw");
|
||||||
}
|
}
|
||||||
|
}
|
@ -9,19 +9,19 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
|
|||||||
public sealed class CreateUserCommandHandlerTests
|
public sealed class CreateUserCommandHandlerTests
|
||||||
{
|
{
|
||||||
private readonly CreateUserCommandTestFixture _fixture = new();
|
private readonly CreateUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Create_User()
|
public void Should_Create_User()
|
||||||
{
|
{
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
var command = new CreateUserCommand(
|
var command = new CreateUserCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"test@email.com",
|
"test@email.com",
|
||||||
"Test",
|
"Test",
|
||||||
"Email",
|
"Email",
|
||||||
"Po=PF]PC6t.?8?ks)A6W");
|
"Po=PF]PC6t.?8?ks)A6W");
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
@ -29,19 +29,19 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
.VerifyCommit()
|
.VerifyCommit()
|
||||||
.VerifyRaisedEvent<UserCreatedEvent>(x => x.UserId == command.UserId);
|
.VerifyRaisedEvent<UserCreatedEvent>(x => x.UserId == command.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_Already_Existing_User()
|
public void Should_Not_Create_Already_Existing_User()
|
||||||
{
|
{
|
||||||
var user = _fixture.SetupUser();
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
var command = new CreateUserCommand(
|
var command = new CreateUserCommand(
|
||||||
user.Id,
|
user.Id,
|
||||||
"test@email.com",
|
"test@email.com",
|
||||||
"Test",
|
"Test",
|
||||||
"Email",
|
"Email",
|
||||||
"Po=PF]PC6t.?8?ks)A6W");
|
"Po=PF]PC6t.?8?ks)A6W");
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
|
@ -8,20 +8,20 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
|
|||||||
|
|
||||||
public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public CreateUserCommandHandler CommandHandler { get; }
|
|
||||||
private Mock<IUserRepository> UserRepository { get; }
|
|
||||||
|
|
||||||
public CreateUserCommandTestFixture()
|
public CreateUserCommandTestFixture()
|
||||||
{
|
{
|
||||||
UserRepository = new Mock<IUserRepository>();
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new CreateUserCommandHandler(
|
||||||
Bus.Object,
|
Bus.Object,
|
||||||
UnitOfWork.Object,
|
UnitOfWork.Object,
|
||||||
NotificationHandler.Object,
|
NotificationHandler.Object,
|
||||||
UserRepository.Object);
|
UserRepository.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CreateUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
public Entities.User SetupUser()
|
public Entities.User SetupUser()
|
||||||
{
|
{
|
||||||
var user = new Entities.User(
|
var user = new Entities.User(
|
||||||
|
@ -13,103 +13,103 @@ public sealed class CreateUserCommandValidationTests :
|
|||||||
public CreateUserCommandValidationTests() : base(new CreateUserCommandValidation())
|
public CreateUserCommandValidationTests() : base(new CreateUserCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand();
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_User_Id()
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(userId: Guid.Empty);
|
var command = CreateTestCommand(Guid.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptyId,
|
DomainErrorCodes.UserEmptyId,
|
||||||
"User id may not be empty");
|
"User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Email()
|
public void Should_Be_Invalid_For_Empty_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: string.Empty);
|
var command = CreateTestCommand(email: string.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Invalid_Email()
|
public void Should_Be_Invalid_For_Invalid_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: "not a email");
|
var command = CreateTestCommand(email: "not a email");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmailExceedsMaxLength,
|
DomainErrorCodes.UserEmailExceedsMaxLength,
|
||||||
"Email may not be longer than 320 characters");
|
"Email may not be longer than 320 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Surname()
|
public void Should_Be_Invalid_For_Empty_Surname()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(surName: "");
|
var command = CreateTestCommand(surName: "");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptySurname,
|
DomainErrorCodes.UserEmptySurname,
|
||||||
"Surname may not be empty");
|
"Surname may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(surName: new string('a', 101));
|
var command = CreateTestCommand(surName: new string('a', 101));
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
||||||
"Surname may not be longer than 100 characters");
|
"Surname may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Given_Name()
|
public void Should_Be_Invalid_For_Empty_Given_Name()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(givenName: "");
|
var command = CreateTestCommand(givenName: "");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptyGivenName,
|
DomainErrorCodes.UserEmptyGivenName,
|
||||||
"Given name may not be empty");
|
"Given name may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(givenName: new string('a', 101));
|
var command = CreateTestCommand(givenName: new string('a', 101));
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
||||||
"Given name may not be longer than 100 characters");
|
"Given name may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Password()
|
public void Should_Be_Invalid_For_Empty_Password()
|
||||||
{
|
{
|
||||||
@ -124,68 +124,70 @@ public sealed class CreateUserCommandValidationTests :
|
|||||||
DomainErrorCodes.UserUppercaseLetterPassword,
|
DomainErrorCodes.UserUppercaseLetterPassword,
|
||||||
DomainErrorCodes.UserShortPassword
|
DomainErrorCodes.UserShortPassword
|
||||||
};
|
};
|
||||||
|
|
||||||
ShouldHaveExpectedErrors(command, errors.ToArray());
|
ShouldHaveExpectedErrors(command, errors.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Special_Character()
|
public void Should_Be_Invalid_For_Missing_Special_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm");
|
var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Number()
|
public void Should_Be_Invalid_For_Missing_Number()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm");
|
var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM");
|
var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm");
|
var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Short()
|
public void Should_Be_Invalid_For_Password_Too_Short()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "zA6{");
|
var command = CreateTestCommand(password: "zA6{");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Long()
|
public void Should_Be_Invalid_For_Password_Too_Long()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CreateUserCommand CreateTestCommand(
|
private CreateUserCommand CreateTestCommand(
|
||||||
Guid? userId = null,
|
Guid? userId = null,
|
||||||
string? email = null,
|
string? email = null,
|
||||||
string? surName = null,
|
string? surName = null,
|
||||||
string? givenName = null,
|
string? givenName = null,
|
||||||
string? password = null) =>
|
string? password = null)
|
||||||
new (
|
{
|
||||||
|
return new(
|
||||||
userId ?? Guid.NewGuid(),
|
userId ?? Guid.NewGuid(),
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
surName ?? "test",
|
surName ?? "test",
|
||||||
givenName ?? "email",
|
givenName ?? "email",
|
||||||
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,14 +9,14 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
|||||||
public sealed class DeleteUserCommandHandlerTests
|
public sealed class DeleteUserCommandHandlerTests
|
||||||
{
|
{
|
||||||
private readonly DeleteUserCommandTestFixture _fixture = new();
|
private readonly DeleteUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Delete_User()
|
public void Should_Delete_User()
|
||||||
{
|
{
|
||||||
var user = _fixture.SetupUser();
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
var command = new DeleteUserCommand(user.Id);
|
var command = new DeleteUserCommand(user.Id);
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
@ -24,14 +24,14 @@ public sealed class DeleteUserCommandHandlerTests
|
|||||||
.VerifyCommit()
|
.VerifyCommit()
|
||||||
.VerifyRaisedEvent<UserDeletedEvent>(x => x.UserId == user.Id);
|
.VerifyRaisedEvent<UserDeletedEvent>(x => x.UserId == user.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Delete_Non_Existing_User()
|
public void Should_Not_Delete_Non_Existing_User()
|
||||||
{
|
{
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
var command = new DeleteUserCommand(Guid.NewGuid());
|
var command = new DeleteUserCommand(Guid.NewGuid());
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
|
@ -8,14 +8,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
|||||||
|
|
||||||
public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public DeleteUserCommandHandler CommandHandler { get; }
|
|
||||||
private Mock<IUserRepository> UserRepository { get; }
|
|
||||||
|
|
||||||
public DeleteUserCommandTestFixture()
|
public DeleteUserCommandTestFixture()
|
||||||
{
|
{
|
||||||
UserRepository = new Mock<IUserRepository>();
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
CommandHandler = new (
|
CommandHandler = new DeleteUserCommandHandler(
|
||||||
Bus.Object,
|
Bus.Object,
|
||||||
UnitOfWork.Object,
|
UnitOfWork.Object,
|
||||||
NotificationHandler.Object,
|
NotificationHandler.Object,
|
||||||
@ -23,6 +20,9 @@ public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
User.Object);
|
User.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeleteUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
public Entities.User SetupUser()
|
public Entities.User SetupUser()
|
||||||
{
|
{
|
||||||
var user = new Entities.User(
|
var user = new Entities.User(
|
||||||
|
@ -16,21 +16,23 @@ public sealed class DeleteUserCommandValidationTests :
|
|||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand();
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_User_Id()
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(userId: Guid.Empty);
|
var command = CreateTestCommand(Guid.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptyId,
|
DomainErrorCodes.UserEmptyId,
|
||||||
"User id may not be empty");
|
"User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private DeleteUserCommand CreateTestCommand(Guid? userId = null) =>
|
private DeleteUserCommand CreateTestCommand(Guid? userId = null)
|
||||||
new (userId ?? Guid.NewGuid());
|
{
|
||||||
|
return new(userId ?? Guid.NewGuid());
|
||||||
|
}
|
||||||
}
|
}
|
@ -79,4 +79,4 @@ public sealed class LoginUserCommandHandlerTests
|
|||||||
|
|
||||||
token.Should().BeEmpty();
|
token.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,16 @@
|
|||||||
using CleanArchitecture.Domain.Commands.Users.LoginUser;
|
using System;
|
||||||
|
using CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Settings;
|
using CleanArchitecture.Domain.Settings;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
using System;
|
|
||||||
using BC = BCrypt.Net.BCrypt;
|
using BC = BCrypt.Net.BCrypt;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.LoginUser;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.LoginUser;
|
||||||
|
|
||||||
public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public LoginUserCommandHandler CommandHandler { get; set; }
|
|
||||||
public Mock<IUserRepository> UserRepository { get; set; }
|
|
||||||
public IOptions<TokenSettings> TokenSettings { get; set; }
|
|
||||||
|
|
||||||
public LoginUserCommandTestFixture()
|
public LoginUserCommandTestFixture()
|
||||||
{
|
{
|
||||||
UserRepository = new Mock<IUserRepository>();
|
UserRepository = new Mock<IUserRepository>();
|
||||||
@ -26,7 +22,7 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
Secret = "asjdlkasjd87439284)@#(*"
|
Secret = "asjdlkasjd87439284)@#(*"
|
||||||
});
|
});
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new LoginUserCommandHandler(
|
||||||
Bus.Object,
|
Bus.Object,
|
||||||
UnitOfWork.Object,
|
UnitOfWork.Object,
|
||||||
NotificationHandler.Object,
|
NotificationHandler.Object,
|
||||||
@ -34,6 +30,10 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
TokenSettings);
|
TokenSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoginUserCommandHandler CommandHandler { get; set; }
|
||||||
|
public Mock<IUserRepository> UserRepository { get; set; }
|
||||||
|
public IOptions<TokenSettings> TokenSettings { get; set; }
|
||||||
|
|
||||||
public Entities.User SetupUser()
|
public Entities.User SetupUser()
|
||||||
{
|
{
|
||||||
var user = new Entities.User(
|
var user = new Entities.User(
|
||||||
@ -52,4 +52,4 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,48 +12,48 @@ public sealed class LoginUserCommandValidationTests :
|
|||||||
public LoginUserCommandValidationTests() : base(new LoginUserCommandValidation())
|
public LoginUserCommandValidationTests() : base(new LoginUserCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand();
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Email()
|
public void Should_Be_Invalid_For_Empty_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: string.Empty);
|
var command = CreateTestCommand(string.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Invalid_Email()
|
public void Should_Be_Invalid_For_Invalid_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: "not a email");
|
var command = CreateTestCommand("not a email");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
var command = CreateTestCommand(new string('a', 320) + "@test.com");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmailExceedsMaxLength,
|
DomainErrorCodes.UserEmailExceedsMaxLength,
|
||||||
"Email may not be longer than 320 characters");
|
"Email may not be longer than 320 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Password()
|
public void Should_Be_Invalid_For_Empty_Password()
|
||||||
{
|
{
|
||||||
@ -68,62 +68,64 @@ public sealed class LoginUserCommandValidationTests :
|
|||||||
DomainErrorCodes.UserUppercaseLetterPassword,
|
DomainErrorCodes.UserUppercaseLetterPassword,
|
||||||
DomainErrorCodes.UserShortPassword
|
DomainErrorCodes.UserShortPassword
|
||||||
};
|
};
|
||||||
|
|
||||||
ShouldHaveExpectedErrors(command, errors.ToArray());
|
ShouldHaveExpectedErrors(command, errors.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Special_Character()
|
public void Should_Be_Invalid_For_Missing_Special_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm");
|
var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Number()
|
public void Should_Be_Invalid_For_Missing_Number()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm");
|
var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
public void Should_Be_Invalid_For_Missing_Lowercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM");
|
var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
public void Should_Be_Invalid_For_Missing_Uppercase_Character()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm");
|
var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Short()
|
public void Should_Be_Invalid_For_Password_Too_Short()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: "zA6{");
|
var command = CreateTestCommand(password: "zA6{");
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Password_Too_Long()
|
public void Should_Be_Invalid_For_Password_Too_Long()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12));
|
||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoginUserCommand CreateTestCommand(
|
private LoginUserCommand CreateTestCommand(
|
||||||
string? email = null,
|
string? email = null,
|
||||||
string? password = null) =>
|
string? password = null)
|
||||||
new (
|
{
|
||||||
|
return new(
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
||||||
}
|
}
|
||||||
|
}
|
@ -11,19 +11,19 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
|
|||||||
public sealed class UpdateUserCommandHandlerTests
|
public sealed class UpdateUserCommandHandlerTests
|
||||||
{
|
{
|
||||||
private readonly UpdateUserCommandTestFixture _fixture = new();
|
private readonly UpdateUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Update_User()
|
public async Task Should_Update_User()
|
||||||
{
|
{
|
||||||
var user = _fixture.SetupUser();
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
var command = new UpdateUserCommand(
|
var command = new UpdateUserCommand(
|
||||||
user.Id,
|
user.Id,
|
||||||
"test@email.com",
|
"test@email.com",
|
||||||
"Test",
|
"Test",
|
||||||
"Email",
|
"Email",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
await _fixture.CommandHandler.Handle(command, default);
|
await _fixture.CommandHandler.Handle(command, default);
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
@ -31,19 +31,19 @@ public sealed class UpdateUserCommandHandlerTests
|
|||||||
.VerifyCommit()
|
.VerifyCommit()
|
||||||
.VerifyRaisedEvent<UserUpdatedEvent>(x => x.UserId == command.UserId);
|
.VerifyRaisedEvent<UserUpdatedEvent>(x => x.UserId == command.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Not_Update_Non_Existing_User()
|
public async Task Should_Not_Update_Non_Existing_User()
|
||||||
{
|
{
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
var command = new UpdateUserCommand(
|
var command = new UpdateUserCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"test@email.com",
|
"test@email.com",
|
||||||
"Test",
|
"Test",
|
||||||
"Email",
|
"Email",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
await _fixture.CommandHandler.Handle(command, default);
|
await _fixture.CommandHandler.Handle(command, default);
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
|
@ -8,21 +8,21 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
|
|||||||
|
|
||||||
public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public UpdateUserCommandHandler CommandHandler { get; }
|
|
||||||
private Mock<IUserRepository> UserRepository { get; }
|
|
||||||
|
|
||||||
public UpdateUserCommandTestFixture()
|
public UpdateUserCommandTestFixture()
|
||||||
{
|
{
|
||||||
UserRepository = new Mock<IUserRepository>();
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new UpdateUserCommandHandler(
|
||||||
Bus.Object,
|
Bus.Object,
|
||||||
UnitOfWork.Object,
|
UnitOfWork.Object,
|
||||||
NotificationHandler.Object,
|
NotificationHandler.Object,
|
||||||
UserRepository.Object,
|
UserRepository.Object,
|
||||||
User.Object);
|
User.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UpdateUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
public Entities.User SetupUser()
|
public Entities.User SetupUser()
|
||||||
{
|
{
|
||||||
var user = new Entities.User(
|
var user = new Entities.User(
|
||||||
|
@ -12,113 +12,115 @@ public sealed class UpdateUserCommandValidationTests :
|
|||||||
public UpdateUserCommandValidationTests() : base(new UpdateUserCommandValidation())
|
public UpdateUserCommandValidationTests() : base(new UpdateUserCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand();
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_User_Id()
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(userId: Guid.Empty);
|
var command = CreateTestCommand(Guid.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptyId,
|
DomainErrorCodes.UserEmptyId,
|
||||||
"User id may not be empty");
|
"User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Email()
|
public void Should_Be_Invalid_For_Empty_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: string.Empty);
|
var command = CreateTestCommand(email: string.Empty);
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Invalid_Email()
|
public void Should_Be_Invalid_For_Invalid_Email()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: "not a email");
|
var command = CreateTestCommand(email: "not a email");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserInvalidEmail,
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
"Email is not a valid email address");
|
"Email is not a valid email address");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmailExceedsMaxLength,
|
DomainErrorCodes.UserEmailExceedsMaxLength,
|
||||||
"Email may not be longer than 320 characters");
|
"Email may not be longer than 320 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Surname()
|
public void Should_Be_Invalid_For_Empty_Surname()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(surName: "");
|
var command = CreateTestCommand(surName: "");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptySurname,
|
DomainErrorCodes.UserEmptySurname,
|
||||||
"Surname may not be empty");
|
"Surname may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(surName: new string('a', 101));
|
var command = CreateTestCommand(surName: new string('a', 101));
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
||||||
"Surname may not be longer than 100 characters");
|
"Surname may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Given_Name()
|
public void Should_Be_Invalid_For_Empty_Given_Name()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(givenName: "");
|
var command = CreateTestCommand(givenName: "");
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserEmptyGivenName,
|
DomainErrorCodes.UserEmptyGivenName,
|
||||||
"Given name may not be empty");
|
"Given name may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
||||||
{
|
{
|
||||||
var command = CreateTestCommand(givenName: new string('a', 101));
|
var command = CreateTestCommand(givenName: new string('a', 101));
|
||||||
|
|
||||||
ShouldHaveSingleError(
|
ShouldHaveSingleError(
|
||||||
command,
|
command,
|
||||||
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
||||||
"Given name may not be longer than 100 characters");
|
"Given name may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UpdateUserCommand CreateTestCommand(
|
private static UpdateUserCommand CreateTestCommand(
|
||||||
Guid? userId = null,
|
Guid? userId = null,
|
||||||
string? email = null,
|
string? email = null,
|
||||||
string? surName = null,
|
string? surName = null,
|
||||||
string? givenName = null,
|
string? givenName = null,
|
||||||
UserRole? role = null) =>
|
UserRole? role = null)
|
||||||
new (
|
{
|
||||||
|
return new(
|
||||||
userId ?? Guid.NewGuid(),
|
userId ?? Guid.NewGuid(),
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
surName ?? "test",
|
surName ?? "test",
|
||||||
givenName ?? "email",
|
givenName ?? "email",
|
||||||
role ?? UserRole.User);
|
role ?? UserRole.User);
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,24 +9,24 @@ namespace CleanArchitecture.Domain.Tests;
|
|||||||
|
|
||||||
public class CommandHandlerFixtureBase
|
public class CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
protected Mock<IMediatorHandler> Bus { get; }
|
|
||||||
protected Mock<IUnitOfWork> UnitOfWork { get; }
|
|
||||||
protected Mock<DomainNotificationHandler> NotificationHandler { get; }
|
|
||||||
protected Mock<IUser> User { get; }
|
|
||||||
|
|
||||||
protected CommandHandlerFixtureBase()
|
protected CommandHandlerFixtureBase()
|
||||||
{
|
{
|
||||||
Bus = new Mock<IMediatorHandler>();
|
Bus = new Mock<IMediatorHandler>();
|
||||||
UnitOfWork = new Mock<IUnitOfWork>();
|
UnitOfWork = new Mock<IUnitOfWork>();
|
||||||
NotificationHandler = new Mock<DomainNotificationHandler>();
|
NotificationHandler = new Mock<DomainNotificationHandler>();
|
||||||
User = new Mock<IUser>();
|
User = new Mock<IUser>();
|
||||||
|
|
||||||
User.Setup(x => x.GetUserId()).Returns(Guid.NewGuid());
|
User.Setup(x => x.GetUserId()).Returns(Guid.NewGuid());
|
||||||
User.Setup(x => x.GetUserRole()).Returns(UserRole.Admin);
|
User.Setup(x => x.GetUserRole()).Returns(UserRole.Admin);
|
||||||
|
|
||||||
UnitOfWork.Setup(unit => unit.CommitAsync()).ReturnsAsync(true);
|
UnitOfWork.Setup(unit => unit.CommitAsync()).ReturnsAsync(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Mock<IMediatorHandler> Bus { get; }
|
||||||
|
protected Mock<IUnitOfWork> UnitOfWork { get; }
|
||||||
|
protected Mock<DomainNotificationHandler> NotificationHandler { get; }
|
||||||
|
protected Mock<IUser> User { get; }
|
||||||
|
|
||||||
public CommandHandlerFixtureBase VerifyExistingNotification(string errorCode, string message)
|
public CommandHandlerFixtureBase VerifyExistingNotification(string errorCode, string message)
|
||||||
{
|
{
|
||||||
Bus.Verify(
|
Bus.Verify(
|
||||||
|
@ -8,7 +8,7 @@ namespace CleanArchitecture.Domain.Tests;
|
|||||||
|
|
||||||
public class ValidationTestBase<TCommand, TValidation>
|
public class ValidationTestBase<TCommand, TValidation>
|
||||||
where TCommand : CommandBase
|
where TCommand : CommandBase
|
||||||
where TValidation: AbstractValidator<TCommand>
|
where TValidation : AbstractValidator<TCommand>
|
||||||
{
|
{
|
||||||
private readonly TValidation _validation;
|
private readonly TValidation _validation;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public class ValidationTestBase<TCommand, TValidation>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void ShouldHaveExpectedErrors(
|
protected void ShouldHaveExpectedErrors(
|
||||||
TCommand command,
|
TCommand command,
|
||||||
params KeyValuePair<string, string>[] expectedErrors)
|
params KeyValuePair<string, string>[] expectedErrors)
|
||||||
{
|
{
|
||||||
var result = _validation.Validate(command);
|
var result = _validation.Validate(command);
|
||||||
@ -70,9 +70,9 @@ public class ValidationTestBase<TCommand, TValidation>
|
|||||||
.Be(1);
|
.Be(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ShouldHaveExpectedErrors(
|
protected void ShouldHaveExpectedErrors(
|
||||||
TCommand command,
|
TCommand command,
|
||||||
params string[] expectedErrors)
|
params string[] expectedErrors)
|
||||||
{
|
{
|
||||||
var result = _validation.Validate(command);
|
var result = _validation.Validate(command);
|
||||||
|
@ -29,19 +29,6 @@ public sealed class ApiUser : IUser
|
|||||||
throw new ArgumentException("Could not parse user id to guid");
|
throw new ArgumentException("Could not parse user id to guid");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUserEmail()
|
|
||||||
{
|
|
||||||
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
|
||||||
.FirstOrDefault(x => string.Equals(x.Type, ClaimTypes.Email));
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(claim?.Value))
|
|
||||||
{
|
|
||||||
return claim?.Value!;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException("Could not parse user email");
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserRole GetUserRole()
|
public UserRole GetUserRole()
|
||||||
{
|
{
|
||||||
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
||||||
@ -56,4 +43,17 @@ public sealed class ApiUser : IUser
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Name => _httpContextAccessor.HttpContext?.User.Identity?.Name ?? string.Empty;
|
public string Name => _httpContextAccessor.HttpContext?.User.Identity?.Name ?? string.Empty;
|
||||||
}
|
|
||||||
|
public string GetUserEmail()
|
||||||
|
{
|
||||||
|
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
||||||
|
.FirstOrDefault(x => string.Equals(x.Type, ClaimTypes.Email));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(claim?.Value))
|
||||||
|
{
|
||||||
|
return claim?.Value!;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException("Could not parse user email");
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
|
||||||
<PackageReference Include="FluentValidation" Version="11.5.1" />
|
<PackageReference Include="FluentValidation" Version="11.5.1"/>
|
||||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
<PackageReference Include="MediatR" Version="12.0.1"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,17 +6,17 @@ namespace CleanArchitecture.Domain.Commands;
|
|||||||
|
|
||||||
public abstract class CommandBase : IRequest
|
public abstract class CommandBase : IRequest
|
||||||
{
|
{
|
||||||
public Guid AggregateId { get; }
|
|
||||||
public string MessageType { get; }
|
|
||||||
public DateTime Timestamp { get; }
|
|
||||||
public ValidationResult? ValidationResult { get; protected set; }
|
|
||||||
|
|
||||||
protected CommandBase(Guid aggregateId)
|
protected CommandBase(Guid aggregateId)
|
||||||
{
|
{
|
||||||
MessageType = GetType().Name;
|
MessageType = GetType().Name;
|
||||||
Timestamp = DateTime.Now;
|
Timestamp = DateTime.Now;
|
||||||
AggregateId = aggregateId;
|
AggregateId = aggregateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid AggregateId { get; }
|
||||||
|
public string MessageType { get; }
|
||||||
|
public DateTime Timestamp { get; }
|
||||||
|
public ValidationResult? ValidationResult { get; protected set; }
|
||||||
|
|
||||||
public abstract bool IsValid();
|
public abstract bool IsValid();
|
||||||
}
|
}
|
@ -10,8 +10,8 @@ namespace CleanArchitecture.Domain.Commands;
|
|||||||
public abstract class CommandHandlerBase
|
public abstract class CommandHandlerBase
|
||||||
{
|
{
|
||||||
protected readonly IMediatorHandler _bus;
|
protected readonly IMediatorHandler _bus;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
|
||||||
private readonly DomainNotificationHandler _notifications;
|
private readonly DomainNotificationHandler _notifications;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
protected CommandHandlerBase(
|
protected CommandHandlerBase(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -22,7 +22,7 @@ public abstract class CommandHandlerBase
|
|||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
_notifications = (DomainNotificationHandler)notifications;
|
_notifications = (DomainNotificationHandler)notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CommitAsync()
|
public async Task<bool> CommitAsync()
|
||||||
{
|
{
|
||||||
if (_notifications.HasNotifications())
|
if (_notifications.HasNotifications())
|
||||||
@ -43,7 +43,7 @@ public abstract class CommandHandlerBase
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task NotifyAsync(string key, string message, string code)
|
protected async Task NotifyAsync(string key, string message, string code)
|
||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(
|
await _bus.RaiseEventAsync(
|
||||||
@ -54,7 +54,7 @@ public abstract class CommandHandlerBase
|
|||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(notification);
|
await _bus.RaiseEventAsync(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async ValueTask<bool> TestValidityAsync(CommandBase command)
|
protected async ValueTask<bool> TestValidityAsync(CommandBase command)
|
||||||
{
|
{
|
||||||
if (command.IsValid())
|
if (command.IsValid())
|
||||||
@ -71,8 +71,8 @@ public abstract class CommandHandlerBase
|
|||||||
{
|
{
|
||||||
await NotifyAsync(
|
await NotifyAsync(
|
||||||
new DomainNotification(
|
new DomainNotification(
|
||||||
command.MessageType,
|
command.MessageType,
|
||||||
error.ErrorMessage,
|
error.ErrorMessage,
|
||||||
error.ErrorCode,
|
error.ErrorCode,
|
||||||
error.FormattedMessagePlaceholderValues));
|
error.FormattedMessagePlaceholderValues));
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,15 @@ public sealed class ChangePasswordCommand : CommandBase
|
|||||||
{
|
{
|
||||||
private readonly ChangePasswordCommandValidation _validation = new();
|
private readonly ChangePasswordCommandValidation _validation = new();
|
||||||
|
|
||||||
public string Password { get; }
|
|
||||||
public string NewPassword { get; }
|
|
||||||
|
|
||||||
public ChangePasswordCommand(string password, string newPassword) : base(Guid.NewGuid())
|
public ChangePasswordCommand(string password, string newPassword) : base(Guid.NewGuid())
|
||||||
{
|
{
|
||||||
Password = password;
|
Password = password;
|
||||||
NewPassword = newPassword;
|
NewPassword = newPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Password { get; }
|
||||||
|
public string NewPassword { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = _validation.Validate(this);
|
ValidationResult = _validation.Validate(this);
|
||||||
|
@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
|||||||
public sealed class ChangePasswordCommandHandler : CommandHandlerBase,
|
public sealed class ChangePasswordCommandHandler : CommandHandlerBase,
|
||||||
IRequestHandler<ChangePasswordCommand>
|
IRequestHandler<ChangePasswordCommand>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IUser _user;
|
private readonly IUser _user;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public ChangePasswordCommandHandler(
|
public ChangePasswordCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -68,4 +68,4 @@ public sealed class ChangePasswordCommandHandler : CommandHandlerBase,
|
|||||||
await _bus.RaiseEventAsync(new PasswordChangedEvent(user.Id));
|
await _bus.RaiseEventAsync(new PasswordChangedEvent(user.Id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,4 +22,4 @@ public sealed class ChangePasswordCommandValidation : AbstractValidator<ChangePa
|
|||||||
RuleFor(cmd => cmd.NewPassword)
|
RuleFor(cmd => cmd.NewPassword)
|
||||||
.Password();
|
.Password();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,13 +4,7 @@ namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
|||||||
|
|
||||||
public sealed class CreateUserCommand : CommandBase
|
public sealed class CreateUserCommand : CommandBase
|
||||||
{
|
{
|
||||||
private readonly CreateUserCommandValidation _validation = new();
|
private readonly CreateUserCommandValidation _validation = new();
|
||||||
|
|
||||||
public Guid UserId { get; }
|
|
||||||
public string Email { get; }
|
|
||||||
public string Surname { get; }
|
|
||||||
public string GivenName { get; }
|
|
||||||
public string Password { get; }
|
|
||||||
|
|
||||||
public CreateUserCommand(
|
public CreateUserCommand(
|
||||||
Guid userId,
|
Guid userId,
|
||||||
@ -26,6 +20,12 @@ public sealed class CreateUserCommand : CommandBase
|
|||||||
Password = password;
|
Password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
public string Email { get; }
|
||||||
|
public string Surname { get; }
|
||||||
|
public string GivenName { get; }
|
||||||
|
public string Password { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = _validation.Validate(this);
|
ValidationResult = _validation.Validate(this);
|
||||||
|
@ -16,7 +16,7 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
|||||||
IRequestHandler<CreateUserCommand>
|
IRequestHandler<CreateUserCommand>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public CreateUserCommandHandler(
|
public CreateUserCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
IUnitOfWork unitOfWork,
|
IUnitOfWork unitOfWork,
|
||||||
@ -44,9 +44,9 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
|||||||
DomainErrorCodes.UserAlreadyExists));
|
DomainErrorCodes.UserAlreadyExists));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser = await _userRepository.GetByEmailAsync(request.Email);
|
existingUser = await _userRepository.GetByEmailAsync(request.Email);
|
||||||
|
|
||||||
if (existingUser != null)
|
if (existingUser != null)
|
||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(
|
await _bus.RaiseEventAsync(
|
||||||
@ -60,15 +60,15 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
|||||||
var passwordHash = BC.HashPassword(request.Password);
|
var passwordHash = BC.HashPassword(request.Password);
|
||||||
|
|
||||||
var user = new User(
|
var user = new User(
|
||||||
request.UserId,
|
request.UserId,
|
||||||
request.Email,
|
request.Email,
|
||||||
request.Surname,
|
request.Surname,
|
||||||
request.GivenName,
|
request.GivenName,
|
||||||
passwordHash,
|
passwordHash,
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
_userRepository.Add(user);
|
_userRepository.Add(user);
|
||||||
|
|
||||||
if (await CommitAsync())
|
if (await CommitAsync())
|
||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(new UserCreatedEvent(user.Id));
|
await _bus.RaiseEventAsync(new UserCreatedEvent(user.Id));
|
||||||
|
@ -22,7 +22,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
||||||
.WithMessage("User id may not be empty");
|
.WithMessage("User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForEmail()
|
private void AddRuleForEmail()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Email)
|
RuleFor(cmd => cmd.Email)
|
||||||
@ -33,7 +33,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
||||||
.WithMessage("Email may not be longer than 320 characters");
|
.WithMessage("Email may not be longer than 320 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForSurname()
|
private void AddRuleForSurname()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Surname)
|
RuleFor(cmd => cmd.Surname)
|
||||||
@ -44,7 +44,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
||||||
.WithMessage("Surname may not be longer than 100 characters");
|
.WithMessage("Surname may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForGivenName()
|
private void AddRuleForGivenName()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.GivenName)
|
RuleFor(cmd => cmd.GivenName)
|
||||||
@ -55,7 +55,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserGivenNameExceedsMaxLength)
|
.WithErrorCode(DomainErrorCodes.UserGivenNameExceedsMaxLength)
|
||||||
.WithMessage("Given name may not be longer than 100 characters");
|
.WithMessage("Given name may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForPassword()
|
private void AddRuleForPassword()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Password)
|
RuleFor(cmd => cmd.Password)
|
||||||
|
@ -4,15 +4,15 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
|||||||
|
|
||||||
public sealed class DeleteUserCommand : CommandBase
|
public sealed class DeleteUserCommand : CommandBase
|
||||||
{
|
{
|
||||||
private readonly DeleteUserCommandValidation _validation = new();
|
private readonly DeleteUserCommandValidation _validation = new();
|
||||||
|
|
||||||
public Guid UserId { get; }
|
|
||||||
|
|
||||||
public DeleteUserCommand(Guid userId) : base(userId)
|
public DeleteUserCommand(Guid userId) : base(userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = _validation.Validate(this);
|
ValidationResult = _validation.Validate(this);
|
||||||
|
@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
|||||||
public sealed class DeleteUserCommandHandler : CommandHandlerBase,
|
public sealed class DeleteUserCommandHandler : CommandHandlerBase,
|
||||||
IRequestHandler<DeleteUserCommand>
|
IRequestHandler<DeleteUserCommand>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IUser _user;
|
private readonly IUser _user;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public DeleteUserCommandHandler(
|
public DeleteUserCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -35,7 +35,7 @@ public sealed class DeleteUserCommandHandler : CommandHandlerBase,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId);
|
var user = await _userRepository.GetByIdAsync(request.UserId);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
await NotifyAsync(
|
await NotifyAsync(
|
||||||
@ -54,7 +54,7 @@ public sealed class DeleteUserCommandHandler : CommandHandlerBase,
|
|||||||
request.MessageType,
|
request.MessageType,
|
||||||
$"No permission to delete user {request.UserId}",
|
$"No permission to delete user {request.UserId}",
|
||||||
ErrorCodes.InsufficientPermissions));
|
ErrorCodes.InsufficientPermissions));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,6 @@ public sealed class LoginUserCommand : CommandBase,
|
|||||||
{
|
{
|
||||||
private readonly LoginUserCommandValidation _validation = new();
|
private readonly LoginUserCommandValidation _validation = new();
|
||||||
|
|
||||||
public string Email { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public LoginUserCommand(
|
public LoginUserCommand(
|
||||||
string email,
|
string email,
|
||||||
@ -20,6 +17,9 @@ public sealed class LoginUserCommand : CommandBase,
|
|||||||
Password = password;
|
Password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = _validation.Validate(this);
|
ValidationResult = _validation.Validate(this);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Security.Claims;
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
@ -10,10 +11,9 @@ using CleanArchitecture.Domain.Interfaces.Repositories;
|
|||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
using CleanArchitecture.Domain.Settings;
|
using CleanArchitecture.Domain.Settings;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using BC = BCrypt.Net.BCrypt;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using BC = BCrypt.Net.BCrypt;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
|
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
|
|||||||
IRequestHandler<LoginUserCommand, string>
|
IRequestHandler<LoginUserCommand, string>
|
||||||
{
|
{
|
||||||
private const double ExpiryDurationMinutes = 30;
|
private const double ExpiryDurationMinutes = 30;
|
||||||
|
private readonly TokenSettings _tokenSettings;
|
||||||
|
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly TokenSettings _tokenSettings;
|
|
||||||
|
|
||||||
public LoginUserCommandHandler(
|
public LoginUserCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -80,10 +80,10 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
|
|||||||
{
|
{
|
||||||
var claims = new[]
|
var claims = new[]
|
||||||
{
|
{
|
||||||
new Claim(ClaimTypes.Email, email),
|
new Claim(ClaimTypes.Email, email),
|
||||||
new Claim(ClaimTypes.Role, role.ToString()),
|
new Claim(ClaimTypes.Role, role.ToString()),
|
||||||
new Claim(ClaimTypes.NameIdentifier, id.ToString())
|
new Claim(ClaimTypes.NameIdentifier, id.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var securityKey = new SymmetricSecurityKey(
|
var securityKey = new SymmetricSecurityKey(
|
||||||
Encoding.UTF8.GetBytes(tokenSettings.Secret));
|
Encoding.UTF8.GetBytes(tokenSettings.Secret));
|
||||||
@ -101,4 +101,4 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
|
|||||||
|
|
||||||
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
|
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,4 +28,4 @@ public sealed class LoginUserCommandValidation : AbstractValidator<LoginUserComm
|
|||||||
RuleFor(cmd => cmd.Password)
|
RuleFor(cmd => cmd.Password)
|
||||||
.Password();
|
.Password();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,12 +6,6 @@ namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
|||||||
public sealed class UpdateUserCommand : CommandBase
|
public sealed class UpdateUserCommand : CommandBase
|
||||||
{
|
{
|
||||||
private readonly UpdateUserCommandValidation _validation = new();
|
private readonly UpdateUserCommandValidation _validation = new();
|
||||||
|
|
||||||
public Guid UserId { get; }
|
|
||||||
public string Email { get; }
|
|
||||||
public string Surname { get; }
|
|
||||||
public string GivenName { get; }
|
|
||||||
public UserRole Role { get; }
|
|
||||||
|
|
||||||
public UpdateUserCommand(
|
public UpdateUserCommand(
|
||||||
Guid userId,
|
Guid userId,
|
||||||
@ -27,6 +21,12 @@ public sealed class UpdateUserCommand : CommandBase
|
|||||||
Role = role;
|
Role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
public string Email { get; }
|
||||||
|
public string Surname { get; }
|
||||||
|
public string GivenName { get; }
|
||||||
|
public UserRole Role { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = _validation.Validate(this);
|
ValidationResult = _validation.Validate(this);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Entities;
|
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Domain.Events.User;
|
||||||
@ -14,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
|||||||
public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
||||||
IRequestHandler<UpdateUserCommand>
|
IRequestHandler<UpdateUserCommand>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IUser _user;
|
private readonly IUser _user;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public UpdateUserCommandHandler(
|
public UpdateUserCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -54,7 +53,7 @@ public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
|||||||
request.MessageType,
|
request.MessageType,
|
||||||
$"No permission to update user {request.UserId}",
|
$"No permission to update user {request.UserId}",
|
||||||
ErrorCodes.InsufficientPermissions));
|
ErrorCodes.InsufficientPermissions));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
|||||||
user.SetGivenName(request.GivenName);
|
user.SetGivenName(request.GivenName);
|
||||||
|
|
||||||
_userRepository.Update(user);
|
_userRepository.Update(user);
|
||||||
|
|
||||||
if (await CommitAsync())
|
if (await CommitAsync())
|
||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(new UserUpdatedEvent(user.Id));
|
await _bus.RaiseEventAsync(new UserUpdatedEvent(user.Id));
|
||||||
|
@ -21,7 +21,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
||||||
.WithMessage("User id may not be empty");
|
.WithMessage("User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForEmail()
|
private void AddRuleForEmail()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Email)
|
RuleFor(cmd => cmd.Email)
|
||||||
@ -32,7 +32,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
||||||
.WithMessage("Email may not be longer than 320 characters");
|
.WithMessage("Email may not be longer than 320 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForSurname()
|
private void AddRuleForSurname()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Surname)
|
RuleFor(cmd => cmd.Surname)
|
||||||
@ -43,7 +43,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
||||||
.WithMessage("Surname may not be longer than 100 characters");
|
.WithMessage("Surname may not be longer than 100 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForGivenName()
|
private void AddRuleForGivenName()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.GivenName)
|
RuleFor(cmd => cmd.GivenName)
|
||||||
|
@ -6,14 +6,6 @@ namespace CleanArchitecture.Domain.Entities;
|
|||||||
|
|
||||||
public class User : Entity
|
public class User : Entity
|
||||||
{
|
{
|
||||||
public string Email { get; private set; }
|
|
||||||
public string GivenName { get; private set; }
|
|
||||||
public string Surname { get; private set; }
|
|
||||||
public string Password { get; private set; }
|
|
||||||
public UserRole Role { get; private set; }
|
|
||||||
|
|
||||||
public string FullName => $"{Surname}, {GivenName}";
|
|
||||||
|
|
||||||
public User(
|
public User(
|
||||||
Guid id,
|
Guid id,
|
||||||
string email,
|
string email,
|
||||||
@ -29,6 +21,14 @@ public class User : Entity
|
|||||||
Role = role;
|
Role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Email { get; private set; }
|
||||||
|
public string GivenName { get; private set; }
|
||||||
|
public string Surname { get; private set; }
|
||||||
|
public string Password { get; private set; }
|
||||||
|
public UserRole Role { get; private set; }
|
||||||
|
|
||||||
|
public string FullName => $"{Surname}, {GivenName}";
|
||||||
|
|
||||||
[MemberNotNull(nameof(Email))]
|
[MemberNotNull(nameof(Email))]
|
||||||
public void SetEmail(string email)
|
public void SetEmail(string email)
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ public class User : Entity
|
|||||||
|
|
||||||
Email = email;
|
Email = email;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemberNotNull(nameof(GivenName))]
|
[MemberNotNull(nameof(GivenName))]
|
||||||
public void SetGivenName(string givenName)
|
public void SetGivenName(string givenName)
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ public class User : Entity
|
|||||||
|
|
||||||
GivenName = givenName;
|
GivenName = givenName;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemberNotNull(nameof(Surname))]
|
[MemberNotNull(nameof(Surname))]
|
||||||
public void SetSurname(string surname)
|
public void SetSurname(string surname)
|
||||||
{
|
{
|
||||||
|
@ -4,4 +4,4 @@ public enum UserRole
|
|||||||
{
|
{
|
||||||
Admin,
|
Admin,
|
||||||
User
|
User
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ public static class DomainErrorCodes
|
|||||||
public const string UserLowercaseLetterPassword = "USER_PASSWORD_MUST_CONTAIN_A_LOWERCASE_LETTER";
|
public const string UserLowercaseLetterPassword = "USER_PASSWORD_MUST_CONTAIN_A_LOWERCASE_LETTER";
|
||||||
public const string UserNumberPassword = "USER_PASSWORD_MUST_CONTAIN_A_NUMBER";
|
public const string UserNumberPassword = "USER_PASSWORD_MUST_CONTAIN_A_NUMBER";
|
||||||
public const string UserSpecialCharPassword = "USER_PASSWORD_MUST_CONTAIN_A_SPECIAL_CHARACTER";
|
public const string UserSpecialCharPassword = "USER_PASSWORD_MUST_CONTAIN_A_SPECIAL_CHARACTER";
|
||||||
|
|
||||||
// User
|
// User
|
||||||
public const string UserAlreadyExists = "USER_ALREADY_EXISTS";
|
public const string UserAlreadyExists = "USER_ALREADY_EXISTS";
|
||||||
public const string UserPasswordIncorrect = "USER_PASSWORD_INCORRECT";
|
public const string UserPasswordIncorrect = "USER_PASSWORD_INCORRECT";
|
||||||
|
@ -11,7 +11,7 @@ public sealed class UserEventHandler :
|
|||||||
INotificationHandler<UserUpdatedEvent>,
|
INotificationHandler<UserUpdatedEvent>,
|
||||||
INotificationHandler<PasswordChangedEvent>
|
INotificationHandler<PasswordChangedEvent>
|
||||||
{
|
{
|
||||||
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@ -20,13 +20,13 @@ public sealed class UserEventHandler :
|
|||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
|
|||||||
|
|
||||||
public sealed class PasswordChangedEvent : DomainEvent
|
public sealed class PasswordChangedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public Guid UserId { get; }
|
|
||||||
|
|
||||||
public PasswordChangedEvent(Guid userId) : base(userId)
|
public PasswordChangedEvent(Guid userId) : base(userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
}
|
@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
|
|||||||
|
|
||||||
public sealed class UserCreatedEvent : DomainEvent
|
public sealed class UserCreatedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public Guid UserId { get; }
|
|
||||||
|
|
||||||
public UserCreatedEvent(Guid userId) : base(userId)
|
public UserCreatedEvent(Guid userId) : base(userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
}
|
}
|
@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
|
|||||||
|
|
||||||
public sealed class UserDeletedEvent : DomainEvent
|
public sealed class UserDeletedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public Guid UserId { get; }
|
|
||||||
|
|
||||||
public UserDeletedEvent(Guid userId) : base(userId)
|
public UserDeletedEvent(Guid userId) : base(userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
}
|
}
|
@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
|
|||||||
|
|
||||||
public sealed class UserUpdatedEvent : DomainEvent
|
public sealed class UserUpdatedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public Guid UserId { get; }
|
|
||||||
|
|
||||||
public UserUpdatedEvent(Guid userId) : base(userId)
|
public UserUpdatedEvent(Guid userId) : base(userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
}
|
}
|
@ -25,7 +25,7 @@ public static class ServiceCollectionExtension
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
// User
|
// User
|
||||||
@ -41,7 +41,7 @@ public static class ServiceCollectionExtension
|
|||||||
{
|
{
|
||||||
// User
|
// User
|
||||||
services.AddScoped<IUser, ApiUser>();
|
services.AddScoped<IUser, ApiUser>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,10 @@ public static class CustomValidator
|
|||||||
return base64.Length % 4 == 0 && Regex.IsMatch(base64, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
|
return base64.Length % 4 == 0 && Regex.IsMatch(base64, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IRuleBuilder<T, string> Password<T>(this IRuleBuilder<T, string> ruleBuilder, int minLength = 8, int maxLength = 50)
|
public static IRuleBuilder<T, string> Password<T>(
|
||||||
|
this IRuleBuilder<T, string> ruleBuilder,
|
||||||
|
int minLength = 8,
|
||||||
|
int maxLength = 50)
|
||||||
{
|
{
|
||||||
var options = ruleBuilder
|
var options = ruleBuilder
|
||||||
.NotEmpty().WithErrorCode(DomainErrorCodes.UserEmptyPassword)
|
.NotEmpty().WithErrorCode(DomainErrorCodes.UserEmptyPassword)
|
||||||
|
@ -5,8 +5,7 @@ namespace CleanArchitecture.Domain.Interfaces;
|
|||||||
|
|
||||||
public interface IUser
|
public interface IUser
|
||||||
{
|
{
|
||||||
|
string Name { get; }
|
||||||
Guid GetUserId();
|
Guid GetUserId();
|
||||||
UserRole GetUserRole();
|
UserRole GetUserRole();
|
||||||
|
}
|
||||||
string Name { get; }
|
|
||||||
}
|
|
@ -4,11 +4,6 @@ namespace CleanArchitecture.Domain.Notifications;
|
|||||||
|
|
||||||
public sealed class DomainNotification : DomainEvent
|
public sealed class DomainNotification : DomainEvent
|
||||||
{
|
{
|
||||||
public string Key { get; private set; }
|
|
||||||
public string Value { get; private set; }
|
|
||||||
public string Code { get; private set; }
|
|
||||||
public object? Data { get; set; }
|
|
||||||
|
|
||||||
public DomainNotification(
|
public DomainNotification(
|
||||||
string key,
|
string key,
|
||||||
string value,
|
string value,
|
||||||
@ -23,4 +18,9 @@ public sealed class DomainNotification : DomainEvent
|
|||||||
|
|
||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Key { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
public string Code { get; }
|
||||||
|
public object? Data { get; set; }
|
||||||
}
|
}
|
@ -15,18 +15,18 @@ public class DomainNotificationHandler : INotificationHandler<DomainNotification
|
|||||||
_notifications = new List<DomainNotification>();
|
_notifications = new List<DomainNotification>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual List<DomainNotification> GetNotifications()
|
|
||||||
{
|
|
||||||
return _notifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Handle(DomainNotification notification, CancellationToken cancellationToken = default)
|
public Task Handle(DomainNotification notification, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_notifications.Add(notification);
|
_notifications.Add(notification);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual List<DomainNotification> GetNotifications()
|
||||||
|
{
|
||||||
|
return _notifications;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool HasNotifications()
|
public virtual bool HasNotifications()
|
||||||
{
|
{
|
||||||
return GetNotifications().Any();
|
return GetNotifications().Any();
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
|
||||||
<PackageReference Include="Moq" Version="4.18.4" />
|
<PackageReference Include="Moq" Version="4.18.4"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -16,9 +16,9 @@ public sealed class DomainNotificationHandlerTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Handle_DomainNotification()
|
public void Should_Handle_DomainNotification()
|
||||||
{
|
{
|
||||||
string key = "Key";
|
var key = "Key";
|
||||||
string value = "Value";
|
var value = "Value";
|
||||||
string code = "Code";
|
var code = "Code";
|
||||||
|
|
||||||
var domainNotification = new DomainNotification(key, value, code);
|
var domainNotification = new DomainNotification(key, value, code);
|
||||||
var domainNotificationHandler = new DomainNotificationHandler();
|
var domainNotificationHandler = new DomainNotificationHandler();
|
||||||
@ -29,9 +29,9 @@ public sealed class DomainNotificationHandlerTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Handle_DomainNotification_Overload()
|
public void Should_Handle_DomainNotification_Overload()
|
||||||
{
|
{
|
||||||
string key = "Key";
|
var key = "Key";
|
||||||
string value = "Value";
|
var value = "Value";
|
||||||
string code = "Code";
|
var code = "Code";
|
||||||
|
|
||||||
var domainNotification = new DomainNotification(key, value, code);
|
var domainNotification = new DomainNotification(key, value, code);
|
||||||
var domainNotificationHandler = new DomainNotificationHandler();
|
var domainNotificationHandler = new DomainNotificationHandler();
|
||||||
@ -42,9 +42,9 @@ public sealed class DomainNotificationHandlerTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void DomainNotification_HasNotifications_After_Handling_One()
|
public void DomainNotification_HasNotifications_After_Handling_One()
|
||||||
{
|
{
|
||||||
string key = "Key";
|
var key = "Key";
|
||||||
string value = "Value";
|
var value = "Value";
|
||||||
string code = "Code";
|
var code = "Code";
|
||||||
|
|
||||||
var domainNotification = new DomainNotification(key, value, code);
|
var domainNotification = new DomainNotification(key, value, code);
|
||||||
var domainNotificationHandler = new DomainNotificationHandler();
|
var domainNotificationHandler = new DomainNotificationHandler();
|
||||||
@ -60,4 +60,4 @@ public sealed class DomainNotificationHandlerTests
|
|||||||
|
|
||||||
domainNotificationHandler.HasNotifications().Should().BeFalse();
|
domainNotificationHandler.HasNotifications().Should().BeFalse();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,9 +10,9 @@ public sealed class DomainNotificationTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Create_DomainNotification_Instance()
|
public void Should_Create_DomainNotification_Instance()
|
||||||
{
|
{
|
||||||
string key = "Key";
|
var key = "Key";
|
||||||
string value = "Value";
|
var value = "Value";
|
||||||
string code = "Code";
|
var code = "Code";
|
||||||
|
|
||||||
var domainNotification = new DomainNotification(
|
var domainNotification = new DomainNotification(
|
||||||
key, value, code);
|
key, value, code);
|
||||||
@ -26,9 +26,9 @@ public sealed class DomainNotificationTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Create_DomainNotification_Overload_Instance()
|
public void Should_Create_DomainNotification_Overload_Instance()
|
||||||
{
|
{
|
||||||
string key = "Key";
|
var key = "Key";
|
||||||
string value = "Value";
|
var value = "Value";
|
||||||
string code = "Code";
|
var code = "Code";
|
||||||
|
|
||||||
var domainNotification = new DomainNotification(
|
var domainNotification = new DomainNotification(
|
||||||
key, value, code);
|
key, value, code);
|
||||||
@ -38,4 +38,4 @@ public sealed class DomainNotificationTests
|
|||||||
domainNotification.Code.Should().Be(code);
|
domainNotification.Code.Should().Be(code);
|
||||||
domainNotification.Should().NotBe(default(Guid));
|
domainNotification.Should().NotBe(default(Guid));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,18 +19,18 @@ public sealed class UnitOfWorkTests
|
|||||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>();
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>();
|
||||||
var dbContextMock = new Mock<ApplicationDbContext>(options.Options);
|
var dbContextMock = new Mock<ApplicationDbContext>(options.Options);
|
||||||
var loggerMock = new Mock<ILogger<UnitOfWork<ApplicationDbContext>>>();
|
var loggerMock = new Mock<ILogger<UnitOfWork<ApplicationDbContext>>>();
|
||||||
|
|
||||||
dbContextMock
|
dbContextMock
|
||||||
.Setup(x => x.SaveChangesAsync(CancellationToken.None))
|
.Setup(x => x.SaveChangesAsync(CancellationToken.None))
|
||||||
.Returns(Task.FromResult(1));
|
.Returns(Task.FromResult(1));
|
||||||
|
|
||||||
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
||||||
|
|
||||||
var result = await unitOfWork.CommitAsync();
|
var result = await unitOfWork.CommitAsync();
|
||||||
|
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Commit_Async_Returns_False()
|
public async Task Should_Commit_Async_Returns_False()
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ public sealed class UnitOfWorkTests
|
|||||||
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
||||||
|
|
||||||
var result = await unitOfWork.CommitAsync();
|
var result = await unitOfWork.CommitAsync();
|
||||||
|
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,18 +6,18 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
<PackageReference Include="MediatR" Version="12.0.1"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.4">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.4">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -24,7 +24,7 @@ public sealed class UserConfiguration : IEntityTypeConfiguration<User>
|
|||||||
.Property(user => user.Surname)
|
.Property(user => user.Surname)
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(100);
|
.HasMaxLength(100);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.Property(user => user.Password)
|
.Property(user => user.Password)
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
@ -6,12 +6,12 @@ namespace CleanArchitecture.Infrastructure.Database;
|
|||||||
|
|
||||||
public class ApplicationDbContext : DbContext
|
public class ApplicationDbContext : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<User> Users { get; set; } = null!;
|
|
||||||
|
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DbSet<User> Users { get; set; } = null!;
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
builder.ApplyConfiguration(new UserConfiguration());
|
builder.ApplyConfiguration(new UserConfiguration());
|
||||||
|
@ -18,4 +18,4 @@ public static class DbContextExtension
|
|||||||
context.Database.Migrate();
|
context.Database.Migrate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,4 +22,4 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ public sealed class InMemoryBus : IMediatorHandler
|
|||||||
{
|
{
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TResponse> QueryAsync<TResponse>(IRequest<TResponse> query)
|
public Task<TResponse> QueryAsync<TResponse>(IRequest<TResponse> query)
|
||||||
{
|
{
|
||||||
return _mediator.Send(query);
|
return _mediator.Send(query);
|
||||||
|
@ -50,19 +50,6 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
|||||||
return await DbSet.FindAsync(id);
|
return await DbSet.FindAsync(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int SaveChanges()
|
|
||||||
{
|
|
||||||
return _dbContext.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
_dbContext.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Update(TEntity entity)
|
public virtual void Update(TEntity entity)
|
||||||
{
|
{
|
||||||
DbSet.Update(entity);
|
DbSet.Update(entity);
|
||||||
@ -72,7 +59,7 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
|||||||
{
|
{
|
||||||
return DbSet.AnyAsync(entity => entity.Id == id);
|
return DbSet.AnyAsync(entity => entity.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(TEntity entity, bool hardDelete = false)
|
public void Remove(TEntity entity, bool hardDelete = false)
|
||||||
{
|
{
|
||||||
if (hardDelete)
|
if (hardDelete)
|
||||||
@ -86,4 +73,16 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int SaveChanges()
|
||||||
|
{
|
||||||
|
return _dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_dbContext.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -45,4 +45,4 @@ public sealed class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContex
|
|||||||
_context.Dispose();
|
_context.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,14 +8,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.4" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
<PackageReference Include="Xunit.Priority" Version="1.1.6"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -27,8 +27,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -24,12 +24,13 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(0)]
|
[Fact]
|
||||||
|
[Priority(0)]
|
||||||
public async Task Should_Create_User()
|
public async Task Should_Create_User()
|
||||||
{
|
{
|
||||||
var user = new CreateUserViewModel(
|
var user = new CreateUserViewModel(
|
||||||
_fixture.CreatedUserEmail,
|
_fixture.CreatedUserEmail,
|
||||||
"Test",
|
"Test",
|
||||||
"Email",
|
"Email",
|
||||||
_fixture.CreatedUserPassword);
|
_fixture.CreatedUserPassword);
|
||||||
|
|
||||||
@ -43,12 +44,13 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
|
|
||||||
_fixture.CreatedUserId = message!.Data;
|
_fixture.CreatedUserId = message!.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(5)]
|
[Fact]
|
||||||
|
[Priority(5)]
|
||||||
public async Task Should_Login_User()
|
public async Task Should_Login_User()
|
||||||
{
|
{
|
||||||
var user = new LoginUserViewModel(
|
var user = new LoginUserViewModel(
|
||||||
_fixture.CreatedUserEmail,
|
_fixture.CreatedUserEmail,
|
||||||
_fixture.CreatedUserPassword);
|
_fixture.CreatedUserPassword);
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", user);
|
var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", user);
|
||||||
@ -63,7 +65,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
_fixture.EnableAuthentication();
|
_fixture.EnableAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(10)]
|
[Fact]
|
||||||
|
[Priority(10)]
|
||||||
public async Task Should_Get_Created_Users()
|
public async Task Should_Get_Created_Users()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
||||||
@ -81,8 +84,9 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.Surname.Should().Be("Test");
|
content.Surname.Should().Be("Test");
|
||||||
content.GivenName.Should().Be("Email");
|
content.GivenName.Should().Be("Email");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(10)]
|
[Fact]
|
||||||
|
[Priority(10)]
|
||||||
public async Task Should_Get_The_Current_Active_Users()
|
public async Task Should_Get_The_Current_Active_Users()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/me");
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/me");
|
||||||
@ -101,7 +105,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.GivenName.Should().Be("Email");
|
content.GivenName.Should().Be("Email");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(15)]
|
[Fact]
|
||||||
|
[Priority(15)]
|
||||||
public async Task Should_Update_User()
|
public async Task Should_Update_User()
|
||||||
{
|
{
|
||||||
var user = new UpdateUserViewModel(
|
var user = new UpdateUserViewModel(
|
||||||
@ -124,7 +129,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.Should().BeEquivalentTo(user);
|
content.Should().BeEquivalentTo(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(20)]
|
[Fact]
|
||||||
|
[Priority(20)]
|
||||||
public async Task Should_Get_Updated_Users()
|
public async Task Should_Get_Updated_Users()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
||||||
@ -141,11 +147,12 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.Email.Should().Be("newtest@email.com");
|
content.Email.Should().Be("newtest@email.com");
|
||||||
content.Surname.Should().Be("NewTest");
|
content.Surname.Should().Be("NewTest");
|
||||||
content.GivenName.Should().Be("NewEmail");
|
content.GivenName.Should().Be("NewEmail");
|
||||||
|
|
||||||
_fixture.CreatedUserEmail = content.Email;
|
_fixture.CreatedUserEmail = content.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(25)]
|
[Fact]
|
||||||
|
[Priority(25)]
|
||||||
public async Task Should_Change_User_Password()
|
public async Task Should_Change_User_Password()
|
||||||
{
|
{
|
||||||
var user = new ChangePasswordViewModel(
|
var user = new ChangePasswordViewModel(
|
||||||
@ -163,10 +170,10 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
var content = message!.Data;
|
var content = message!.Data;
|
||||||
|
|
||||||
content.Should().BeEquivalentTo(user);
|
content.Should().BeEquivalentTo(user);
|
||||||
|
|
||||||
// Verify the user can login with the new password
|
// Verify the user can login with the new password
|
||||||
var login = new LoginUserViewModel(
|
var login = new LoginUserViewModel(
|
||||||
_fixture.CreatedUserEmail,
|
_fixture.CreatedUserEmail,
|
||||||
_fixture.CreatedUserPassword + "1");
|
_fixture.CreatedUserPassword + "1");
|
||||||
|
|
||||||
var loginResponse = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", login);
|
var loginResponse = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", login);
|
||||||
@ -178,7 +185,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
loginMessage?.Data.Should().NotBeEmpty();
|
loginMessage?.Data.Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(30)]
|
[Fact]
|
||||||
|
[Priority(30)]
|
||||||
public async Task Should_Get_All_User()
|
public async Task Should_Get_All_User()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user");
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user");
|
||||||
@ -192,23 +200,24 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
var content = message!.Data!.ToList();
|
var content = message!.Data!.ToList();
|
||||||
|
|
||||||
content.Count.Should().Be(2);
|
content.Count.Should().Be(2);
|
||||||
|
|
||||||
var currentUser = content.First(x => x.Id == _fixture.CreatedUserId);
|
var currentUser = content.First(x => x.Id == _fixture.CreatedUserId);
|
||||||
|
|
||||||
currentUser.Id.Should().Be(_fixture.CreatedUserId);
|
currentUser.Id.Should().Be(_fixture.CreatedUserId);
|
||||||
currentUser.Role.Should().Be(UserRole.User);
|
currentUser.Role.Should().Be(UserRole.User);
|
||||||
currentUser.Email.Should().Be("newtest@email.com");
|
currentUser.Email.Should().Be("newtest@email.com");
|
||||||
currentUser.Surname.Should().Be("NewTest");
|
currentUser.Surname.Should().Be("NewTest");
|
||||||
currentUser.GivenName.Should().Be("NewEmail");
|
currentUser.GivenName.Should().Be("NewEmail");
|
||||||
|
|
||||||
var adminUser = content.First(x => x.Role == UserRole.Admin);
|
var adminUser = content.First(x => x.Role == UserRole.Admin);
|
||||||
|
|
||||||
adminUser.Email.Should().Be("admin@email.com");
|
adminUser.Email.Should().Be("admin@email.com");
|
||||||
adminUser.Surname.Should().Be("Admin");
|
adminUser.Surname.Should().Be("Admin");
|
||||||
adminUser.GivenName.Should().Be("User");
|
adminUser.GivenName.Should().Be("User");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, Priority(35)]
|
[Fact]
|
||||||
|
[Priority(35)]
|
||||||
public async Task Should_Delete_User()
|
public async Task Should_Delete_User()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + _fixture.CreatedUserId);
|
||||||
@ -222,4 +231,4 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
var content = message!.Data;
|
var content = message!.Data;
|
||||||
content.Should().Be(_fixture.CreatedUserId);
|
content.Should().Be(_fixture.CreatedUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,28 +2,29 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Extensions;
|
namespace CleanArchitecture.IntegrationTests.Extensions;
|
||||||
|
|
||||||
public static class FunctionalTestsServiceCollectionExtensions
|
public static class FunctionalTestsServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection SetupTestDatabase<TContext>(this IServiceCollection services, DbConnection connection) where TContext : DbContext
|
public static IServiceCollection SetupTestDatabase<TContext>(this IServiceCollection services,
|
||||||
|
DbConnection connection) where TContext : DbContext
|
||||||
{
|
{
|
||||||
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<TContext>));
|
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<TContext>));
|
||||||
if (descriptor != null)
|
if (descriptor != null)
|
||||||
services.Remove(descriptor);
|
services.Remove(descriptor);
|
||||||
|
|
||||||
services.AddScoped(p =>
|
services.AddScoped(p =>
|
||||||
DbContextOptionsFactory<TContext>(
|
DbContextOptionsFactory<TContext>(
|
||||||
p,
|
p,
|
||||||
(_, options) => options
|
(_, options) => options
|
||||||
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
|
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
|
||||||
.UseLazyLoadingProxies()
|
.UseLazyLoadingProxies()
|
||||||
.UseSqlite(connection)));
|
.UseSqlite(connection)));
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -42,4 +43,4 @@ public static class FunctionalTestsServiceCollectionExtensions
|
|||||||
|
|
||||||
return builder.Options;
|
return builder.Options;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ public static class HttpExtensions
|
|||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true,
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
private static T? Deserialize<T>(string json)
|
private static T? Deserialize<T>(string json)
|
||||||
@ -55,4 +55,4 @@ public static class HttpExtensions
|
|||||||
|
|
||||||
return httpClient.PutAsync(url, content);
|
return httpClient.PutAsync(url, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,8 +9,6 @@ namespace CleanArchitecture.IntegrationTests.Fixtures;
|
|||||||
|
|
||||||
public class TestFixtureBase
|
public class TestFixtureBase
|
||||||
{
|
{
|
||||||
public HttpClient ServerClient { get; }
|
|
||||||
|
|
||||||
public TestFixtureBase()
|
public TestFixtureBase()
|
||||||
{
|
{
|
||||||
var projectDir = Directory.GetCurrentDirectory();
|
var projectDir = Directory.GetCurrentDirectory();
|
||||||
@ -25,6 +23,8 @@ public class TestFixtureBase
|
|||||||
ServerClient.Timeout = TimeSpan.FromMinutes(5);
|
ServerClient.Timeout = TimeSpan.FromMinutes(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpClient ServerClient { get; }
|
||||||
|
|
||||||
protected virtual void SeedTestData(ApplicationDbContext context)
|
protected virtual void SeedTestData(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -35,4 +35,4 @@ public class TestFixtureBase
|
|||||||
IServiceProvider scopedServices)
|
IServiceProvider scopedServices)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,9 +8,9 @@ public sealed class UserTestFixture : TestFixtureBase
|
|||||||
public string CreatedUserEmail { get; set; } = "test@email.com";
|
public string CreatedUserEmail { get; set; } = "test@email.com";
|
||||||
public string CreatedUserPassword { get; set; } = "z8]tnayvd5FNLU9:]AQm";
|
public string CreatedUserPassword { get; set; } = "z8]tnayvd5FNLU9:]AQm";
|
||||||
public string CreatedUserToken { get; set; } = string.Empty;
|
public string CreatedUserToken { get; set; } = string.Empty;
|
||||||
|
|
||||||
public void EnableAuthentication()
|
public void EnableAuthentication()
|
||||||
{
|
{
|
||||||
ServerClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {CreatedUserToken}");
|
ServerClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {CreatedUserToken}");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,11 +18,11 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
ServiceProvider serviceProvider,
|
ServiceProvider serviceProvider,
|
||||||
IServiceProvider scopedServices);
|
IServiceProvider scopedServices);
|
||||||
|
|
||||||
private readonly SqliteConnection _connection = new($"DataSource=:memory:");
|
|
||||||
|
|
||||||
private readonly AddCustomSeedDataHandler? _addCustomSeedDataHandler;
|
private readonly AddCustomSeedDataHandler? _addCustomSeedDataHandler;
|
||||||
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
|
|
||||||
|
private readonly SqliteConnection _connection = new("DataSource=:memory:");
|
||||||
private readonly string? _environment;
|
private readonly string? _environment;
|
||||||
|
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
|
||||||
|
|
||||||
public CleanArchitectureWebApplicationFactory(
|
public CleanArchitectureWebApplicationFactory(
|
||||||
AddCustomSeedDataHandler? addCustomSeedDataHandler,
|
AddCustomSeedDataHandler? addCustomSeedDataHandler,
|
||||||
@ -51,7 +51,7 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
|
|
||||||
var sp = services.BuildServiceProvider();
|
var sp = services.BuildServiceProvider();
|
||||||
|
|
||||||
using IServiceScope scope = sp.CreateScope();
|
using var scope = sp.CreateScope();
|
||||||
var scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
|
|
||||||
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
||||||
@ -62,4 +62,4 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
_registerCustomServicesHandler?.Invoke(services, sp, scopedServices);
|
_registerCustomServicesHandler?.Invoke(services, sp, scopedServices);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,19 +6,19 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Users\Models.proto" />
|
<None Remove="Users\Models.proto"/>
|
||||||
<None Remove="Users\UsersApi.proto" />
|
<None Remove="Users\UsersApi.proto"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Protobuf Include="Users\Models.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\Models.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
|
<PackageReference Include="Google.Protobuf" Version="3.22.1"/>
|
||||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1" />
|
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1"/>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.52.0" />
|
<PackageReference Include="Grpc.AspNetCore" Version="2.52.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
|
||||||
<PackageReference Include="MockQueryable.Moq" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.Moq" Version="7.0.0"/>
|
||||||
<PackageReference Include="Moq" Version="4.18.4" />
|
<PackageReference Include="Moq" Version="4.18.4"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,8 +24,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -11,37 +11,31 @@ namespace CleanArchitecture.gRPC.Tests.Fixtures;
|
|||||||
|
|
||||||
public sealed class UserTestsFixture
|
public sealed class UserTestsFixture
|
||||||
{
|
{
|
||||||
private Mock<IUserRepository> UserRepository { get; } = new ();
|
|
||||||
|
|
||||||
public UsersApiImplementation UsersApiImplementation { get; }
|
|
||||||
|
|
||||||
public IEnumerable<User> ExistingUsers { get; }
|
|
||||||
|
|
||||||
public UserTestsFixture()
|
public UserTestsFixture()
|
||||||
{
|
{
|
||||||
ExistingUsers = new List<User>()
|
ExistingUsers = new List<User>
|
||||||
{
|
{
|
||||||
new (
|
new(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"test@test.de",
|
"test@test.de",
|
||||||
"Test First Name",
|
"Test First Name",
|
||||||
"Test Last Name",
|
"Test Last Name",
|
||||||
"Test Password",
|
"Test Password",
|
||||||
UserRole.User),
|
UserRole.User),
|
||||||
new (
|
new(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"email@Email.de",
|
"email@Email.de",
|
||||||
"Email First Name",
|
"Email First Name",
|
||||||
"Email Last Name",
|
"Email Last Name",
|
||||||
"Email Password",
|
"Email Password",
|
||||||
UserRole.Admin),
|
UserRole.Admin),
|
||||||
new (
|
new(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"user@user.de",
|
"user@user.de",
|
||||||
"User First Name",
|
"User First Name",
|
||||||
"User Last Name",
|
"User Last Name",
|
||||||
"User Password",
|
"User Password",
|
||||||
UserRole.User),
|
UserRole.User)
|
||||||
};
|
};
|
||||||
|
|
||||||
var queryable = ExistingUsers.AsQueryable().BuildMock();
|
var queryable = ExistingUsers.AsQueryable().BuildMock();
|
||||||
@ -52,4 +46,10 @@ public sealed class UserTestsFixture
|
|||||||
|
|
||||||
UsersApiImplementation = new UsersApiImplementation(UserRepository.Object);
|
UsersApiImplementation = new UsersApiImplementation(UserRepository.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Mock<IUserRepository> UserRepository { get; } = new();
|
||||||
|
|
||||||
|
public UsersApiImplementation UsersApiImplementation { get; }
|
||||||
|
|
||||||
|
public IEnumerable<User> ExistingUsers { get; }
|
||||||
}
|
}
|
@ -6,12 +6,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user