Add validator tests for brewery and user commands (#282)

This commit is contained in:
Aaron Po
2026-09-06 17:48:03 -04:00
committed by GitHub
parent 9d5a463d93
commit a3ec7d63f2
13 changed files with 977 additions and 3 deletions

View File

@@ -0,0 +1,39 @@
using Features.Users.Commands.Authentication.Login;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class LoginValidatorTests
{
private readonly LoginValidator _validator = new();
private static LoginCommand ValidCommand() => new("valid.user-99", "Password1!");
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<LoginCommand> result = _validator.TestValidate(ValidCommand());
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyUsername_HasErrorForUsername()
{
LoginCommand command = ValidCommand() with { Username = "" };
TestValidationResult<LoginCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Username).WithErrorMessage("Username is required");
}
[Fact]
public void Validate_EmptyPassword_HasErrorForPassword()
{
LoginCommand command = ValidCommand() with { Password = "" };
TestValidationResult<LoginCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Password).WithErrorMessage("Password is required");
}
}

View File

@@ -0,0 +1,31 @@
using Features.Users.Commands.Authentication.RefreshToken;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class RefreshTokenValidatorTests
{
private readonly RefreshTokenValidator _validator = new();
private static RefreshTokenCommand ValidCommand() => new("a-valid-refresh-token");
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<RefreshTokenCommand> result = _validator.TestValidate(ValidCommand());
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyRefreshToken_HasErrorForRefreshToken()
{
RefreshTokenCommand command = ValidCommand() with { RefreshToken = "" };
TestValidationResult<RefreshTokenCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.RefreshToken)
.WithErrorMessage("Refresh token is required");
}
}

View File

@@ -0,0 +1,241 @@
using Features.Users.Commands.Authentication.RegisterUser;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class RegisterUserValidatorTests
{
private readonly RegisterUserValidator _validator = new();
private static RegisterUserCommand ValidCommand() =>
new(
"valid.user-99",
"Jane",
"Doe",
"jane.doe@example.com",
DateTime.Today.AddYears(-25),
"Password1!"
);
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(ValidCommand());
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyUsername_HasErrorForUsername()
{
RegisterUserCommand command = ValidCommand() with { Username = "" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Username).WithErrorMessage("Username is required");
}
[Fact]
public void Validate_UsernameTooShort_HasErrorForUsername()
{
RegisterUserCommand command = ValidCommand() with { Username = "ab" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Username)
.WithErrorMessage("Username must be between 3 and 64 characters");
}
[Fact]
public void Validate_UsernameTooLong_HasErrorForUsername()
{
RegisterUserCommand command = ValidCommand() with { Username = new string('a', 65) };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Username)
.WithErrorMessage("Username must be between 3 and 64 characters");
}
[Fact]
public void Validate_UsernameHasDisallowedCharacters_HasErrorForUsername()
{
RegisterUserCommand command = ValidCommand() with { Username = "bad user!" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Username)
.WithErrorMessage(
"Username can only contain letters, numbers, dots, underscores, and hyphens"
);
}
[Fact]
public void Validate_EmptyFirstName_HasErrorForFirstName()
{
RegisterUserCommand command = ValidCommand() with { FirstName = "" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.FirstName)
.WithErrorMessage("First name is required");
}
[Fact]
public void Validate_FirstNameTooLong_HasErrorForFirstName()
{
RegisterUserCommand command = ValidCommand() with { FirstName = new string('a', 129) };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.FirstName)
.WithErrorMessage("First name cannot exceed 128 characters");
}
[Fact]
public void Validate_EmptyLastName_HasErrorForLastName()
{
RegisterUserCommand command = ValidCommand() with { LastName = "" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.LastName)
.WithErrorMessage("Last name is required");
}
[Fact]
public void Validate_LastNameTooLong_HasErrorForLastName()
{
RegisterUserCommand command = ValidCommand() with { LastName = new string('a', 129) };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.LastName)
.WithErrorMessage("Last name cannot exceed 128 characters");
}
[Fact]
public void Validate_EmptyEmail_HasErrorForEmail()
{
RegisterUserCommand command = ValidCommand() with { Email = "" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Email).WithErrorMessage("Email is required");
}
[Fact]
public void Validate_InvalidEmailFormat_HasErrorForEmail()
{
RegisterUserCommand command = ValidCommand() with { Email = "not-an-email" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Email).WithErrorMessage("Invalid email format");
}
[Fact]
public void Validate_EmailTooLong_HasErrorForEmail()
{
string localPart = new('a', 121);
RegisterUserCommand command = ValidCommand() with { Email = $"{localPart}@example.com" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Email)
.WithErrorMessage("Email cannot exceed 128 characters");
}
[Fact]
public void Validate_DateOfBirthUnderMinimumAge_HasErrorForDateOfBirth()
{
RegisterUserCommand command = ValidCommand() with
{
DateOfBirth = DateTime.Today.AddYears(-18)
};
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.DateOfBirth)
.WithErrorMessage("You must be at least 19 years old to register");
}
[Fact]
public void Validate_EmptyPassword_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.Password).WithErrorMessage("Password is required");
}
[Fact]
public void Validate_PasswordTooShort_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "Ax1!23" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Password)
.WithErrorMessage("Password must be at least 8 characters");
}
[Fact]
public void Validate_PasswordMissingUppercase_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "password1!" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Password)
.WithErrorMessage("Password must contain at least one uppercase letter");
}
[Fact]
public void Validate_PasswordMissingLowercase_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "PASSWORD1!" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Password)
.WithErrorMessage("Password must contain at least one lowercase letter");
}
[Fact]
public void Validate_PasswordMissingNumber_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "Password!" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Password)
.WithErrorMessage("Password must contain at least one number");
}
[Fact]
public void Validate_PasswordMissingSpecialCharacter_HasErrorForPassword()
{
RegisterUserCommand command = ValidCommand() with { Password = "Password1" };
TestValidationResult<RegisterUserCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.Password)
.WithErrorMessage("Password must contain at least one special character");
}
}

