diff --git a/.idea/.idea.DotNetAngular/.idea/workspace.xml b/.idea/.idea.DotNetAngular/.idea/workspace.xml
index f6f9c31..146affb 100644
--- a/.idea/.idea.DotNetAngular/.idea/workspace.xml
+++ b/.idea/.idea.DotNetAngular/.idea/workspace.xml
@@ -20,10 +20,9 @@
-
-
-
-
+
+
+
@@ -40,7 +39,7 @@
@@ -66,32 +65,33 @@
- {
+ "keyToString": {
+ ".NET Launch Settings Profile.API: Angular_dev.executor": "Run",
+ "RunOnceActivity.MCP Project settings loaded": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.cidr.known.project.marker": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "RunOnceActivity.readMode.enableVisualFormatting": "true",
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
+ "cidr.known.project.marker": "true",
+ "codeWithMe.voiceChat.enabledByDefault": "false",
+ "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
+ "git-widget-placeholder": "develop",
+ "git.auto.fetch.suggestion.counter": "1",
+ "junie.onboarding.icon.badge.shown": "true",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "vcs.Git",
+ "to.speed.mode.migration.done": "true",
+ "ts.external.directory.path": "/home/natlinux/RiderProjects/DotNetAngular/src/ClientApp/node_modules/typescript/lib",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
@@ -235,6 +235,7 @@
+
@@ -300,7 +301,15 @@
1773601314558
-
+
+
+ 1777739314669
+
+
+
+ 1777739314669
+
+
@@ -345,7 +354,8 @@
-
+
+
diff --git a/DotNetAngular.sln.DotSettings.user b/DotNetAngular.sln.DotSettings.user
index d6815eb..3f2d357 100644
--- a/DotNetAngular.sln.DotSettings.user
+++ b/DotNetAngular.sln.DotSettings.user
@@ -1,2 +1,5 @@
+ <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Solution />
+</SessionState>
True
\ No newline at end of file
diff --git a/tests/Application.FunctionalTest/UnitTest1.cs b/tests/Application.FunctionalTest/UnitTest1.cs
deleted file mode 100644
index a8f037c..0000000
--- a/tests/Application.FunctionalTest/UnitTest1.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Application.FunctionalTest;
-
-public class UnitTest1
-{
- [Fact]
- public void Test1()
- {
- }
-}
\ No newline at end of file
diff --git a/tests/Application.UnitTest/Application.UnitTest.csproj b/tests/Application.UnitTest/Application.UnitTest.csproj
index c3a4595..56c559f 100644
--- a/tests/Application.UnitTest/Application.UnitTest.csproj
+++ b/tests/Application.UnitTest/Application.UnitTest.csproj
@@ -18,10 +18,17 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
diff --git a/tests/Application.UnitTest/Services/AuthenticationServiceTests.cs b/tests/Application.UnitTest/Services/AuthenticationServiceTests.cs
new file mode 100644
index 0000000..7bd5a95
--- /dev/null
+++ b/tests/Application.UnitTest/Services/AuthenticationServiceTests.cs
@@ -0,0 +1,285 @@
+using Application.Common.Results;
+using Application.DTOs;
+using Application.Errors;
+using Application.Interfaces;
+using Application.Models;
+using Application.Services;
+using Application.Validators;
+using Domain.Entities;
+using Domain.Interface;
+using Microsoft.AspNetCore.Identity;
+using NSubstitute;
+
+namespace Application.UnitTest.Services;
+
+public class AuthenticationServiceTests
+{
+ private readonly IUnitOfWork _unitOfWork = Substitute.For();
+ private readonly IUserRepository _userRepository = Substitute.For();
+ private readonly IJwtService _jwtService = Substitute.For();
+ private readonly IEmailService _emailService = Substitute.For();
+ private readonly AuthenticationService _sut;
+
+ public AuthenticationServiceTests()
+ {
+ _sut = new AuthenticationService(
+ _unitOfWork,
+ _userRepository,
+ new LoginRequestValidator(),
+ new RegisterRequestValidator(),
+ _jwtService,
+ _emailService);
+ }
+
+ [Fact]
+ public async Task RegisterAsync_ShouldReturnSuccess_WhenRequestIsValid()
+ {
+ var request = new RegisterRequest("newuser", "new@example.com", "ValidP@ss1!");
+ _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null);
+ _userRepository.GetUserByUsernameAsync(request.Username).Returns((User?)null);
+
+ var result = await _sut.RegisterAsync(request);
+
+ Assert.True(result.IsSuccess);
+ await _userRepository.Received(1).AddAsync(Arg.Any());
+ await _unitOfWork.Received(1).CommitAsync();
+ }
+
+ [Fact]
+ public async Task RegisterAsync_ShouldReturnFailure_WhenValidationFails()
+ {
+ var request = new RegisterRequest("", "", "");
+
+ var result = await _sut.RegisterAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(ErrorTypeConstant.ValidationError, result.Error.Code);
+ }
+
+ [Fact]
+ public async Task RegisterAsync_ShouldReturnFailure_WhenEmailAlreadyExists()
+ {
+ var request = new RegisterRequest("newuser", "existing@example.com", "ValidP@ss1!");
+ _userRepository.GetUserByEmailAsync(request.Email).Returns(new User
+ {
+ Username = "existing", Email = "existing@example.com", Password = "hash"
+ });
+
+ var result = await _sut.RegisterAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.EmailAlreadyExists, result.Error);
+ }
+
+ [Fact]
+ public async Task RegisterAsync_ShouldReturnFailure_WhenUsernameAlreadyExists()
+ {
+ var request = new RegisterRequest("existinguser", "new@example.com", "ValidP@ss1!");
+ _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null);
+ _userRepository.GetUserByUsernameAsync(request.Username).Returns(new User
+ {
+ Username = "existinguser", Email = "existing@example.com", Password = "hash"
+ });
+
+ var result = await _sut.RegisterAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.UsernameAlreadyExists, result.Error);
+ }
+
+ [Fact]
+ public async Task LoginAsync_ShouldReturnSuccess_WhenCredentialsAreValid()
+ {
+ var password = "ValidP@ss1!";
+ var user = new User
+ {
+ Id = 1,
+ Username = "testuser",
+ Email = "test@example.com",
+ Password = new PasswordHasher().HashPassword(
+ new User { Username = "testuser", Email = "test@example.com", Password = password },
+ password)
+ };
+ var request = new LoginRequest(user.Email, password);
+ _userRepository.GetUserByEmailAsync(request.Email).Returns(user);
+ _jwtService.GenerateTokenAsync(user).Returns("access-token");
+ _jwtService.GenerateAndSaveRefreshTokenAsync(user).Returns("refresh-token");
+
+ var result = await _sut.LoginAsync(request);
+
+ Assert.True(result.IsSuccess);
+ _userRepository.Received(1).Update(user);
+ }
+
+ [Fact]
+ public async Task LoginAsync_ShouldReturnFailure_WhenValidationFails()
+ {
+ var request = new LoginRequest("", "");
+
+ var result = await _sut.LoginAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(ErrorTypeConstant.ValidationError, result.Error.Code);
+ }
+
+ [Fact]
+ public async Task LoginAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ var request = new LoginRequest("unknown@example.com", "password123");
+ _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null);
+
+ var result = await _sut.LoginAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task RefreshTokensAsync_ShouldReturnSuccess_WhenRefreshTokenIsValid()
+ {
+ var request = new RefreshTokenRequest { UserId = 1, RefreshToken = "valid-refresh-token" };
+ var user = new User { Id = 1, Username = "testuser", Email = "test@example.com", Password = "hash" };
+ _jwtService.ValidateRefreshTokenAsync(request.UserId, request.RefreshToken).Returns(user);
+ _jwtService.GenerateTokenAsync(user).Returns("new-access-token");
+ _jwtService.GenerateAndSaveRefreshTokenAsync(user).Returns("new-refresh-token");
+
+ var result = await _sut.RefreshTokensAsync(request);
+
+ Assert.True(result.IsSuccess);
+ }
+
+ [Fact]
+ public async Task RefreshTokensAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ var request = new RefreshTokenRequest { UserId = 99, RefreshToken = "invalid-token" };
+ _jwtService.ValidateRefreshTokenAsync(request.UserId, request.RefreshToken).Returns((User?)null);
+
+ var result = await _sut.RefreshTokensAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task SendResetEmailAsync_ShouldReturnSuccess_WhenUserExists()
+ {
+ var email = "test@example.com";
+ var user = new User { Id = 1, Username = "testuser", Email = email, Password = "hash" };
+ _userRepository.GetUserByEmailAsync(email).Returns(user);
+
+ var result = await _sut.SendResetEmailAsync(email);
+
+ Assert.True(result.IsSuccess);
+ _emailService.Received(1).SendEmailAsync(Arg.Any());
+ _userRepository.Received(1).Update(user);
+ await _unitOfWork.Received(1).CommitAsync();
+ }
+
+ [Fact]
+ public async Task SendResetEmailAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ var email = "unknown@example.com";
+ _userRepository.GetUserByEmailAsync(email).Returns((User?)null);
+
+ var result = await _sut.SendResetEmailAsync(email);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task ResetPasswordAsync_ShouldReturnSuccess_WhenTokenIsValid()
+ {
+ var dto = new ResetPasswordDto
+ {
+ Email = "test@example.com",
+ EmailToken = "valid-token",
+ NewPassword = "NewValidP@ss1"
+ };
+ var user = new User
+ {
+ Id = 1,
+ Username = "testuser",
+ Email = dto.Email,
+ Password = "old-hash",
+ ResetPasswordToken = "valid-token",
+ ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(1)
+ };
+ _userRepository.GetUserByEmailAsync(dto.Email).Returns(user);
+
+ var result = await _sut.ResetPasswordAsync(dto);
+
+ Assert.True(result.IsSuccess);
+ _userRepository.Received(1).Update(user);
+ await _unitOfWork.Received(1).CommitAsync();
+ }
+
+ [Fact]
+ public async Task ResetPasswordAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ var dto = new ResetPasswordDto
+ {
+ Email = "unknown@example.com",
+ EmailToken = "token",
+ NewPassword = "NewValidP@ss1"
+ };
+ _userRepository.GetUserByEmailAsync(dto.Email).Returns((User?)null);
+
+ var result = await _sut.ResetPasswordAsync(dto);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task ResetPasswordAsync_ShouldReturnFailure_WhenTokenIsInvalid()
+ {
+ var dto = new ResetPasswordDto
+ {
+ Email = "test@example.com",
+ EmailToken = "wrong-token",
+ NewPassword = "NewValidP@ss1"
+ };
+ var user = new User
+ {
+ Id = 1,
+ Username = "testuser",
+ Email = dto.Email,
+ Password = "old-hash",
+ ResetPasswordToken = "valid-token",
+ ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(1)
+ };
+ _userRepository.GetUserByEmailAsync(dto.Email).Returns(user);
+
+ var result = await _sut.ResetPasswordAsync(dto);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.InvalidResetLink, result.Error);
+ }
+
+ [Fact]
+ public async Task ResetPasswordAsync_ShouldReturnFailure_WhenTokenIsExpired()
+ {
+ var dto = new ResetPasswordDto
+ {
+ Email = "test@example.com",
+ EmailToken = "valid-token",
+ NewPassword = "NewValidP@ss1"
+ };
+ var user = new User
+ {
+ Id = 1,
+ Username = "testuser",
+ Email = dto.Email,
+ Password = "old-hash",
+ ResetPasswordToken = "valid-token",
+ ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(-1)
+ };
+ _userRepository.GetUserByEmailAsync(dto.Email).Returns(user);
+
+ var result = await _sut.ResetPasswordAsync(dto);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(AuthError.InvalidResetLink, result.Error);
+ }
+}
diff --git a/tests/Application.UnitTest/Services/UserServiceTests.cs b/tests/Application.UnitTest/Services/UserServiceTests.cs
new file mode 100644
index 0000000..a970512
--- /dev/null
+++ b/tests/Application.UnitTest/Services/UserServiceTests.cs
@@ -0,0 +1,222 @@
+using Application.Common.Results;
+using Application.DTOs;
+using Application.Errors;
+using Application.Models;
+using Application.Services;
+using Application.Validators;
+using Domain.Entities;
+using Domain.Interface;
+using NSubstitute;
+
+namespace Application.UnitTest.Services;
+
+public class UserServiceTests
+{
+ private readonly IUnitOfWork _unitOfWork = Substitute.For();
+ private readonly IUserRepository _userRepository = Substitute.For();
+ private readonly IUserRoleRepository _userRoleRepository = Substitute.For();
+ private readonly UserService _sut;
+
+ public UserServiceTests()
+ {
+ _sut = new UserService(
+ _unitOfWork,
+ new UserUpdateRequestValidator(),
+ _userRepository,
+ _userRoleRepository);
+ }
+
+ [Fact]
+ public async Task GetAsync_ShouldReturnPagedResults()
+ {
+ var users = new List
+ {
+ new() { Id = 1, Username = "user1", Email = "user1@test.com", Password = "hash",
+ UserRoles = [new UserRole { Role = new Role { Id = 1, Name = "Admin" } }] },
+ new() { Id = 2, Username = "user2", Email = "user2@test.com", Password = "hash",
+ UserRoles = [new UserRole { Role = new Role { Id = 2, Name = "User" } }] }
+ };
+ _userRepository.GetAllAsync().Returns(users);
+
+ var result = await _sut.GetAsync(1, 10);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(2, result.Value.Items.Count);
+ Assert.Equal(2, result.Value.TotalCount);
+ }
+
+ [Fact]
+ public async Task UpdateAsync_ShouldReturnSuccess_WhenRequestIsValid()
+ {
+ var request = new UserUpdateRequest(1, "updateduser", "updated@test.com");
+ var user = new User { Id = 1, Username = "olduser", Email = "old@test.com", Password = "hash" };
+ _userRepository.GetByIdAsync(request.Id).Returns(user);
+
+ var result = await _sut.UpdateAsync(request);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(request.Username, user.Username);
+ Assert.Equal(request.Email, user.Email);
+ _userRepository.Received(1).Update(user);
+ await _unitOfWork.Received(1).CommitAsync();
+ }
+
+ [Fact]
+ public async Task UpdateAsync_ShouldReturnFailure_WhenValidationFails()
+ {
+ var request = new UserUpdateRequest(0, "", "");
+
+ var result = await _sut.UpdateAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(ErrorTypeConstant.ValidationError, result.Error.Code);
+ }
+
+ [Fact]
+ public async Task UpdateAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ var request = new UserUpdateRequest(99, "user", "user@test.com");
+ _userRepository.GetByIdAsync(request.Id).Returns((User?)null);
+
+ var result = await _sut.UpdateAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_ShouldReturnSuccess_WhenUserExists()
+ {
+ var user = new User { Id = 2, Username = "user2", Email = "user2@test.com", Password = "hash" };
+ _userRepository.GetByIdAsync(2).Returns(user);
+
+ var result = await _sut.DeleteAsync(2, 1);
+
+ Assert.True(result.IsSuccess);
+ _userRepository.Received(1).Delete(user);
+ await _unitOfWork.Received(1).CommitAsync();
+ }
+
+ [Fact]
+ public async Task DeleteAsync_ShouldReturnFailure_WhenDeletingYourself()
+ {
+ var result = await _sut.DeleteAsync(1, 1);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.CannotDeleteYourself, result.Error);
+ }
+
+ [Fact]
+ public async Task DeleteAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ _userRepository.GetByIdAsync(99).Returns((User?)null);
+
+ var result = await _sut.DeleteAsync(99, 1);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task GetUserByIdAsync_ShouldReturnUser_WhenUserExists()
+ {
+ var user = new User
+ {
+ Id = 1, Username = "testuser", Email = "test@test.com", Password = "hash",
+ UserRoles = [new UserRole { Role = new Role { Id = 1, Name = "Admin" } }]
+ };
+ _userRepository.GetByIdAsync(1).Returns(user);
+
+ var result = await _sut.GetUserByIdAsync(1);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(user.Id, result.Value.Id);
+ Assert.Equal(user.Username, result.Value.Username);
+ Assert.Equal(user.Email, result.Value.Email);
+ }
+
+ [Fact]
+ public async Task GetUserByIdAsync_ShouldReturnFailure_WhenUserNotFound()
+ {
+ _userRepository.GetByIdAsync(99).Returns((User?)null);
+
+ var result = await _sut.GetUserByIdAsync(99);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.UserNotFound, result.Error);
+ }
+
+ [Fact]
+ public async Task AssignRoleAsync_ShouldReturnSuccess_WhenUserDoesNotHaveRole()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(false);
+ _userRoleRepository.AddRoleAsync(request.UserId, request.RoleId).Returns(true);
+
+ var result = await _sut.AssignRoleAsync(request);
+
+ Assert.True(result.IsSuccess);
+ }
+
+ [Fact]
+ public async Task AssignRoleAsync_ShouldReturnFailure_WhenUserAlreadyHasRole()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(true);
+
+ var result = await _sut.AssignRoleAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.UserAlreadyHasRole, result.Error);
+ }
+
+ [Fact]
+ public async Task AssignRoleAsync_ShouldReturnFailure_WhenAddRoleFails()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(false);
+ _userRoleRepository.AddRoleAsync(request.UserId, request.RoleId).Returns(false);
+
+ var result = await _sut.AssignRoleAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.FailedToAssignRole, result.Error);
+ }
+
+ [Fact]
+ public async Task RevokeRoleAsync_ShouldReturnSuccess_WhenUserHasRole()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(true);
+ _userRoleRepository.RemoveRoleAsync(request.UserId, request.RoleId).Returns(true);
+
+ var result = await _sut.RevokeRoleAsync(request);
+
+ Assert.True(result.IsSuccess);
+ }
+
+ [Fact]
+ public async Task RevokeRoleAsync_ShouldReturnFailure_WhenUserHasNoRole()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(false);
+
+ var result = await _sut.RevokeRoleAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.UserHasNoRole, result.Error);
+ }
+
+ [Fact]
+ public async Task RevokeRoleAsync_ShouldReturnFailure_WhenRemoveRoleFails()
+ {
+ var request = new AssingRoleRequest(1, 2);
+ _userRoleRepository.HasRoleAsync(request.UserId, request.RoleId).Returns(true);
+ _userRoleRepository.RemoveRoleAsync(request.UserId, request.RoleId).Returns(false);
+
+ var result = await _sut.RevokeRoleAsync(request);
+
+ Assert.True(result.IsFailure);
+ Assert.Equal(UserError.FailedToRevokeRole, result.Error);
+ }
+}
diff --git a/tests/Application.UnitTest/UnitTest1.cs b/tests/Application.UnitTest/UnitTest1.cs
deleted file mode 100644
index 612fee0..0000000
--- a/tests/Application.UnitTest/UnitTest1.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Application.UnitTest;
-
-public class UnitTest1
-{
- [Fact]
- public void Test1()
- {
- }
-}
\ No newline at end of file
diff --git a/tests/Application.UnitTest/Validators/LoginRequestValidatorTests.cs b/tests/Application.UnitTest/Validators/LoginRequestValidatorTests.cs
new file mode 100644
index 0000000..0a951b2
--- /dev/null
+++ b/tests/Application.UnitTest/Validators/LoginRequestValidatorTests.cs
@@ -0,0 +1,33 @@
+using Application.Models;
+using Application.Validators;
+
+namespace Application.UnitTest.Validators;
+
+public class LoginRequestValidatorTests
+{
+ private readonly LoginRequestValidator _sut = new();
+
+ [Fact]
+ public void Validate_ShouldPass_WhenAllFieldsAreValid()
+ {
+ var request = new LoginRequest("test@example.com", "password123");
+
+ var result = _sut.Validate(request);
+
+ Assert.True(result.IsValid);
+ }
+
+ [Theory]
+ [InlineData("", "password123", "Email")]
+ [InlineData("not-an-email", "password123", "Email")]
+ [InlineData("test@example.com", "", "Password")]
+ public void Validate_ShouldFail_WhenFieldIsInvalid(string email, string password, string expectedProperty)
+ {
+ var request = new LoginRequest(email, password);
+
+ var result = _sut.Validate(request);
+
+ Assert.False(result.IsValid);
+ Assert.Contains(result.Errors, e => e.PropertyName == expectedProperty);
+ }
+}
diff --git a/tests/Application.UnitTest/Validators/RegisterRequestValidatorTests.cs b/tests/Application.UnitTest/Validators/RegisterRequestValidatorTests.cs
new file mode 100644
index 0000000..073758f
--- /dev/null
+++ b/tests/Application.UnitTest/Validators/RegisterRequestValidatorTests.cs
@@ -0,0 +1,41 @@
+using Application.Models;
+using Application.Validators;
+
+namespace Application.UnitTest.Validators;
+
+public class RegisterRequestValidatorTests
+{
+ private readonly RegisterRequestValidator _sut = new();
+
+ [Fact]
+ public void Validate_ShouldPass_WhenAllFieldsAreValid()
+ {
+ var request = new RegisterRequest("ValidUser", "test@example.com", "ValidP@ss1");
+
+ var result = _sut.Validate(request);
+
+ Assert.True(result.IsValid);
+ }
+
+ [Theory]
+ [InlineData("ValidUser", "", "ValidP@ss1", "Email")]
+ [InlineData("ValidUser", "invalid-email", "ValidP@ss1", "Email")]
+ [InlineData("ValidUser", "test@example.com", "", "Password")]
+ [InlineData("ValidUser", "test@example.com", "short1A@", "Password")]
+ [InlineData("ValidUser", "test@example.com", "nouppercase1@", "Password")]
+ [InlineData("ValidUser", "test@example.com", "NOLOWERCASE1@", "Password")]
+ [InlineData("ValidUser", "test@example.com", "NoDigit@aa", "Password")]
+ [InlineData("ValidUser", "test@example.com", "NoSpecialChar1", "Password")]
+ [InlineData("", "test@example.com", "ValidP@ss1", "Username")]
+ [InlineData("ab", "test@example.com", "ValidP@ss1", "Username")]
+ [InlineData("user@name", "test@example.com", "ValidP@ss1", "Username")]
+ public void Validate_ShouldFail_WhenFieldIsInvalid(string username, string email, string password, string expectedProperty)
+ {
+ var request = new RegisterRequest(username, email, password);
+
+ var result = _sut.Validate(request);
+
+ Assert.False(result.IsValid);
+ Assert.Contains(result.Errors, e => e.PropertyName == expectedProperty);
+ }
+}
diff --git a/tests/Application.UnitTest/Validators/UserUpdateRequestValidatorTests.cs b/tests/Application.UnitTest/Validators/UserUpdateRequestValidatorTests.cs
new file mode 100644
index 0000000..5aa70ce
--- /dev/null
+++ b/tests/Application.UnitTest/Validators/UserUpdateRequestValidatorTests.cs
@@ -0,0 +1,34 @@
+using Application.Models;
+using Application.Validators;
+
+namespace Application.UnitTest.Validators;
+
+public class UserUpdateRequestValidatorTests
+{
+ private readonly UserUpdateRequestValidator _sut = new();
+
+ [Fact]
+ public void Validate_ShouldPass_WhenAllFieldsAreValid()
+ {
+ var request = new UserUpdateRequest(1, "ValidUser", "test@example.com");
+
+ var result = _sut.Validate(request);
+
+ Assert.True(result.IsValid);
+ }
+
+ [Theory]
+ [InlineData(0, "ValidUser", "test@example.com", "Id")]
+ [InlineData(1, "", "test@example.com", "Username")]
+ [InlineData(1, "ValidUser", "", "Email")]
+ [InlineData(1, "ValidUser", "invalid-email", "Email")]
+ public void Validate_ShouldFail_WhenFieldIsInvalid(int id, string username, string email, string expectedProperty)
+ {
+ var request = new UserUpdateRequest(id, username, email);
+
+ var result = _sut.Validate(request);
+
+ Assert.False(result.IsValid);
+ Assert.Contains(result.Errors, e => e.PropertyName == expectedProperty);
+ }
+}