aspnetcore/test/Microsoft.AspNet.Mvc.ModelB.../Metadata/AssociatedMetadataProviderT...

353 lines
13 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
#if ASPNET50
using System.ComponentModel;
#endif
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNet.Testing;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class AssociatedMetadataProviderTest
{
// GetMetadataForProperties
[Fact]
public void GetMetadataForPropertiesCreatesMetadataForAllPropertiesOnModelWithPropertyValues()
{
// Arrange
var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
var provider = new TestableAssociatedMetadataProvider();
// Act
// Call ToList() to force the lazy evaluation to evaluate
provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
// Assert
var local = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "LocalAttributes");
Assert.Equal(typeof(int), local.ModelType);
Assert.True(local.Attributes.Any(a => a is RequiredAttribute));
var metadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MetadataAttributes");
Assert.Equal(typeof(string), metadata.ModelType);
Assert.True(metadata.Attributes.Any(a => a is RangeAttribute));
var mixed = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MixedAttributes");
Assert.Equal(typeof(double), mixed.ModelType);
Assert.True(mixed.Attributes.Any(a => a is RequiredAttribute));
Assert.True(mixed.Attributes.Any(a => a is RangeAttribute));
}
[Fact]
public void GetMetadataForProperties_ExcludesIndexers()
{
// Arrange
var value = "some value";
var model = new ModelWithIndexer { Value = value };
var provider = new TestableAssociatedMetadataProvider();
var modelType = model.GetType();
// Act
provider.GetMetadataForProperties(model, modelType).ToList();
// Assert
Assert.Equal(2, provider.CreateMetadataFromPrototypeLog.Count);
Assert.Equal(value, provider.CreateMetadataFromPrototypeLog[0].Model);
Assert.Null(provider.CreateMetadataFromPrototypeLog[1].Model);
var valueMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == modelType && m.PropertyName == "Value");
Assert.Equal(typeof(string), valueMetadata.ModelType);
Assert.Single(valueMetadata.Attributes.OfType<MinLengthAttribute>());
var testPropertyMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == modelType && m.PropertyName == "TestProperty");
Assert.Equal(typeof(string), testPropertyMetadata.ModelType);
}
[Fact]
public void GetMetadataForPropertyWithNullContainerReturnsMetadataWithNullValuesForProperties()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act
provider.GetMetadataForProperties(null, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate
// Assert
Assert.NotEmpty(provider.CreateMetadataFromPrototypeLog);
foreach (var parms in provider.CreateMetadataFromPrototypeLog)
{
Assert.Null(parms.Model);
}
}
[Fact]
public void GetMetadataForProperty_WithNoBinderMetadata_GetsItFromType()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.Parent));
// Assert
Assert.NotNull(propertyMetadata.BinderMetadata);
Assert.IsType<TestBinderMetadataAttribute>(propertyMetadata.BinderMetadata);
}
#if ASPNET50
[Fact]
public void GetMetadataForParameter_WithNoBinderMetadata_GetsItFromType()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var parameterMetadata = provider.GetMetadataForParameter(null,
typeof(Person).GetMethod("Update"),
"person",
null);
// Assert
Assert.NotNull(parameterMetadata.BinderMetadata);
Assert.IsType<TestBinderMetadataAttribute>(parameterMetadata.BinderMetadata);
}
#endif
public class TestBinderMetadataAttribute : Attribute, IBinderMetadata
{
}
[TestBinderMetadata]
public class Person
{
public Person Parent { get; set; }
public void Update(Person person)
{
}
}
// GetMetadataForProperty
[Fact]
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: null),
"propertyName");
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: String.Empty),
"propertyName");
}
[Fact]
public void GetMetadataForPropertyInvalidPropertyNameThrows()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: "BadPropertyName"),
"propertyName",
"The property System.Object.BadPropertyName could not be found.");
}
[Fact]
public void GetMetadataForPropertyWithLocalAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(int), "LocalAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes");
// Assert
Assert.Same(metadata, result);
var localAttributes = Assert.Single(
provider.CreateMetadataPrototypeLog,
parameters => parameters.PropertyName == "LocalAttributes");
Assert.Single(localAttributes.Attributes, a => a is RequiredAttribute);
}
[Fact]
public void GetMetadataForPropertyWithMetadataAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(string), "MetadataAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes");
// Assert
Assert.Same(metadata, result);
var parmaters = Assert.Single(
provider.CreateMetadataPrototypeLog,
p => p.PropertyName == "MetadataAttributes");
Assert.Single(parmaters.Attributes, a => a is RangeAttribute);
}
[Fact]
public void GetMetadataForPropertyWithMixedAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(double), "MixedAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes");
// Assert
Assert.Same(metadata, result);
var parms = Assert.Single(provider.CreateMetadataPrototypeLog, p => p.PropertyName == "MixedAttributes");
Assert.Single(parms.Attributes, a => a is RequiredAttribute);
Assert.Single(parms.Attributes, a => a is RangeAttribute);
}
// GetMetadataForType
#if ASPNET50 // No ReadOnlyAttribute in K
[Fact]
public void GetMetadataForTypeIncludesAttributesOnType()
{
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(TypeModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForType(null, typeof(TypeModel));
// Assert
Assert.Same(metadata, result);
var parms = Assert.Single(provider.CreateMetadataPrototypeLog, p => p.ModelType == typeof(TypeModel));
Assert.Single(parms.Attributes, a => a is ReadOnlyAttribute);
}
#endif
[Fact]
public void GetMetadataForProperties_SetsContainerAsExpected()
{
// Arrange
var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
var provider = new EmptyModelMetadataProvider();
// Act
var metadata = provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
// Assert
Assert.Equal(3, metadata.Count);
Assert.Same(model, metadata[0].Container);
Assert.Same(model, metadata[1].Container);
Assert.Same(model, metadata[2].Container);
}
// Helpers
private class PropertyModel
{
[Required]
public int LocalAttributes { get; set; }
[Range(10, 100)]
public string MetadataAttributes { get; set; }
[Required]
[Range(10, 100)]
public double MixedAttributes { get; set; }
}
private class BaseType
{
public string TestProperty { get; set; }
}
private class ModelWithIndexer : BaseType
{
public string this[string x]
{
get { return string.Empty; }
set { }
}
[MinLength(4)]
public string Value { get; set; }
}
#if ASPNET50 // No [ReadOnly] in K
[ReadOnly(true)]
private class TypeModel
{
}
#endif
private class TestableAssociatedMetadataProvider : AssociatedMetadataProvider<ModelMetadata>
{
public List<CreateMetadataPrototypeParams> CreateMetadataPrototypeLog = new List<CreateMetadataPrototypeParams>();
public List<CreateMetadataFromPrototypeParams> CreateMetadataFromPrototypeLog = new List<CreateMetadataFromPrototypeParams>();
public ModelMetadata CreateMetadataPrototypeReturnValue = null;
public ModelMetadata CreateMetadataFromPrototypeReturnValue = null;
protected override ModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName)
{
CreateMetadataPrototypeLog.Add(new CreateMetadataPrototypeParams
{
Attributes = attributes,
ContainerType = containerType,
ModelType = modelType,
PropertyName = propertyName
});
return CreateMetadataPrototypeReturnValue;
}
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype, Func<object> modelAccessor)
{
CreateMetadataFromPrototypeLog.Add(new CreateMetadataFromPrototypeParams
{
Prototype = prototype,
Model = modelAccessor == null ? null : modelAccessor()
});
return CreateMetadataFromPrototypeReturnValue;
}
}
private class CreateMetadataPrototypeParams
{
public IEnumerable<Attribute> Attributes { get; set; }
public Type ContainerType { get; set; }
public Type ModelType { get; set; }
public string PropertyName { get; set; }
}
private class CreateMetadataFromPrototypeParams
{
public ModelMetadata Prototype { get; set; }
public object Model { get; set; }
}
}
}