0
0
mirror of https://github.com/alex289/CleanArchitecture.git synced 2025-06-30 02:31:08 +00:00

Full Cleanup

This commit is contained in:
alex289 2023-03-22 19:06:01 +01:00
parent 492ea93b0d
commit df5530c726
No known key found for this signature in database
GPG Key ID: 573F77CD2D87F863
91 changed files with 593 additions and 579 deletions

View File

@ -7,22 +7,22 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" 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.EntityFrameworkCore.Design" Version="7.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@ -28,37 +28,36 @@ builder.Services.AddSwaggerGen(c =>
{
Title = "CleanArchitecture",
Version = "v1",
Description = "A clean architecture API",
Description = "A clean architecture API"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. " +
"Use the /auth/azureLogin endpoint to generate a token (use the id_token here), " +
"or create a personal access token in centralhub.",
"Use the /api/v1/user/login endpoint to generate a token",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
new List<string>()
}
});
});
builder.Services.AddHealthChecks();
@ -72,15 +71,9 @@ builder.Services.AddDbContext<ApplicationDbContext>(options =>
});
builder.Services.AddAuthentication(
options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
.AddJwtBearer(
jwtOptions =>
{
jwtOptions.TokenValidationParameters = CreateTokenValidationParameters();
});
jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(); });
builder.Services.AddInfrastructure();
builder.Services.AddQueryHandlers();
@ -94,10 +87,7 @@ builder.Services
.Bind(builder.Configuration.GetSection("Auth"))
.ValidateOnStart();
builder.Services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly);
});
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
var app = builder.Build();
@ -116,10 +106,10 @@ app.MapControllers();
app.MapHealthChecks("/health");
app.MapGrpcService<UsersApiImplementation>();
using (IServiceScope scope = app.Services.CreateScope())
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
ApplicationDbContext appDbContext = services.GetRequiredService<ApplicationDbContext>();
var appDbContext = services.GetRequiredService<ApplicationDbContext>();
appDbContext.EnsureMigrationsApplied();
}
@ -137,8 +127,8 @@ TokenValidationParameters CreateTokenValidationParameters()
ValidIssuer = builder.Configuration["Auth:Issuer"],
ValidAudience = builder.Configuration["Auth:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
builder.Configuration["Auth:Secret"]!)),
Encoding.UTF8.GetBytes(
builder.Configuration["Auth:Secret"]!)),
RequireSignedTokens = false
};
@ -146,4 +136,6 @@ TokenValidationParameters CreateTokenValidationParameters()
}
// Needed for integration tests webapplication factory
public partial class Program { }
public partial class Program
{
}

View File

@ -8,11 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MockQueryable.Moq" Version="7.0.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="MockQueryable.Moq" Version="7.0.0"/>
<PackageReference Include="Moq" Version="4.18.4"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
@ -24,8 +24,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
</ItemGroup>
</Project>

View File

