fix: reject inverted range when comparer returns negative other than -1
RangeValidator's constructor guarded against an inverted range with
comparer.Compare(to, from) == -1. The IComparer<T>/IComparable<T> contract
only guarantees the sign of the result, not the magnitude, so the guard
silently failed for comparers returning other negatives, e.g. char.CompareTo
('a'.CompareTo('c') == -2) or custom comparers that return x - y. This let
InclusiveBetween('c', 'a') build a validator that can never pass instead of
throwing, unlike the int case which happens to return -1.
Compare against < 0 to honor the comparer contract. Add char-bound throw
tests to the inclusive and exclusive between suites.
This commit is contained in:
1 parent
d49580c67a
commit
81309656cb
3 files changed
+19
-1
No files matched your search
@@ -127,6 +127,13 @@ public class ExclusiveBetweenValidatorTests {
|
|||||||
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateExclusiveBetween<Person, string>("ccc", "aaa"));
|
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateExclusiveBetween<Person, string>("ccc", "aaa"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_chars() {
|
||||||
|
// char.CompareTo returns the arithmetic difference (for example 'a'.CompareTo('c') == -2),
|
||||||
|
// so the range guard must treat any negative result as "less than", not only -1.
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateExclusiveBetween<Person, char>('c', 'a'));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_the_validator_fails_the_error_message_should_be_set_for_strings() {
|
public void When_the_validator_fails_the_error_message_should_be_set_for_strings() {
|
||||||
var validator = new TestValidator(v => v.RuleFor(x => x.Surname).ExclusiveBetween("bbb", "zzz"));
|
var validator = new TestValidator(v => v.RuleFor(x => x.Surname).ExclusiveBetween("bbb", "zzz"));
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ public class InclusiveBetweenValidatorTests {
|
|||||||
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateInclusiveBetween<Person, string>("ccc", "aaa"));
|
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateInclusiveBetween<Person, string>("ccc", "aaa"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_chars() {
|
||||||
|
// char.CompareTo returns the arithmetic difference (for example 'a'.CompareTo('c') == -2),
|
||||||
|
// so the range guard must treat any negative result as "less than", not only -1.
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => RangeValidatorFactory.CreateInclusiveBetween<Person, char>('c', 'a'));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_the_validator_fails_the_error_message_should_be_set_for_strings() {
|
public void When_the_validator_fails_the_error_message_should_be_set_for_strings() {
|
||||||
var validator = new TestValidator(v => v.RuleFor(x => x.Surname).InclusiveBetween("bbb", "zzz"));
|
var validator = new TestValidator(v => v.RuleFor(x => x.Surname).InclusiveBetween("bbb", "zzz"));
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ public abstract class RangeValidator<T, TProperty> : PropertyValidator<T, TPrope
|
|||||||
|
|
||||||
_explicitComparer = comparer;
|
_explicitComparer = comparer;
|
||||||
|
|
||||||
if (comparer.Compare(to, from) == -1) {
|
// IComparer<T>.Compare only guarantees the sign of the result, not a specific
|
||||||
|
// magnitude, so any negative value means "to" is less than "from". Comparing against
|
||||||
|
// -1 misses comparers that return other negatives (for example char.CompareTo, which
|
||||||
|
// returns the arithmetic difference, or custom comparers).
|
||||||
|
if (comparer.Compare(to, from) < 0) {
|
||||||
throw new ArgumentOutOfRangeException(nameof(to), "To should be larger than from.");
|
throw new ArgumentOutOfRangeException(nameof(to), "To should be larger than from.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user