View File

@@ -0,0 +1,54 @@
using Features.Users.Commands.Account.UpdateEmail;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class UpdateEmailValidatorTests
{
private readonly UpdateEmailValidator _validator = new();
private static UpdateEmailCommand ValidCommand() => new(Guid.NewGuid(), "user@example.com");
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<UpdateEmailCommand> result = _validator.TestValidate(ValidCommand());
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyNewEmail_HasErrorForNewEmail()
{
UpdateEmailCommand command = ValidCommand() with { NewEmail = "" };
TestValidationResult<UpdateEmailCommand> result = _validator.TestValidate(command);
result.ShouldHaveValidationErrorFor(x => x.NewEmail).WithErrorMessage("Email is required");
}
[Fact]
public void Validate_InvalidEmailFormat_HasErrorForNewEmail()
{
UpdateEmailCommand command = ValidCommand() with { NewEmail = "not-an-email" };
TestValidationResult<UpdateEmailCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewEmail)
.WithErrorMessage("Invalid email format");
}
[Fact]
public void Validate_NewEmailTooLong_HasErrorForNewEmail()
{
string localPart = new('a', 120);
UpdateEmailCommand command = ValidCommand() with { NewEmail = $"{localPart}@example.com" };
TestValidationResult<UpdateEmailCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewEmail)
.WithErrorMessage("Email cannot exceed 128 characters");
}
}

View File

@@ -0,0 +1,122 @@
using Features.Users.Commands.Account.UpdatePassword;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class UpdatePasswordValidatorTests
{
private readonly UpdatePasswordValidator _validator = new();
private static UpdatePasswordCommand ValidCommand() =>
new(Guid.NewGuid(), "OldPass1!", "NewPass2@");
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(
ValidCommand()
);
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyCurrentPassword_HasErrorForCurrentPassword()
{
UpdatePasswordCommand command = ValidCommand() with { CurrentPassword = "" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.CurrentPassword)
.WithErrorMessage("Current password is required");
}
[Fact]
public void Validate_EmptyNewPassword_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("New password is required");
}
[Fact]
public void Validate_NewPasswordTooShort_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "Ax1!23" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("Password must be at least 8 characters");
}
[Fact]
public void Validate_NewPasswordMissingUppercase_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "newpass1!" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("Password must contain at least one uppercase letter");
}
[Fact]
public void Validate_NewPasswordMissingLowercase_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "NEWPASS1!" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("Password must contain at least one lowercase letter");
}
[Fact]
public void Validate_NewPasswordMissingNumber_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "NewPassword!" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("Password must contain at least one number");
}
[Fact]
public void Validate_NewPasswordMissingSpecialCharacter_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with { NewPassword = "NewPassword1" };
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("Password must contain at least one special character");
}
[Fact]
public void Validate_NewPasswordSameAsCurrentPassword_HasErrorForNewPassword()
{
UpdatePasswordCommand command = ValidCommand() with
{
CurrentPassword = "SamePass1!",
NewPassword = "SamePass1!"
};
TestValidationResult<UpdatePasswordCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewPassword)
.WithErrorMessage("New password must be different from the current password");
}
}