@ -11,27 +11,27 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
{
public GetAllUsersTestFixture()
{
UserRepository = new Mock<IUserRepository>();
Handler = new GetAllUsersQueryHandler(UserRepository.Object);
}
private Mock<IUserRepository> UserRepository { get; }
public GetAllUsersQueryHandler Handler { get; }
public Guid ExistingUserId { get; } = Guid.NewGuid();
public GetAllUsersTestFixture()
{
UserRepository = new();
Handler = new(UserRepository.Object);
}
public void SetupUserAsync()
{
var user = new Mock<User>(() =>
new User(
ExistingUserId,
"max@mustermann.com",
"Max",
"Mustermann",
"Password",
UserRole.User));
new User(
ExistingUserId,
"max@mustermann.com",
"Max",
"Mustermann",
"Password",
UserRole.User));
var query = new[] { user.Object }.AsQueryable().BuildMock();
@ -43,13 +43,13 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
public void SetupDeletedUserAsync()
{
var user = new Mock<User>(() =>
new User(
ExistingUserId,
"max@mustermann.com",
"Max",
"Mustermann",
"Password",
UserRole.User));
new User(
ExistingUserId,
"max@mustermann.com",
"Max",
"Mustermann",
"Password",
UserRole.User));
user.Object.Delete();

View File

@ -11,17 +11,17 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
{
public GetUserByIdTestFixture()
{
UserRepository = new Mock<IUserRepository>();
Handler = new GetUserByIdQueryHandler(UserRepository.Object, Bus.Object);
}
private Mock<IUserRepository> UserRepository { get; }
public GetUserByIdQueryHandler Handler { get; }
public Guid ExistingUserId { get; } = Guid.NewGuid();
public GetUserByIdTestFixture()
{
UserRepository = new();
Handler = new(UserRepository.Object, Bus.Object);
}
public void SetupUserAsync()
{
var user = new Mock<User>(() =>

View File

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using CleanArchitecture.Application.Queries.Users.GetAll;
using CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
using FluentAssertions;
using Xunit;
@ -16,7 +17,7 @@ public sealed class GetAllUsersQueryHandlerTests
_fixture.SetupUserAsync();
var result = await _fixture.Handler.Handle(
new(),
new GetAllUsersQuery(),
default);
_fixture.VerifyNoDomainNotification();
@ -32,7 +33,7 @@ public sealed class GetAllUsersQueryHandlerTests
_fixture.SetupDeletedUserAsync();
var result = await _fixture.Handler.Handle(
new(),
new GetAllUsersQuery(),
default);
_fixture.VerifyNoDomainNotification();

View File

@ -18,7 +18,7 @@ public sealed class GetUserByIdQueryHandlerTests
_fixture.SetupUserAsync();
var result = await _fixture.Handler.Handle(
new(_fixture.ExistingUserId, false),
new GetUserByIdQuery(_fixture.ExistingUserId, false),
default);
_fixture.VerifyNoDomainNotification();
@ -51,7 +51,7 @@ public sealed class GetUserByIdQueryHandlerTests
_fixture.SetupDeletedUserAsync();
var result = await _fixture.Handler.Handle(
new(_fixture.ExistingUserId, false),
new GetUserByIdQuery(_fixture.ExistingUserId, false),
default);
_fixture.VerifyExistingNotification(

View File

@ -6,11 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
</ItemGroup>
</Project>

View File

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CleanArchitecture.Application.ViewModels.Users;
namespace CleanArchitecture.Application.Interfaces;

View File

@ -13,8 +13,8 @@ namespace CleanArchitecture.Application.Queries.Users.GetUserById;
public sealed class GetUserByIdQueryHandler :
IRequestHandler<GetUserByIdQuery, UserViewModel?>
{
private readonly IUserRepository _userRepository;
private readonly IMediatorHandler _bus;
private readonly IUserRepository _userRepository;
public GetUserByIdQueryHandler(IUserRepository userRepository, IMediatorHandler bus)
{

View File

@ -8,11 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="Moq" Version="4.18.4"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
</ItemGroup>
</Project>

View File

@ -9,14 +9,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.ChangePassword;
public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
{
public ChangePasswordCommandHandler CommandHandler { get; set; }
public Mock<IUserRepository> UserRepository { get; set; }
public ChangePasswordCommandTestFixture()
{
UserRepository = new Mock<IUserRepository>();
CommandHandler = new(
CommandHandler = new ChangePasswordCommandHandler(
Bus.Object,
UnitOfWork.Object,
NotificationHandler.Object,
@ -24,6 +21,9 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
User.Object);
}
public ChangePasswordCommandHandler CommandHandler { get; set; }
public Mock<IUserRepository> UserRepository { get; set; }
public Entities.User SetupUser()
{
var user = new Entities.User(

View File

@ -88,7 +88,10 @@ public sealed class ChangePasswordCommandValidationTests :
}
private ChangePasswordCommand CreateTestCommand(
string? password = null, string? newPassword = null) => new(
password ?? "z8]tnayvd5FNLU9:]AQm",
string? password = null, string? newPassword = null)
{
return new(
password ?? "z8]tnayvd5FNLU9:]AQm",
newPassword ?? "z8]tnayvd5FNLU9:]AQw");
}
}

View File

@ -8,20 +8,20 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase
{
public CreateUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public CreateUserCommandTestFixture()
{
UserRepository = new Mock<IUserRepository>();
CommandHandler = new(
CommandHandler = new CreateUserCommandHandler(
Bus.Object,
UnitOfWork.Object,
NotificationHandler.Object,
UserRepository.Object);
}
public CreateUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public Entities.User SetupUser()
{
var user = new Entities.User(

View File

@ -25,7 +25,7 @@ public sealed class CreateUserCommandValidationTests :
[Fact]
public void Should_Be_Invalid_For_Empty_User_Id()
{
var command = CreateTestCommand(userId: Guid.Empty);
var command = CreateTestCommand(Guid.Empty);
ShouldHaveSingleError(
command,
@ -181,11 +181,13 @@ public sealed class CreateUserCommandValidationTests :
string? email = null,
string? surName = null,
string? givenName = null,
string? password = null) =>
new (
string? password = null)
{
return new(
userId ?? Guid.NewGuid(),
email ?? "test@email.com",
surName ?? "test",
givenName ?? "email",
password ?? "Po=PF]PC6t.?8?ks)A6W");
}
}

View File

@ -8,14 +8,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
{
public DeleteUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public DeleteUserCommandTestFixture()
{
UserRepository = new Mock<IUserRepository>();
CommandHandler = new (
CommandHandler = new DeleteUserCommandHandler(
Bus.Object,
UnitOfWork.Object,
NotificationHandler.Object,
@ -23,6 +20,9 @@ public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
User.Object);
}
public DeleteUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public Entities.User SetupUser()
{
var user = new Entities.User(

View File

@ -23,7 +23,7 @@ public sealed class DeleteUserCommandValidationTests :
[Fact]
public void Should_Be_Invalid_For_Empty_User_Id()
{
var command = CreateTestCommand(userId: Guid.Empty);
var command = CreateTestCommand(Guid.Empty);
ShouldHaveSingleError(
command,
@ -31,6 +31,8 @@ public sealed class DeleteUserCommandValidationTests :
"User id may not be empty");
}
private DeleteUserCommand CreateTestCommand(Guid? userId = null) =>
new (userId ?? Guid.NewGuid());
private DeleteUserCommand CreateTestCommand(Guid? userId = null)
{
return new(userId ?? Guid.NewGuid());
}
}

View File

@ -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.Interfaces.Repositories;
using CleanArchitecture.Domain.Settings;
using Microsoft.Extensions.Options;
using Moq;
using System;
using BC = BCrypt.Net.BCrypt;
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.LoginUser;
public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
{
public LoginUserCommandHandler CommandHandler { get; set; }
public Mock<IUserRepository> UserRepository { get; set; }
public IOptions<TokenSettings> TokenSettings { get; set; }
public LoginUserCommandTestFixture()
{
UserRepository = new Mock<IUserRepository>();
@ -26,7 +22,7 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
Secret = "asjdlkasjd87439284)@#(*"
});
CommandHandler = new(
CommandHandler = new LoginUserCommandHandler(
Bus.Object,
UnitOfWork.Object,
NotificationHandler.Object,
@ -34,6 +30,10 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
TokenSettings);
}
public LoginUserCommandHandler CommandHandler { get; set; }
public Mock<IUserRepository> UserRepository { get; set; }
public IOptions<TokenSettings> TokenSettings { get; set; }
public Entities.User SetupUser()
{
var user = new Entities.User(

View File

@ -24,7 +24,7 @@ public sealed class LoginUserCommandValidationTests :
[Fact]
public void Should_Be_Invalid_For_Empty_Email()
{
var command = CreateTestCommand(email: string.Empty);
var command = CreateTestCommand(string.Empty);
ShouldHaveSingleError(
command,
@ -35,7 +35,7 @@ public sealed class LoginUserCommandValidationTests :
[Fact]
public void Should_Be_Invalid_For_Invalid_Email()
{
var command = CreateTestCommand(email: "not a email");
var command = CreateTestCommand("not a email");
ShouldHaveSingleError(
command,
@ -46,7 +46,7 @@ public sealed class LoginUserCommandValidationTests :
[Fact]
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(
command,
@ -122,8 +122,10 @@ public sealed class LoginUserCommandValidationTests :
private LoginUserCommand CreateTestCommand(
string? email = null,
string? password = null) =>
new (
string? password = null)
{
return new(
email ?? "test@email.com",
password ?? "Po=PF]PC6t.?8?ks)A6W");
}
}

View File

@ -8,14 +8,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
{
public UpdateUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public UpdateUserCommandTestFixture()
{
UserRepository = new Mock<IUserRepository>();
CommandHandler = new(
CommandHandler = new UpdateUserCommandHandler(
Bus.Object,
UnitOfWork.Object,
NotificationHandler.Object,
@ -23,6 +20,9 @@ public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
User.Object);
}
public UpdateUserCommandHandler CommandHandler { get; }
private Mock<IUserRepository> UserRepository { get; }
public Entities.User SetupUser()
{
var user = new Entities.User(

View File

@ -24,7 +24,7 @@ public sealed class UpdateUserCommandValidationTests :
[Fact]
public void Should_Be_Invalid_For_Empty_User_Id()
{
var command = CreateTestCommand(userId: Guid.Empty);
var command = CreateTestCommand(Guid.Empty);
ShouldHaveSingleError(
command,
@ -114,11 +114,13 @@ public sealed class UpdateUserCommandValidationTests :
string? email = null,
string? surName = null,
string? givenName = null,
UserRole? role = null) =>
new (
UserRole? role = null)
{
return new(
userId ?? Guid.NewGuid(),
email ?? "test@email.com",
surName ?? "test",
givenName ?? "email",
role ?? UserRole.User);
}
}

View File

@ -9,11 +9,6 @@ namespace CleanArchitecture.Domain.Tests;
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()
{
Bus = new Mock<IMediatorHandler>();
@ -27,6 +22,11 @@ public class CommandHandlerFixtureBase
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)
{
Bus.Verify(

View File

@ -8,7 +8,7 @@ namespace CleanArchitecture.Domain.Tests;
public class ValidationTestBase<TCommand, TValidation>
where TCommand : CommandBase
where TValidation: AbstractValidator<TCommand>
where TValidation : AbstractValidator<TCommand>
{
private readonly TValidation _validation;

View File

@ -29,19 +29,6 @@ public sealed class ApiUser : IUser
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()
{
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 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");
}
}

View File

@ -6,12 +6,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="FluentValidation" Version="11.5.1" />
<PackageReference Include="MediatR" Version="12.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
<PackageReference Include="FluentValidation" Version="11.5.1"/>
<PackageReference Include="MediatR" Version="12.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0"/>
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0"/>
</ItemGroup>
</Project>

View File

@ -6,11 +6,6 @@ namespace CleanArchitecture.Domain.Commands;
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)
{
MessageType = GetType().Name;
@ -18,5 +13,10 @@ public abstract class CommandBase : IRequest
AggregateId = aggregateId;
}
public Guid AggregateId { get; }
public string MessageType { get; }
public DateTime Timestamp { get; }
public ValidationResult? ValidationResult { get; protected set; }
public abstract bool IsValid();
}

View File

@ -10,8 +10,8 @@ namespace CleanArchitecture.Domain.Commands;
public abstract class CommandHandlerBase
{
protected readonly IMediatorHandler _bus;
private readonly IUnitOfWork _unitOfWork;
private readonly DomainNotificationHandler _notifications;
private readonly IUnitOfWork _unitOfWork;
protected CommandHandlerBase(
IMediatorHandler bus,

View File

@ -6,15 +6,15 @@ public sealed class ChangePasswordCommand : CommandBase
{
private readonly ChangePasswordCommandValidation _validation = new();
public string Password { get; }
public string NewPassword { get; }
public ChangePasswordCommand(string password, string newPassword) : base(Guid.NewGuid())
{
Password = password;
NewPassword = newPassword;
}
public string Password { get; }
public string NewPassword { get; }
public override bool IsValid()
{
ValidationResult = _validation.Validate(this);

View File

@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.ChangePassword;
public sealed class ChangePasswordCommandHandler : CommandHandlerBase,
IRequestHandler<ChangePasswordCommand>
{
private readonly IUserRepository _userRepository;
private readonly IUser _user;
private readonly IUserRepository _userRepository;
public ChangePasswordCommandHandler(
IMediatorHandler bus,

View File

@ -6,12 +6,6 @@ public sealed class CreateUserCommand : CommandBase
{
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(
Guid userId,
string email,
@ -26,6 +20,12 @@ public sealed class CreateUserCommand : CommandBase
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()
{
ValidationResult = _validation.Validate(this);

View File

@ -6,13 +6,13 @@ public sealed class DeleteUserCommand : CommandBase
{
private readonly DeleteUserCommandValidation _validation = new();
public Guid UserId { get; }
public DeleteUserCommand(Guid userId) : base(userId)
{
UserId = userId;
}
public Guid UserId { get; }
public override bool IsValid()
{
ValidationResult = _validation.Validate(this);

View File

@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
public sealed class DeleteUserCommandHandler : CommandHandlerBase,
IRequestHandler<DeleteUserCommand>
{
private readonly IUserRepository _userRepository;
private readonly IUser _user;
private readonly IUserRepository _userRepository;
public DeleteUserCommandHandler(
IMediatorHandler bus,

View File

@ -8,9 +8,6 @@ public sealed class LoginUserCommand : CommandBase,
{
private readonly LoginUserCommandValidation _validation = new();
public string Email { get; set; }
public string Password { get; set; }
public LoginUserCommand(
string email,
@ -20,6 +17,9 @@ public sealed class LoginUserCommand : CommandBase,
Password = password;
}
public string Email { get; set; }
public string Password { get; set; }
public override bool IsValid()
{
ValidationResult = _validation.Validate(this);

View File

@ -1,6 +1,7 @@
using System.Security.Claims;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System;
using System.Threading;
using System.Threading.Tasks;
using CleanArchitecture.Domain.Enums;
@ -10,10 +11,9 @@ using CleanArchitecture.Domain.Interfaces.Repositories;
using CleanArchitecture.Domain.Notifications;
using CleanArchitecture.Domain.Settings;
using MediatR;
using BC = BCrypt.Net.BCrypt;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using BC = BCrypt.Net.BCrypt;
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
@ -21,9 +21,9 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
IRequestHandler<LoginUserCommand, string>
{
private const double ExpiryDurationMinutes = 30;
private readonly TokenSettings _tokenSettings;
private readonly IUserRepository _userRepository;
private readonly TokenSettings _tokenSettings;
public LoginUserCommandHandler(
IMediatorHandler bus,
@ -80,10 +80,10 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
{
var claims = new[]
{
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, role.ToString()),
new Claim(ClaimTypes.NameIdentifier, id.ToString())
};
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, role.ToString()),
new Claim(ClaimTypes.NameIdentifier, id.ToString())
};
var securityKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(tokenSettings.Secret));

View File

@ -7,12 +7,6 @@ public sealed class UpdateUserCommand : CommandBase
{
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(
Guid userId,
string email,
@ -27,6 +21,12 @@ public sealed class UpdateUserCommand : CommandBase
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()
{
ValidationResult = _validation.Validate(this);

View File

@ -1,6 +1,5 @@
using System.Threading;
using System.Threading.Tasks;
using CleanArchitecture.Domain.Entities;
using CleanArchitecture.Domain.Enums;
using CleanArchitecture.Domain.Errors;
using CleanArchitecture.Domain.Events.User;
@ -14,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
public sealed class UpdateUserCommandHandler : CommandHandlerBase,
IRequestHandler<UpdateUserCommand>
{
private readonly IUserRepository _userRepository;
private readonly IUser _user;
private readonly IUserRepository _userRepository;
public UpdateUserCommandHandler(
IMediatorHandler bus,

View File

@ -6,14 +6,6 @@ namespace CleanArchitecture.Domain.Entities;
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(
Guid id,
string email,
@ -29,6 +21,14 @@ public class User : Entity
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))]
public void SetEmail(string email)
{

View File

@ -11,7 +11,7 @@ public sealed class UserEventHandler :
INotificationHandler<UserUpdatedEvent>,
INotificationHandler<PasswordChangedEvent>
{
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
@ -21,12 +21,12 @@ public sealed class UserEventHandler :
return Task.CompletedTask;
}
public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken)
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken)
public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

View File

@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
public sealed class PasswordChangedEvent : DomainEvent
{
public Guid UserId { get; }
public PasswordChangedEvent(Guid userId) : base(userId)
{
UserId = userId;
}
public Guid UserId { get; }
}

View File

@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
public sealed class UserCreatedEvent : DomainEvent
{
public Guid UserId { get; }
public UserCreatedEvent(Guid userId) : base(userId)
{
UserId = userId;
}
public Guid UserId { get; }
}

View File

@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
public sealed class UserDeletedEvent : DomainEvent
{
public Guid UserId { get; }
public UserDeletedEvent(Guid userId) : base(userId)
{
UserId = userId;
}
public Guid UserId { get; }
}

View File

@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User;
public sealed class UserUpdatedEvent : DomainEvent
{
public Guid UserId { get; }
public UserUpdatedEvent(Guid userId) : base(userId)
{
UserId = userId;
}
public Guid UserId { get; }
}

View File

@ -17,7 +17,10 @@ public static class CustomValidator
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
.NotEmpty().WithErrorCode(DomainErrorCodes.UserEmptyPassword)

View File

@ -5,8 +5,7 @@ namespace CleanArchitecture.Domain.Interfaces;
public interface IUser
{
string Name { get; }
Guid GetUserId();
UserRole GetUserRole();
string Name { get; }
}

View File

@ -4,11 +4,6 @@ namespace CleanArchitecture.Domain.Notifications;
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(
string key,
string value,
@ -23,4 +18,9 @@ public sealed class DomainNotification : DomainEvent
Data = data;
}
public string Key { get; }
public string Value { get; }
public string Code { get; }
public object? Data { get; set; }
}

View File

@ -15,11 +15,6 @@ public class DomainNotificationHandler : INotificationHandler<DomainNotification
_notifications = new List<DomainNotification>();
}
public virtual List<DomainNotification> GetNotifications()
{
return _notifications;
}
public Task Handle(DomainNotification notification, CancellationToken cancellationToken = default)
{
_notifications.Add(notification);
@ -27,6 +22,11 @@ public class DomainNotificationHandler : INotificationHandler<DomainNotification
return Task.CompletedTask;
}
public virtual List<DomainNotification> GetNotifications()
{
return _notifications;
}
public virtual bool HasNotifications()
{
return GetNotifications().Any();

View File

@ -8,10 +8,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="Moq" Version="4.18.4"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@ -16,9 +16,9 @@ public sealed class DomainNotificationHandlerTests
[Fact]
public void Should_Handle_DomainNotification()
{
string key = "Key";
string value = "Value";
string code = "Code";
var key = "Key";
var value = "Value";
var code = "Code";
var domainNotification = new DomainNotification(key, value, code);
var domainNotificationHandler = new DomainNotificationHandler();
@ -29,9 +29,9 @@ public sealed class DomainNotificationHandlerTests
[Fact]
public void Should_Handle_DomainNotification_Overload()
{
string key = "Key";
string value = "Value";
string code = "Code";
var key = "Key";
var value = "Value";
var code = "Code";
var domainNotification = new DomainNotification(key, value, code);
var domainNotificationHandler = new DomainNotificationHandler();
@ -42,9 +42,9 @@ public sealed class DomainNotificationHandlerTests
[Fact]
public void DomainNotification_HasNotifications_After_Handling_One()
{
string key = "Key";
string value = "Value";
string code = "Code";
var key = "Key";
var value = "Value";
var code = "Code";
var domainNotification = new DomainNotification(key, value, code);
var domainNotificationHandler = new DomainNotificationHandler();

View File

@ -10,9 +10,9 @@ public sealed class DomainNotificationTests
[Fact]
public void Should_Create_DomainNotification_Instance()
{
string key = "Key";
string value = "Value";
string code = "Code";
var key = "Key";
var value = "Value";
var code = "Code";
var domainNotification = new DomainNotification(
key, value, code);
@ -26,9 +26,9 @@ public sealed class DomainNotificationTests
[Fact]
public void Should_Create_DomainNotification_Overload_Instance()
{
string key = "Key";
string value = "Value";
string code = "Code";
var key = "Key";
var value = "Value";
var code = "Code";
var domainNotification = new DomainNotification(
key, value, code);

View File

@ -6,18 +6,18 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" 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.Tools" Version="7.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MediatR" Version="12.0.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" 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.Tools" Version="7.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -6,12 +6,12 @@ namespace CleanArchitecture.Infrastructure.Database;
public class ApplicationDbContext : DbContext
{
public DbSet<User> Users { get; set; } = null!;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new UserConfiguration());

View File

@ -50,19 +50,6 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
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)
{
DbSet.Update(entity);
@ -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();
}
}
}

View File

@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" 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.Sqlite" Version="7.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" 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.Sqlite" Version="7.0.4"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="Xunit.Priority" Version="1.1.6"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
@ -27,8 +27,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj" />
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj"/>
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@ -24,7 +24,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
_fixture = fixture;
}
[Fact, Priority(0)]
[Fact]
[Priority(0)]
public async Task Should_Create_User()
{
var user = new CreateUserViewModel(
@ -44,7 +45,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
_fixture.CreatedUserId = message!.Data;
}
[Fact, Priority(5)]
[Fact]
[Priority(5)]
public async Task Should_Login_User()
{
var user = new LoginUserViewModel(
@ -63,7 +65,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
_fixture.EnableAuthentication();
}
[Fact, Priority(10)]
[Fact]
[Priority(10)]
public async Task Should_Get_Created_Users()
{
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
@ -82,7 +85,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
content.GivenName.Should().Be("Email");
}
[Fact, Priority(10)]
[Fact]
[Priority(10)]
public async Task Should_Get_The_Current_Active_Users()
{
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");
}
[Fact, Priority(15)]
[Fact]
[Priority(15)]
public async Task Should_Update_User()
{
var user = new UpdateUserViewModel(
@ -124,7 +129,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
content.Should().BeEquivalentTo(user);
}
[Fact, Priority(20)]
[Fact]
[Priority(20)]
public async Task Should_Get_Updated_Users()
{
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId);
@ -145,7 +151,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
_fixture.CreatedUserEmail = content.Email;
}
[Fact, Priority(25)]
[Fact]
[Priority(25)]
public async Task Should_Change_User_Password()
{
var user = new ChangePasswordViewModel(
@ -178,7 +185,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
loginMessage?.Data.Should().NotBeEmpty();
}
[Fact, Priority(30)]
[Fact]
[Priority(30)]
public async Task Should_Get_All_User()
{
var response = await _fixture.ServerClient.GetAsync("/api/v1/user");
@ -208,7 +216,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
adminUser.GivenName.Should().Be("User");
}
[Fact, Priority(35)]
[Fact]
[Priority(35)]
public async Task Should_Delete_User()
{
var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + _fixture.CreatedUserId);

View File

@ -2,28 +2,29 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace CleanArchitecture.IntegrationTests.Extensions;
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>));
if (descriptor != null)
services.Remove(descriptor);
services.AddScoped(p =>
DbContextOptionsFactory<TContext>(
p,
(_, options) => options
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
.UseLazyLoadingProxies()
.UseSqlite(connection)));
DbContextOptionsFactory<TContext>(
p,
(_, options) => options
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
.UseLazyLoadingProxies()
.UseSqlite(connection)));
return services;
}

View File

@ -10,7 +10,7 @@ public static class HttpExtensions
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNameCaseInsensitive = true
};
private static T? Deserialize<T>(string json)

View File

@ -9,8 +9,6 @@ namespace CleanArchitecture.IntegrationTests.Fixtures;
public class TestFixtureBase
{
public HttpClient ServerClient { get; }
public TestFixtureBase()
{
var projectDir = Directory.GetCurrentDirectory();
@ -25,6 +23,8 @@ public class TestFixtureBase
ServerClient.Timeout = TimeSpan.FromMinutes(5);
}
public HttpClient ServerClient { get; }
protected virtual void SeedTestData(ApplicationDbContext context)
{
}

View File

@ -18,11 +18,11 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
ServiceProvider serviceProvider,
IServiceProvider scopedServices);
private readonly SqliteConnection _connection = new($"DataSource=:memory:");
private readonly AddCustomSeedDataHandler? _addCustomSeedDataHandler;
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
private readonly SqliteConnection _connection = new("DataSource=:memory:");
private readonly string? _environment;
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
public CleanArchitectureWebApplicationFactory(
AddCustomSeedDataHandler? addCustomSeedDataHandler,
@ -51,7 +51,7 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
var sp = services.BuildServiceProvider();
using IServiceScope scope = sp.CreateScope();
using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider;
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();

View File

@ -6,19 +6,19 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Users\Models.proto" />
<None Remove="Users\UsersApi.proto" />
<None Remove="Users\Models.proto"/>
<None Remove="Users\UsersApi.proto"/>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Users\Models.proto" GrpcServices="Both" />
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both" />
<Protobuf Include="Users\Models.proto" GrpcServices="Both"/>
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.52.0" />
<PackageReference Include="Google.Protobuf" Version="3.22.1"/>
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1"/>
<PackageReference Include="Grpc.AspNetCore" Version="2.52.0"/>
</ItemGroup>
</Project>

View File

@ -8,11 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MockQueryable.Moq" Version="7.0.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="FluentAssertions" Version="6.10.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="MockQueryable.Moq" Version="7.0.0"/>
<PackageReference Include="Moq" Version="4.18.4"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
@ -24,8 +24,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
</ItemGroup>
</Project>

View File

@ -11,37 +11,31 @@ namespace CleanArchitecture.gRPC.Tests.Fixtures;
public sealed class UserTestsFixture
{
private Mock<IUserRepository> UserRepository { get; } = new ();
public UsersApiImplementation UsersApiImplementation { get; }
public IEnumerable<User> ExistingUsers { get; }
public UserTestsFixture()
{
ExistingUsers = new List<User>()
ExistingUsers = new List<User>
{
new (
new(
Guid.NewGuid(),
"test@test.de",
"Test First Name",
"Test Last Name",
"Test Password",
UserRole.User),
new (
new(
Guid.NewGuid(),
"email@Email.de",
"Email First Name",
"Email Last Name",
"Email Password",
UserRole.Admin),
new (
new(
Guid.NewGuid(),
"user@user.de",
"User First Name",
"User Last Name",
"User Password",
UserRole.User),
UserRole.User)
};
var queryable = ExistingUsers.AsQueryable().BuildMock();
@ -52,4 +46,10 @@ public sealed class UserTestsFixture
UsersApiImplementation = new UsersApiImplementation(UserRepository.Object);
}
private Mock<IUserRepository> UserRepository { get; } = new();
public UsersApiImplementation UsersApiImplementation { get; }
public IEnumerable<User> ExistingUsers { get; }
}

View File

@ -6,12 +6,12 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj" />
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
</ItemGroup>
</Project>