Fix issue with server validation from dictionary configuration where floating point values can be be accessed as doubles or ints (#18508)
* Fix issue with server validation from dictionary configuration where floating point values can be be accessed as doubles or ints. * Fixed typo in comment. --------- Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
@@ -21,7 +21,7 @@ public abstract class DictionaryConfigurationValidatorBase
|
||||
return false;
|
||||
}
|
||||
|
||||
if (configuration.TryGetValue(key, out object? obj) && obj is TValue castValue)
|
||||
if (configuration.TryGetValue(key, out object? obj) && TryCastValue(obj, out TValue? castValue))
|
||||
{
|
||||
value = castValue;
|
||||
return true;
|
||||
@@ -30,4 +30,24 @@ public abstract class DictionaryConfigurationValidatorBase
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCastValue<TValue>(object? value, [NotNullWhen(true)] out TValue? castValue)
|
||||
{
|
||||
if (value is TValue valueAsType)
|
||||
{
|
||||
castValue = valueAsType;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special case for floating point numbers - when deserialized these will be integers if whole numbers rather
|
||||
// than double.
|
||||
if (typeof(TValue) == typeof(double) && value is int valueAsInt)
|
||||
{
|
||||
castValue = (TValue)(object)Convert.ToDouble(valueAsInt);
|
||||
return true;
|
||||
}
|
||||
|
||||
castValue = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,25 @@ public class DecimalValueEditorTests
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1.8, true)]
|
||||
[TestCase(2.2, false)]
|
||||
public void Validates_Is_Less_Than_Or_Equal_To_Configured_Max_With_Configured_Whole_Numbers(object value, bool expectedSuccess)
|
||||
{
|
||||
var editor = CreateValueEditor(min: 1, max: 2);
|
||||
var result = editor.Validate(value, false, null, PropertyValidationContext.Empty());
|
||||
if (expectedSuccess)
|
||||
{
|
||||
Assert.IsEmpty(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(1, result.Count());
|
||||
|
||||
var validationResult = result.First();
|
||||
Assert.AreEqual(validationResult.ErrorMessage, "validation_outOfRangeMaximum");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(0.2, 1.4, false)]
|
||||
[TestCase(0.2, 1.5, true)]
|
||||
[TestCase(0.0, 1.4, true)] // A step of zero would trigger a divide by zero error in evaluating. So we always pass validation for zero, as effectively any step value is valid.
|
||||
@@ -165,7 +184,7 @@ public class DecimalValueEditorTests
|
||||
return CreateValueEditor().ToEditor(property.Object);
|
||||
}
|
||||
|
||||
private static DecimalPropertyEditor.DecimalPropertyValueEditor CreateValueEditor(double step = 0.2)
|
||||
private static DecimalPropertyEditor.DecimalPropertyValueEditor CreateValueEditor(double min = 1.1, double max = 1.9, double step = 0.2)
|
||||
{
|
||||
var localizedTextServiceMock = new Mock<ILocalizedTextService>();
|
||||
localizedTextServiceMock.Setup(x => x.Localize(
|
||||
@@ -174,6 +193,37 @@ public class DecimalValueEditorTests
|
||||
It.IsAny<CultureInfo>(),
|
||||
It.IsAny<IDictionary<string, string>>()))
|
||||
.Returns((string key, string alias, CultureInfo culture, IDictionary<string, string> args) => $"{key}_{alias}");
|
||||
|
||||
// When configuration is populated from the deserialized JSON, whole number values are deserialized as integers.
|
||||
// So we want to replicate that in our tests.
|
||||
var configuration = new Dictionary<string, object>();
|
||||
if (min % 1 == 0)
|
||||
{
|
||||
configuration.Add("min", (int)min);
|
||||
}
|
||||
else
|
||||
{
|
||||
configuration.Add("min", min);
|
||||
}
|
||||
|
||||
if (max % 1 == 0)
|
||||
{
|
||||
configuration.Add("max", (int)max);
|
||||
}
|
||||
else
|
||||
{
|
||||
configuration.Add("max", max);
|
||||
}
|
||||
|
||||
if (step % 1 == 0)
|
||||
{
|
||||
configuration.Add("step", (int)step);
|
||||
}
|
||||
else
|
||||
{
|
||||
configuration.Add("step", step);
|
||||
}
|
||||
|
||||
return new DecimalPropertyEditor.DecimalPropertyValueEditor(
|
||||
Mock.Of<IShortStringHelper>(),
|
||||
Mock.Of<IJsonSerializer>(),
|
||||
@@ -181,12 +231,7 @@ public class DecimalValueEditorTests
|
||||
new DataEditorAttribute("alias"),
|
||||
localizedTextServiceMock.Object)
|
||||
{
|
||||
ConfigurationObject = new Dictionary<string, object>
|
||||
{
|
||||
{ "min", 1.1 },
|
||||
{ "max", 1.9 },
|
||||
{ "step", step }
|
||||
}
|
||||
ConfigurationObject = configuration
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user