mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Add validator tests for brewery and user commands (#282)
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
using Features.Breweries.Commands.CreateBrewery;
|
||||
using FluentValidation.TestHelper;
|
||||
|
||||
namespace Features.Breweries.Tests.Commands;
|
||||
|
||||
public class CreateBreweryValidatorTests
|
||||
{
|
||||
private readonly CreateBreweryValidator _validator = new();
|
||||
|
||||
private static CreateBreweryLocation ValidLocation() =>
|
||||
new(Guid.NewGuid(), "123 Main St", null, "12345", null);
|
||||
|
||||
private static CreateBreweryCommand ValidCommand() =>
|
||||
new(Guid.NewGuid(), "Valid Brewery", "A valid description.", ValidLocation());
|
||||
|
||||
[Fact]
|
||||
public void Validate_ValidCommand_HasNoValidationErrors()
|
||||
{
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(ValidCommand());
|
||||
|
||||
result.ShouldNotHaveAnyValidationErrors();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyPostedById_HasErrorForPostedById()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { PostedById = Guid.Empty };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.PostedById)
|
||||
.WithErrorMessage("PostedById is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyBreweryName_HasErrorForBreweryName()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { BreweryName = "" };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.BreweryName)
|
||||
.WithErrorMessage("Brewery name is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_BreweryNameTooLong_HasErrorForBreweryName()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { BreweryName = new string('a', 257) };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.BreweryName)
|
||||
.WithErrorMessage("Brewery name cannot exceed 256 characters.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyDescription_HasErrorForDescription()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { Description = "" };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Description)
|
||||
.WithErrorMessage("Description is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_DescriptionTooLong_HasErrorForDescription()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { Description = new string('a', 513) };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Description)
|
||||
.WithErrorMessage("Description cannot exceed 512 characters.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_NullLocation_HasErrorForLocationOnly()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with { Location = null! };
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result.ShouldHaveValidationErrorFor(x => x.Location).WithErrorMessage("Location is required.");
|
||||
result.ShouldNotHaveValidationErrorFor("Location.CityId");
|
||||
result.ShouldNotHaveValidationErrorFor("Location.AddressLine1");
|
||||
result.ShouldNotHaveValidationErrorFor("Location.PostalCode");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyCityId_HasErrorForCityId()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with
|
||||
{
|
||||
Location = ValidLocation() with { CityId = Guid.Empty }
|
||||
};
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Location.CityId)
|
||||
.WithErrorMessage("CityId is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyAddressLine1_HasErrorForAddressLine1()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with
|
||||
{
|
||||
Location = ValidLocation() with { AddressLine1 = "" }
|
||||
};
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Location.AddressLine1)
|
||||
.WithErrorMessage("Address line 1 is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_AddressLine1TooLong_HasErrorForAddressLine1()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with
|
||||
{
|
||||
Location = ValidLocation() with { AddressLine1 = new string('a', 257) }
|
||||
};
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Location.AddressLine1)
|
||||
.WithErrorMessage("Address line 1 cannot exceed 256 characters.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyPostalCode_HasErrorForPostalCode()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with
|
||||
{
|
||||
Location = ValidLocation() with { PostalCode = "" }
|
||||
};
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Location.PostalCode)
|
||||
.WithErrorMessage("Postal code is required.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_PostalCodeTooLong_HasErrorForPostalCode()
|
||||
{
|
||||
CreateBreweryCommand command = ValidCommand() with
|
||||
{
|
||||
Location = ValidLocation() with { PostalCode = new string('1', 21) }
|
||||
};
|
||||
|
||||
TestValidationResult<CreateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Location.PostalCode)
|
||||
.WithErrorMessage("Postal code cannot exceed 20 characters.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Features.Breweries.Commands.UpdateBrewery;
|
||||
using FluentValidation.TestHelper;
|
||||
|
||||
namespace Features.Breweries.Tests.Commands;
|
||||
|
||||
public class UpdateBreweryValidatorTests
|
||||
{
|
||||
private readonly UpdateBreweryValidator _validator = new();
|
||||
|
||||
private static UpdateBreweryCommand ValidCommand() =>
|
||||
new(Guid.NewGuid(), Guid.NewGuid(), [0x01, 0x02], "Name", "Description", null);
|
||||
|
||||
[Fact]
|
||||
public void Validate_ValidCommand_HasNoValidationErrors()
|
||||
{
|
||||
TestValidationResult<UpdateBreweryCommand> result = _validator.TestValidate(ValidCommand());
|
||||
|
||||
result.ShouldNotHaveAnyValidationErrors();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyRowVersion_HasErrorForRowVersion()
|
||||
{
|
||||
UpdateBreweryCommand command = ValidCommand() with { RowVersion = [] };
|
||||
|
||||
TestValidationResult<UpdateBreweryCommand> result = _validator.TestValidate(command);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.RowVersion)
|
||||
.WithErrorMessage("RowVersion is required to detect conflicting concurrent updates.");
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"/>
|
||||
<PackageReference Include="Moq" Version="4.20.72"/>
|
||||
<PackageReference Include="FluentAssertions" Version="7.2.0"/>
|
||||
<PackageReference Include="FluentValidation" Version="11.3.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using Features.Breweries.Queries.GetBreweryLocationsWithinRange;
|
||||
using FluentValidation.TestHelper;
|
||||
|
||||
namespace Features.Breweries.Tests.Queries;
|
||||
|
||||
public class GetBreweryLocationsWithinRangeValidatorTests
|
||||
{
|
||||
private readonly GetBreweryLocationsWithinRangeValidator _validator = new();
|
||||
|
||||
private static GetBreweryLocationsWithinRangeQuery ValidQuery() => new(45, -73, 1000);
|
||||
|
||||
[Fact]
|
||||
public void Validate_ValidQuery_HasNoValidationErrors()
|
||||
{
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
ValidQuery()
|
||||
);
|
||||
|
||||
result.ShouldNotHaveAnyValidationErrors();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-90)]
|
||||
[InlineData(90)]
|
||||
public void Validate_LatitudeAtBoundary_HasNoValidationErrorForLatitude(double latitude)
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { Latitude = latitude };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result.ShouldNotHaveValidationErrorFor(x => x.Latitude);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(90.0001)]
|
||||
[InlineData(-90.0001)]
|
||||
public void Validate_LatitudeOutOfRange_HasErrorForLatitude(double latitude)
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { Latitude = latitude };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Latitude)
|
||||
.WithErrorMessage("Latitude must be between -90 and 90 degrees.");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-180)]
|
||||
[InlineData(180)]
|
||||
public void Validate_LongitudeAtBoundary_HasNoValidationErrorForLongitude(double longitude)
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { Longitude = longitude };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result.ShouldNotHaveValidationErrorFor(x => x.Longitude);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(180.0001)]
|
||||
[InlineData(-180.0001)]
|
||||
public void Validate_LongitudeOutOfRange_HasErrorForLongitude(double longitude)
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { Longitude = longitude };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.Longitude)
|
||||
.WithErrorMessage("Longitude must be between -180 and 180 degrees.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_RangeInMetresIsZero_HasErrorForRangeInMetres()
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { RangeInMetres = 0 };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.RangeInMetres)
|
||||
.WithErrorMessage("RangeInMetres must be greater than zero.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_RangeInMetresIsNegative_HasErrorForRangeInMetres()
|
||||
{
|
||||
GetBreweryLocationsWithinRangeQuery query = ValidQuery() with { RangeInMetres = -1 };
|
||||
|
||||
TestValidationResult<GetBreweryLocationsWithinRangeQuery> result = _validator.TestValidate(
|
||||
query
|
||||
);
|
||||
|
||||
result
|
||||
.ShouldHaveValidationErrorFor(x => x.RangeInMetres)
|
||||
.WithErrorMessage("RangeInMetres must be greater than zero.");
|
||||
}
|
||||
}
|
||||
@@ -25,18 +25,23 @@ public class CreateBreweryValidator : AbstractValidator<CreateBreweryCommand>
|
||||
|
||||
RuleFor(x => x.Location).NotNull().WithMessage("Location is required.");
|
||||
|
||||
RuleFor(x => x.Location.CityId).NotEmpty().WithMessage("CityId is required.");
|
||||
RuleFor(x => x.Location.CityId)
|
||||
.NotEmpty()
|
||||
.WithMessage("CityId is required.")
|
||||
.When(x => x.Location != null);
|
||||
|
||||
RuleFor(x => x.Location.AddressLine1)
|
||||
.NotEmpty()
|
||||
.WithMessage("Address line 1 is required.")
|
||||
.MaximumLength(256)
|
||||
.WithMessage("Address line 1 cannot exceed 256 characters.");
|
||||
.WithMessage("Address line 1 cannot exceed 256 characters.")
|
||||
.When(x => x.Location != null);
|
||||
|
||||
RuleFor(x => x.Location.PostalCode)
|
||||
.NotEmpty()
|
||||
.WithMessage("Postal code is required.")
|
||||
.MaximumLength(20)
|
||||
.WithMessage("Postal code cannot exceed 20 characters.");
|
||||
.WithMessage("Postal code cannot exceed 20 characters.")
|
||||
.When(x => x.Location != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="FluentAssertions" Version="7.2.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user