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,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.");
}
}

View File

@@ -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.");
}
}

View File

@@ -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>

View File

@@ -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.");
}
}