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

254 lines
10 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;
using System.ComponentModel;
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 = provider.CreateMetadataPrototypeLog.Single(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 = provider.CreateMetadataPrototypeLog.Single(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 = provider.CreateMetadataPrototypeLog.Single(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 GetMetadataForPropertyWithNullContainerReturnsMetadataWithNullValuesForProperties()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act
provider.GetMetadataForProperties(null, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate
// Assert
Assert.True(provider.CreateMetadataFromPrototypeLog.Any());
foreach (var parms in provider.CreateMetadataFromPrototypeLog)
{
Assert.Null(parms.Model);
}
}
// GetMetadataForProperty
[Fact]
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: null),
"propertyName",
"The value cannot be null or empty.");
ExceptionAssert.ThrowsArgument(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: String.Empty),
"propertyName",
"The value cannot be null or empty.");
}
[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);
Assert.True(provider.CreateMetadataPrototypeLog
.Single(parameters => parameters.PropertyName == "LocalAttributes")
.Attributes.Any(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 = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MetadataAttributes");
Assert.True(parmaters.Attributes.Any(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 = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MixedAttributes");
Assert.True(parms.Attributes.Any(a => a is RequiredAttribute));
Assert.True(parms.Attributes.Any(a => a is RangeAttribute));
}
// GetMetadataForType
#if NET45 // No ReadOnlyAttribute in K
[Fact]
public void GetMetadataForTypeIncludesAttributesOnType()
{
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
ModelMetadata metadata = new ModelMetadata(provider, null, null, typeof(TypeModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
ModelMetadata result = provider.GetMetadataForType(null, typeof(TypeModel));
// Assert
Assert.Same(metadata, result);
CreateMetadataPrototypeParams parms = provider.CreateMetadataPrototypeLog.Single(p => p.ModelType == typeof(TypeModel));
Assert.True(parms.Attributes.Any(a => a is ReadOnlyAttribute));
}
#endif
// Helpers
// TODO: This type used System.ComponentModel.MetadataType to separate attribute declaration from property
// declaration. Need to figure out if this is still relevant since the type does not exist in CoreCLR.
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 sealed class RequiredAttribute : Attribute
{
}
private sealed class RangeAttribute : Attribute
{
public RangeAttribute(int min, int max)
{
}
}
private class ModelWithReadOnlyProperty
{
public int ReadOnlyProperty { get; private set; }
}
#if NET45 // 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; }
}
}
}