|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Xml;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Moq;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
public class DefaultModelMetadataTest
{
[Fact]
public void DefaultValues()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues);
Assert.Equal(typeof(string), metadata.ModelType);
Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat);
Assert.False(metadata.HideSurroundingHtml);
Assert.True(metadata.HtmlEncode);
Assert.True(metadata.IsBindingAllowed);
Assert.False(metadata.IsBindingRequired);
Assert.False(metadata.IsCollectionType);
Assert.False(metadata.IsComplexType);
Assert.False(metadata.IsEnumerableType);
Assert.False(metadata.IsEnum);
Assert.False(metadata.IsFlagsEnum);
Assert.False(metadata.IsNullableValueType);
Assert.False(metadata.IsReadOnly);
Assert.False(metadata.IsRequired); // Defaults to false for reference types
Assert.True(metadata.ShowForDisplay);
Assert.True(metadata.ShowForEdit);
Assert.False(metadata.ValidateChildren); // Defaults to true for complex and enumerable types.
Assert.Null(metadata.DataTypeName);
Assert.Null(metadata.Description);
Assert.Null(metadata.DisplayFormatString);
Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.ElementMetadata);
Assert.Null(metadata.EnumGroupedDisplayNamesAndValues);
Assert.Null(metadata.EnumNamesAndValues);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.PropertyValidationFilter);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Null(metadata.Placeholder);
Assert.Null(metadata.TemplateHint);
Assert.Equal(10000, ModelMetadata.DefaultOrder);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
Assert.Null(metadata.BinderModelName);
Assert.Null(metadata.BinderType);
Assert.Null(metadata.BindingSource);
Assert.Null(metadata.PropertyFilterProvider);
}
[Fact]
public void CreateMetadataForType()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(Exception));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.Equal(typeof(Exception), metadata.ModelType);
Assert.Null(metadata.PropertyName);
Assert.Null(metadata.ContainerType);
}
[Fact]
public void CreateMetadataForProperty()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(typeof(Exception).GetProperty(nameof(Exception.Message)), typeof(string), typeof(Exception));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], new object[0], null));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("Message", metadata.PropertyName);
Assert.Equal(typeof(Exception), metadata.ContainerType);
}
[Fact]
public void DisplayFormatString_DoesNotCacheInitialDelegateValue()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
var displayFormat = "initial format";
var cache = new DefaultMetadataDetails(key, attributes)
{
DisplayMetadata = new DisplayMetadata
{
DisplayFormatStringProvider = () => displayFormat,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
foreach (var newFormat in new[] { "one", "two", "three" })
{
// Arrange n
displayFormat = newFormat;
// Act n
var result = metadata.DisplayFormatString;
// Assert n
Assert.Equal(newFormat, result);
}
}
[Fact]
public void EditFormatString_DoesNotCacheInitialDelegateValue()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
var editFormat = "initial format";
var cache = new DefaultMetadataDetails(key, attributes)
{
DisplayMetadata = new DisplayMetadata
{
EditFormatStringProvider = () => editFormat,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
foreach (var newFormat in new[] { "one", "two", "three" })
{
// Arrange n
editFormat = newFormat;
// Act n
var result = metadata.EditFormatString;
// Assert n
Assert.Equal(newFormat, result);
}
}
[Fact]
public void NullDisplayText_DoesNotCacheInitialDelegateValue()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
var nullDisplay = "initial display text";
var cache = new DefaultMetadataDetails(key, attributes)
{
DisplayMetadata = new DisplayMetadata
{
NullDisplayTextProvider = () => nullDisplay,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
foreach (var newDisplay in new[] { "one", "two", "three" })
{
// Arrange n
nullDisplay = newDisplay;
// Act n
var result = metadata.NullDisplayText;
// Assert n
Assert.Equal(newDisplay, result);
}
}
[Theory]
[InlineData(typeof(object))]
[InlineData(typeof(int))]
[InlineData(typeof(NonCollectionType))]
[InlineData(typeof(string))]
public void ElementMetadata_ReturnsNull_ForNonCollections(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var elementMetadata = metadata.ElementMetadata;
// Assert
Assert.Null(elementMetadata);
}
[Theory]
[InlineData(typeof(int[]), typeof(int))]
[InlineData(typeof(List<string>), typeof(string))]
[InlineData(typeof(DerivedList), typeof(int))]
[InlineData(typeof(IEnumerable), typeof(object))]
[InlineData(typeof(IEnumerable<string>), typeof(string))]
[InlineData(typeof(Collection<int>), typeof(int))]
[InlineData(typeof(Dictionary<object, object>), typeof(KeyValuePair<object, object>))]
[InlineData(typeof(DerivedDictionary), typeof(KeyValuePair<string, int>))]
public void ElementMetadata_ReturnsExpectedMetadata(Type modelType, Type elementType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var elementMetadata = metadata.ElementMetadata;
// Assert
Assert.NotNull(elementMetadata);
Assert.Equal(elementType, elementMetadata.ModelType);
}
private class NonCollectionType
{
}
private class DerivedList : List<int>
{
}
private class DerivedDictionary : Dictionary<string, int>
{
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(int))]
public void IsBindingAllowed_ReturnsTrue_ForTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
BindingMetadata = new BindingMetadata()
{
IsBindingAllowed = false, // Will be ignored.
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isBindingAllowed = metadata.IsBindingAllowed;
// Assert
Assert.True(isBindingAllowed);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(int))]
public void IsBindingRequired_ReturnsFalse_ForTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
BindingMetadata = new BindingMetadata()
{
IsBindingRequired = true, // Will be ignored.
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isBindingRequired = metadata.IsBindingRequired;
// Assert
Assert.False(isBindingRequired);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(int?))]
[InlineData(typeof(Guid))]
[InlineData(typeof(DateTime))]
[InlineData(typeof(DateTime?))]
[InlineData(typeof(System.Net.IPEndPoint))]
[InlineData(typeof(TypeWithTryParse))]
[InlineData(typeof(TypeWithTryParseAndIFormatProvider))]
public void IsParseableType_ReturnsTrue_ForParseableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(Array.Empty<object>(), null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isParseableType = metadata.IsParseableType;
// Assert
Assert.True(isParseableType);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(object))]
[InlineData(typeof(TypeWithInvalidTryParse))]
[InlineData(typeof(TypeWithProperties))]
public void IsParseableType_ReturnsFalse_ForNonParseableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(Array.Empty<object>(), null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isParseableType = metadata.IsParseableType;
// Assert
Assert.False(isParseableType);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(int))]
public void IsParseableType_ReturnsFalse_ForByRefTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType.MakeByRefType());
var cache = new DefaultMetadataDetails(key, new ModelAttributes(Array.Empty<object>(), null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isParseableType = metadata.IsParseableType;
// Assert
Assert.False(isParseableType);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(IDisposable))]
[InlineData(typeof(Nullable<int>))]
public void IsRequired_ReturnsFalse_ForNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.False(isRequired);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(DayOfWeek))]
public void IsRequired_ReturnsTrue_ForNonNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.True(isRequired);
}
[Fact]
public void PropertiesProperty_CallsProvider()
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var prop1 = typeof(Exception).GetProperty(nameof(Exception.Message));
var prop2 = typeof(Exception).GetProperty(nameof(Exception.StackTrace));
var expectedProperties = new DefaultModelMetadata[]
{
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(prop1, typeof(int), typeof(string)),
attributes: new ModelAttributes(new object[0], new object[0], null))),
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(prop2, typeof(int), typeof(string)),
attributes: new ModelAttributes(new object[0], new object[0], null))),
};
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedProperties.Length, properties.Count);
for (var i = 0; i < expectedProperties.Length; i++)
{
Assert.Same(expectedProperties[i], properties[i]);
}
}
// Input (original) property names and expected (ordered) property names.
public static TheoryData<IEnumerable<string>, IEnumerable<string>> PropertyNamesTheoryData
{
get
{
// ModelMetadata does not reorder properties the provider returns without an Order override.
return new TheoryData<IEnumerable<string>, IEnumerable<string>>
{
{
new List<string> { "Property1", "Property2", "Property3", "Property4", },
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
{
new List<string> { "Property4", "Property3", "Property2", "Property1", },
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", },
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", }
},
{
new List<string> { "John", "Jonathan", "Jon", "Joan", },
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesTheoryData))]
public void PropertiesProperty_WithDefaultOrder_OrdersPropertyNamesAsProvided(
IEnumerable<string> originalNames,
IEnumerable<string> expectedNames)
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var expectedProperties = new List<DefaultModelMetadata>();
foreach (var originalName in originalNames)
{
expectedProperties.Add(new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
#pragma warning disable CS0618 // Using the obsolete overload does not affect the intent of this test, but fixing it requires a lot of code churn.
ModelMetadataIdentity.ForProperty(typeof(int), originalName, typeof(string)),
#pragma warning restore CS0618 // Type or member is obsolete
attributes: new ModelAttributes(new object[0], new object[0], null))));
}
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedNames.Count(), properties.Count);
Assert.Equal(expectedNames.ToArray(), properties.Select(p => p.PropertyName).ToArray());
}
// Input (original) property names, Order values, and expected (ordered) property names.
public static TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
PropertyNamesAndOrdersTheoryData
{
get
{
return new TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
{
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 23),
new KeyValuePair<string, int>("Property2", 23),
new KeyValuePair<string, int>("Property3", 23),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
// Same order if already ordered using Order.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property4", 23),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property1", 26),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
// Rest of the orderings get updated within ModelMetadata.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 26),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Alpha", 26),
new KeyValuePair<string, int>("Bravo", 24),
new KeyValuePair<string, int>("Charlie", 23),
new KeyValuePair<string, int>("Delta", 25),
},
new List<string> { "Charlie", "Bravo", "Delta", "Alpha", }
},
// Jonathan and Jon will not be reordered.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Joan", 1),
new KeyValuePair<string, int>("Jonathan", 0),
new KeyValuePair<string, int>("Jon", 0),
new KeyValuePair<string, int>("John", -1),
},
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesAndOrdersTheoryData))]
public void PropertiesProperty_OrdersPropertyNamesUsingOrder_ThenAsProvided(
IEnumerable<KeyValuePair<string, int>> originalNamesAndOrders,
IEnumerable<string> expectedNames)
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var expectedProperties = new List<DefaultModelMetadata>();
foreach (var kvp in originalNamesAndOrders)
{
var propertyCache = new DefaultMetadataDetails(
#pragma warning disable CS0618 // Using the obsolete overload does not affect the intent of this test, but fixing it requires a lot of code churn.
ModelMetadataIdentity.ForProperty(typeof(int), kvp.Key, typeof(string)),
#pragma warning restore CS0618 // Type or member is obsolete
attributes: new ModelAttributes(new object[0], new object[0], null))
{
DisplayMetadata = new DisplayMetadata(),
};
propertyCache.DisplayMetadata.Order = kvp.Value;
expectedProperties.Add(new DefaultModelMetadata(
provider.Object,
detailsProvider,
propertyCache));
}
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedNames.Count(), properties.Count);
Assert.Equal(expectedNames.ToArray(), properties.Select(p => p.PropertyName).ToArray());
}
[Fact]
public void PropertiesSetOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var SinglePropertiesEvaluation = metadata.Properties;
var secondPropertiesEvaluation = metadata.Properties;
// Assert
// Same IEnumerable<ModelMetadata> object.
Assert.Same(SinglePropertiesEvaluation, secondPropertiesEvaluation);
}
[Fact]
public void PropertiesEnumerationEvaluatedOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var SinglePropertiesEvaluation = metadata.Properties.ToList();
var secondPropertiesEvaluation = metadata.Properties.ToList();
// Assert
// Identical ModelMetadata objects every time we run through the Properties collection.
Assert.Equal(SinglePropertiesEvaluation, secondPropertiesEvaluation);
}
[Fact]
public void IsReadOnly_ReturnsFalse_ForType()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(int[]));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
BindingMetadata = new BindingMetadata()
{
IsReadOnly = true, // Will be ignored.
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isReadOnly = metadata.IsReadOnly;
// Assert
Assert.False(isReadOnly);
}
[Fact]
public void IsReadOnly_ReturnsTrue_ForPrivateSetProperty()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isReadOnly = metadata.Properties["PublicGetPrivateSetProperty"].IsReadOnly;
// Assert
Assert.True(isReadOnly);
}
[Fact]
public void IsReadOnly_ReturnsTrue_ForProtectedSetProperty()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isReadOnly = metadata.Properties["PublicGetProtectedSetProperty"].IsReadOnly;
// Assert
Assert.True(isReadOnly);
}
[Fact]
public void IsReadOnly_ReturnsFalse_ForPublicSetProperty()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isReadOnly = metadata.Properties["PublicGetPublicSetProperty"].IsReadOnly;
// Assert
Assert.False(isReadOnly);
}
[Theory]
[InlineData(typeof(int))] // Primitive
[InlineData(typeof(int?))] // Nullable
[InlineData(typeof(Guid))] // TypeConverter
[InlineData(typeof(Guid?))] // Nullable + TypeConverter
[InlineData(typeof(string))]
public void ValidateChildren_SimpleTypes(Type modelType)
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.False(validateChildren);
}
[Theory]
[InlineData(typeof(int[]))]
[InlineData(typeof(List<decimal>))]
[InlineData(typeof(IEnumerable))]
[InlineData(typeof(Dictionary<string, string>))]
[InlineData(typeof(KeyValuePair<string, string>))]
[InlineData(typeof(KeyValuePair<string, string>?))]
[InlineData(typeof(TypeWithProperties))]
[InlineData(typeof(List<TypeWithProperties>))]
public void ValidateChildren_ComplexAndEnumerableTypes(Type modelType)
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.True(validateChildren);
}
public static TheoryData<IPropertyValidationFilter> ValidationFilterData
{
get
{
return new TheoryData<IPropertyValidationFilter>
{
null,
new ValidateNeverAttribute(),
};
}
}
[Theory]
[MemberData(nameof(ValidationFilterData))]
public void PropertyValidationFilter_ReflectsFilter_FromValidationMetadata(IPropertyValidationFilter value)
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(int));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
ValidationMetadata = new ValidationMetadata
{
PropertyValidationFilter = value,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validationFilter = metadata.PropertyValidationFilter;
// Assert
Assert.Same(value, validationFilter);
}
[Fact]
public void ValidateChildren_OverrideTrue()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(int));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
ValidationMetadata = new ValidationMetadata()
{
ValidateChildren = true,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.True(validateChildren);
}
[Fact]
public void ValidateChildren_OverrideFalse()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(XmlDocument));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null))
{
ValidationMetadata = new ValidationMetadata()
{
ValidateChildren = false,
},
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.False(validateChildren);
}
[Fact]
public void GetMetadataForType_CallsProvider()
{
// Arrange
var detailsProvider = new Mock<ICompositeMetadataDetailsProvider>();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(mp => mp.GetMetadataForType(typeof(string)))
.Verifiable();
var metadata1 = new DefaultModelMetadata(metadataProvider.Object, detailsProvider.Object, cache);
// Act
var metadata2 = metadata1.GetMetadataForType(typeof(string));
// Assert
metadataProvider.VerifyAll();
}
[Fact]
public void GetMetadataForProperties_CallsProvider()
{
// Arrange
var detailsProvider = new Mock<ICompositeMetadataDetailsProvider>();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(typeof(Exception)))
.Verifiable();
var metadata1 = new DefaultModelMetadata(metadataProvider.Object, detailsProvider.Object, cache);
// Act
var metadata2 = metadata1.GetMetadataForProperties(typeof(Exception));
// Assert
metadataProvider.VerifyAll();
}
[Fact]
public void CalculateHasValidators_ParameterMetadata_TypeHasNoValidators()
{
// Arrange
var parameter = GetType()
.GetMethod(nameof(CalculateHasValidators_ParameterMetadata_TypeHasNoValidatorsMethod), BindingFlags.Static | BindingFlags.NonPublic)
.GetParameters()[0];
var modelIdentity = ModelMetadataIdentity.ForParameter(parameter);
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), hasValidators: false);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
private static void CalculateHasValidators_ParameterMetadata_TypeHasNoValidatorsMethod(string model) { }
[Fact]
public void CalculateHasValidators_PropertyMetadata_TypeHasNoValidators()
{
// Arrange
var property = GetType()
.GetProperty(nameof(CalculateHasValidators_PropertyMetadata_TypeHasNoValidatorsProperty), BindingFlags.Static | BindingFlags.NonPublic);
var modelIdentity = ModelMetadataIdentity.ForProperty(property, property.PropertyType, GetType());
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), hasValidators: false);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
private static int CalculateHasValidators_PropertyMetadata_TypeHasNoValidatorsProperty { get; set; }
[Fact]
public void CalculateHasValidators_TypeWithoutProperties_TypeHasNoValidators()
{
// Arrange
var modelIdentity = ModelMetadataIdentity.ForType(typeof(string));
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), hasValidators: false);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
[Fact]
public void CalculateHasValidators_SimpleType_TypeHasValidators()
{
// Arrange
var modelIdentity = ModelMetadataIdentity.ForType(typeof(string));
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), hasValidators: true);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_ReturnsTrue_SimpleType_TypeHasNonDeterministicValidators()
{
// Arrange
var modelIdentity = ModelMetadataIdentity.ForType(typeof(string));
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), hasValidators: null);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_TypeWithProperties_PropertyIsNotDefaultModelMetadata()
{
// Arrange
var modelType = typeof(TypeWithProperties);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var property = typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty));
var propertyIdentity = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(TypeWithProperties));
var propertyMetadata = new Mock<ModelMetadata>(propertyIdentity);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata.Object, })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_TypeWithProperties_HasValidatorForAnyPropertyIsTrue()
{
// Arrange
var modelType = typeof(TypeWithProperties);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var property1Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var property1Metadata = CreateModelMetadata(property1Identity, metadataProvider.Object, hasValidators: false);
var property2Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetProtectedSetProperty)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Identity, metadataProvider.Object, hasValidators: true);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { property1Metadata, property2Metadata })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_TypeWithProperties_HasValidatorsForPropertyIsNotDeterminstic()
{
// Arrange
var modelType = typeof(TypeWithProperties);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var propertyIdentity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyIdentity, metadataProvider.Object, hasValidators: null);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata, })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_TypeWithProperties_HasValidatorForAllPropertiesIsFalse()
{
// Arrange
var modelType = typeof(TypeWithProperties);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var property1Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var property1Metadata = CreateModelMetadata(property1Identity, metadataProvider.Object, hasValidators: false);
var property2Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetProtectedSetProperty)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Identity, metadataProvider.Object, hasValidators: false);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { property1Metadata, property2Metadata })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
[Fact]
public void CalculateHasValidators_SelfReferencingType_HasValidatorOnNestedProperty()
{
// Arrange
var modelType = typeof(Employee);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, hasValidators: false);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, hasValidators: false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, hasValidators: false);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, hasValidators: false);
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, hasValidators: true); // BusinessUnit.Id has validators.
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { employeeIdMetadata, employeeUnitMetadata, employeeManagerMetadata, employeeEmployeesMetadata, })
.Verifiable();
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(typeof(BusinessUnit)))
.Returns(new[] { unitHeadMetadata, unitIdMetadata, })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_SelfReferencingType_HasValidatorOnSelfReferencedProperty()
{
// Arrange
var modelType = typeof(Employee);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, hasValidators: false);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, hasValidators: false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, hasValidators: false);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, hasValidators: true); // BusinessUnit.Head has validators
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, hasValidators: false);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { employeeIdMetadata, employeeUnitMetadata, employeeManagerMetadata, employeeEmployeesMetadata, });
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(typeof(BusinessUnit)))
.Returns(new[] { unitHeadMetadata, unitIdMetadata, });
metadataProvider
.Setup(mp => mp.GetMetadataForType(modelType))
.Returns(modelMetadata);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_CollectionElementHasValidators()
{
// Arrange
var modelType = typeof(Employee);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, hasValidators: false);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { employeeIdMetadata, employeeEmployeesMetadata, });
metadataProvider
.Setup(mp => mp.GetMetadataForType(modelType))
.Returns(CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: true)); // Employees.Employee has validators
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_SelfReferencingType_NoValidatorsInGraph()
{
// Arrange
var modelType = typeof(Employee);
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, hasValidators: false);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, hasValidators: false);
var employeeEmployeesId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesIdMetadata = CreateModelMetadata(employeeEmployeesId, metadataProvider.Object, hasValidators: false);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, hasValidators: false);
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, hasValidators: false);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { employeeIdMetadata, employeeUnitMetadata, employeeManagerMetadata, employeeEmployeesIdMetadata, });
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(typeof(BusinessUnit)))
.Returns(new[] { unitHeadMetadata, unitIdMetadata, });
metadataProvider
.Setup(mp => mp.GetMetadataForType(modelType))
.Returns(modelMetadata);
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
private record SimpleRecordType(int Property);
[Fact]
public void CalculateHasValidators_RecordType_ParametersWithNoValidators()
{
// Arrange
var modelType = typeof(SimpleRecordType);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordType.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: false);
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
// Parameter has no validation metadata.
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: false);
var constructorMetadata = CreateModelMetadata(
ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata)
.Verifiable();
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata })
.Verifiable();
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
metadataProvider.Verify();
}
[Fact]
public void CalculateHasValidators_RecordType_ParametersWithValidators()
{
// Arrange
var modelType = typeof(SimpleRecordType);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordType.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: false);
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
// Parameter has some validation metadata.
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: true);
var constructorMetadata = CreateModelMetadata(ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata });
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
private record SimpleRecordTypeWithProperty(int Property)
{
public int Property2 { get; set; }
}
[Fact]
public void CalculateHasValidators_RecordTypeWithProperty_NoValidators()
{
// Arrange
var modelType = typeof(SimpleRecordTypeWithProperty);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: false);
// Property2 has no validators
var property2Id = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property2)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Id, metadataProvider.Object, hasValidators: false);
// Parameter named "Property" has no validators
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: false);
var constructorMetadata = CreateModelMetadata(
ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata, property2Metadata });
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
[Fact]
public void CalculateHasValidators_RecordTypeWithProperty_ParameteryHasValidators()
{
// Arrange
var modelType = typeof(SimpleRecordTypeWithProperty);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: false);
// Property2 has no validators
var property2Id = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property2)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Id, metadataProvider.Object, hasValidators: false);
// Parameter named "Property" has validators
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: true);
var constructorMetadata = CreateModelMetadata(
ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata, property2Metadata });
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_RecordTypeWithProperty_PropertyHasValidators()
{
// Arrange
var modelType = typeof(SimpleRecordTypeWithProperty);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: false);
// Property2 has some validators
var property2Id = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property2)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Id, metadataProvider.Object, hasValidators: true);
// Parameter named "Property" has no validators
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: false);
var constructorMetadata = CreateModelMetadata(
ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata, property2Metadata });
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.True(result);
}
[Fact]
public void CalculateHasValidators_RecordTypeWithProperty_MappedPropertyHasValidators_ValidatorsAreIgnored()
{
// Arrange
var modelType = typeof(SimpleRecordTypeWithProperty);
var constructor = modelType.GetConstructors().Single();
var parameter = constructor.GetParameters().Single();
var modelIdentity = ModelMetadataIdentity.ForType(modelType);
var metadataProvider = new Mock<ModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, hasValidators: false);
modelMetadata.BindingMetadata.BoundConstructor = constructor;
var propertyId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyId, metadataProvider.Object, hasValidators: true);
var property2Id = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(SimpleRecordTypeWithProperty.Property2)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Id, metadataProvider.Object, hasValidators: false);
var parameterId = ModelMetadataIdentity.ForParameter(parameter);
var parameterMetadata = CreateModelMetadata(parameterId, metadataProvider.Object, hasValidators: false);
var constructorMetadata = CreateModelMetadata(
ModelMetadataIdentity.ForConstructor(constructor, modelType), metadataProvider.Object, hasValidators: null);
constructorMetadata.Details.BoundConstructorParameters = new[]
{
parameterMetadata,
};
metadataProvider
.Setup(mp => mp.GetMetadataForConstructor(constructor, modelType))
.Returns(constructorMetadata);
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(modelType))
.Returns(new[] { propertyMetadata, property2Metadata });
// Act
var result = DefaultModelMetadata.CalculateHasValidators(new HashSet<DefaultModelMetadata>(), modelMetadata);
// Assert
Assert.False(result);
}
private static DefaultModelMetadata CreateModelMetadata(
ModelMetadataIdentity modelIdentity,
IModelMetadataProvider metadataProvider,
bool? hasValidators)
{
return new DefaultModelMetadata(
metadataProvider,
new SetHasValidatorsCompositeMetadataDetailsProvider { HasValidators = hasValidators },
new DefaultMetadataDetails(modelIdentity, new ModelAttributes(new object[0], new object[0], new object[0])));
}
private void ActionMethod(string input)
{
}
private class TypeWithProperties
{
public string PublicGetPrivateSetProperty { get; private set; }
public int PublicGetProtectedSetProperty { get; protected set; }
public int PublicGetPublicSetProperty { get; set; }
}
private class TypeWithInvalidTryParse
{
public static bool TryParse(string s)
{
return true;
}
}
private class TypeWithTryParse
{
public static bool TryParse(string s, out TypeWithTryParse result)
{
result = new TypeWithTryParse();
return true;
}
}
private class TypeWithTryParseAndIFormatProvider
{
public static bool TryParse(string s, IFormatProvider provider, out TypeWithTryParseAndIFormatProvider result)
{
result = new TypeWithTryParseAndIFormatProvider();
return true;
}
}
public class Employee
{
public int Id { get; set; }
public BusinessUnit Unit { get; set; }
public Employee Manager { get; set; }
public List<Employee> Employees { get; set; }
}
public class BusinessUnit
{
public Employee Head { get; set; }
public int Id { get; set; }
}
private class SetHasValidatorsCompositeMetadataDetailsProvider : ICompositeMetadataDetailsProvider
{
public bool? HasValidators { get; set; }
public void CreateBindingMetadata(BindingMetadataProviderContext context)
{
}
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
}
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
context.ValidationMetadata.HasValidators = HasValidators;
}
}
}
|