326 lines
No EOL
12 KiB
C#
326 lines
No EOL
12 KiB
C#
// Copyright (c) The Geekeey Authors
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Geekeey.Request.Validation.Tests;
|
|
|
|
internal sealed class RuleBuilderExtensionsTests
|
|
{
|
|
[Test]
|
|
public async Task I_can_validate_not_null_for_reference_types()
|
|
{
|
|
var validator = new PropertyValidator<ReferenceValueModel, object?>(model =>
|
|
model.Value, rule => rule.NotNull());
|
|
|
|
var invalid = validator.Validate(new ReferenceValueModel());
|
|
var valid = validator.Validate(new ReferenceValueModel { Value = new object() });
|
|
|
|
await AssertSingleProblem(invalid, nameof(ReferenceValueModel.Value), "Value is required.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_not_null_for_nullable_value_types()
|
|
{
|
|
var validator = new PropertyValidator<NullableIntValueModel, int?>(model =>
|
|
model.Value, rule => rule.NotNull());
|
|
|
|
var invalid = validator.Validate(new NullableIntValueModel());
|
|
var valid = validator.Validate(new NullableIntValueModel { Value = 1 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(NullableIntValueModel.Value), "Value is required.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_not_empty_for_strings()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model =>
|
|
model.Value, rule => rule.NotEmpty());
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = " " });
|
|
var valid = validator.Validate(new StringValueModel { Value = "abc" });
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value is required.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_not_empty_for_collections()
|
|
{
|
|
var validator = new PropertyValidator<CollectionValueModel, IEnumerable<string>?>(model =>
|
|
model.Value, rule => rule.NotEmpty());
|
|
|
|
var invalid = validator.Validate(new CollectionValueModel { Value = [] });
|
|
var valid = validator.Validate(new CollectionValueModel { Value = ["item"] });
|
|
|
|
await AssertSingleProblem(invalid, nameof(CollectionValueModel.Value), "Value is required.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_min_length()
|
|
{
|
|
var validator =
|
|
new PropertyValidator<StringValueModel, string?>(model =>
|
|
model.Value, rule => rule.MinLength(3));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "ab" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "abc" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be at least 3 characters long.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_max_length()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.MaxLength(5));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "abcdef" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "abcde" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be at most 5 characters long.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_length()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Length(2, 4));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "a" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ab" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value),
|
|
"Value must be between 2 and 4 characters long.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_greater_than()
|
|
{
|
|
var validator = new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.GreaterThan(18));
|
|
|
|
var invalid = validator.Validate(new IntValueModel { Value = 18 });
|
|
var valid = validator.Validate(new IntValueModel { Value = 19 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(IntValueModel.Value), "Value must be greater than 18.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_greater_than_or_equal_to()
|
|
{
|
|
var validator = new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.GreaterThanOrEqualTo(1));
|
|
|
|
var invalid = validator.Validate(new IntValueModel { Value = 0 });
|
|
var valid = validator.Validate(new IntValueModel { Value = 1 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(IntValueModel.Value), "Value must be greater than or equal to 1.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_less_than()
|
|
{
|
|
var validator = new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.LessThan(10));
|
|
|
|
var invalid = validator.Validate(new IntValueModel { Value = 10 });
|
|
var valid = validator.Validate(new IntValueModel { Value = 9 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(IntValueModel.Value), "Value must be less than 10.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_less_than_or_equal_to()
|
|
{
|
|
var validator = new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.LessThanOrEqualTo(5));
|
|
|
|
var invalid = validator.Validate(new IntValueModel { Value = 6 });
|
|
var valid = validator.Validate(new IntValueModel { Value = 5 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(IntValueModel.Value), "Value must be less than or equal to 5.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_between()
|
|
{
|
|
var validator = new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.Between(0, 100));
|
|
|
|
var invalid = validator.Validate(new IntValueModel { Value = 101 });
|
|
var valid = validator.Validate(new IntValueModel { Value = 100 });
|
|
|
|
await AssertSingleProblem(invalid, nameof(IntValueModel.Value), "Value must be between 0 and 100.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_equal()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Equal("ACTIVE"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "INACTIVE" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ACTIVE" });
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be equal to ACTIVE.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_not_equal()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.NotEqual("BANNED"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "BANNED" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ALLOWED" });
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must not be equal to BANNED.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_equal_with_null_reference_values()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Equal("ACTIVE"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "INACTIVE" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ACTIVE" });
|
|
var nullValue = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be equal to ACTIVE.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(nullValue.IsValid).IsFalse();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_not_equal_with_null_reference_values()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.NotEqual("BANNED"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "BANNED" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ALLOWED" });
|
|
var nullValue = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must not be equal to BANNED.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(nullValue.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_skip_null_values_for_reference_greater_than()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.GreaterThan("B"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "A" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "C" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be greater than B.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_skip_null_values_for_reference_between()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Between("A", "C"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "D" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "C" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value must be between A and C.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_matches_with_a_pattern()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Matches("^[A-Z]+$"));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "Abc" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "ABC" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value is not in the correct format.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_can_validate_matches_with_a_regex()
|
|
{
|
|
var validator = new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Matches(new Regex(@"^\d{4}$", RegexOptions.CultureInvariant)));
|
|
|
|
var invalid = validator.Validate(new StringValueModel { Value = "12AB" });
|
|
var valid = validator.Validate(new StringValueModel { Value = "1234" });
|
|
var ignoredNull = validator.Validate(new StringValueModel());
|
|
|
|
await AssertSingleProblem(invalid, nameof(StringValueModel.Value), "Value is not in the correct format.");
|
|
await Assert.That(valid.IsValid).IsTrue();
|
|
await Assert.That(ignoredNull.IsValid).IsTrue();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_see_it_throw_for_invalid_length_configuration()
|
|
{
|
|
await Assert.That(() => new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.Length(5, 4)))
|
|
.Throws<ArgumentOutOfRangeException>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_see_it_throw_for_negative_min_length_configuration()
|
|
{
|
|
await Assert.That(() => new PropertyValidator<StringValueModel, string?>(model
|
|
=> model.Value, rule => rule.MinLength(-1)))
|
|
.Throws<ArgumentOutOfRangeException>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task I_see_it_throw_for_invalid_between_configuration()
|
|
{
|
|
await Assert.That(() => new PropertyValidator<IntValueModel, int>(model
|
|
=> model.Value, rule => rule.Between(10, 0)))
|
|
.Throws<ArgumentOutOfRangeException>();
|
|
}
|
|
|
|
private static async Task AssertSingleProblem(Validation validation, string propertyPath, string message)
|
|
{
|
|
await Assert.That(validation.Problems).Count().IsEqualTo(1);
|
|
|
|
var problem = validation.Problems.Single();
|
|
|
|
using (Assert.Multiple())
|
|
{
|
|
await Assert.That(problem.PropertyPath).IsEqualTo(propertyPath);
|
|
await Assert.That(problem.Message).IsEqualTo(message);
|
|
}
|
|
}
|
|
} |