View File

@@ -0,0 +1,97 @@
using Features.Users.Commands.Profile.UpdateProfile;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class UpdateProfileValidatorTests
{
private readonly UpdateProfileValidator _validator = new();
private static UpdateProfileCommand ValidCommand() =>
new(Guid.NewGuid(), "John", "Doe", DateTime.Today.AddYears(-25));
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(
ValidCommand()
);
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyFirstName_HasErrorForFirstName()
{
UpdateProfileCommand command = ValidCommand() with { FirstName = "" };
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.FirstName)
.WithErrorMessage("First name is required");
}
[Fact]
public void Validate_FirstNameTooLong_HasErrorForFirstName()
{
UpdateProfileCommand command = ValidCommand() with { FirstName = new string('a', 129) };
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.FirstName)
.WithErrorMessage("First name cannot exceed 128 characters");
}
[Fact]
public void Validate_EmptyLastName_HasErrorForLastName()
{
UpdateProfileCommand command = ValidCommand() with { LastName = "" };
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.LastName)
.WithErrorMessage("Last name is required");
}
[Fact]
public void Validate_LastNameTooLong_HasErrorForLastName()
{
UpdateProfileCommand command = ValidCommand() with { LastName = new string('a', 129) };
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.LastName)
.WithErrorMessage("Last name cannot exceed 128 characters");
}
[Fact]
public void Validate_DefaultDateOfBirth_HasErrorForDateOfBirth()
{
UpdateProfileCommand command = ValidCommand() with { DateOfBirth = default };
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.DateOfBirth)
.WithErrorMessage("Date of birth is required");
}
[Fact]
public void Validate_DateOfBirthExactlyNineteenYearsAgo_HasErrorForDateOfBirth()
{
UpdateProfileCommand command = ValidCommand() with
{
DateOfBirth = DateTime.Today.AddYears(-19)
};
TestValidationResult<UpdateProfileCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.DateOfBirth)
.WithErrorMessage("You must be at least 19 years old");
}
}

View File

@@ -0,0 +1,71 @@
using Features.Users.Commands.Account.UpdateUsername;
using FluentValidation.TestHelper;
namespace Features.Users.Tests.Commands;
public class UpdateUsernameValidatorTests
{
private readonly UpdateUsernameValidator _validator = new();
private static UpdateUsernameCommand ValidCommand() => new(Guid.NewGuid(), "valid.user-99");
[Fact]
public void Validate_ValidCommand_HasNoValidationErrors()
{
TestValidationResult<UpdateUsernameCommand> result = _validator.TestValidate(
ValidCommand()
);
result.ShouldNotHaveAnyValidationErrors();
}
[Fact]
public void Validate_EmptyNewUsername_HasErrorForNewUsername()
{
UpdateUsernameCommand command = ValidCommand() with { NewUsername = "" };
TestValidationResult<UpdateUsernameCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewUsername)
.WithErrorMessage("Username is required");
}
[Fact]
public void Validate_NewUsernameTooShort_HasErrorForNewUsername()
{
UpdateUsernameCommand command = ValidCommand() with { NewUsername = "ab" };
TestValidationResult<UpdateUsernameCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewUsername)
.WithErrorMessage("Username must be between 3 and 64 characters");
}
[Fact]
public void Validate_NewUsernameTooLong_HasErrorForNewUsername()
{
UpdateUsernameCommand command = ValidCommand() with { NewUsername = new string('a', 65) };
TestValidationResult<UpdateUsernameCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewUsername)
.WithErrorMessage("Username must be between 3 and 64 characters");
}
[Fact]
public void Validate_NewUsernameHasDisallowedCharacters_HasErrorForNewUsername()
{
UpdateUsernameCommand command = ValidCommand() with { NewUsername = "bad user!" };
TestValidationResult<UpdateUsernameCommand> result = _validator.TestValidate(command);
result
.ShouldHaveValidationErrorFor(x => x.NewUsername)
.WithErrorMessage(
"Username can only contain letters, numbers, dots, underscores, and hyphens"
);
}
}