520 lines
20 KiB
C#
Raw Normal View History

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Http.Metadata;
using System.Web.Http.Metadata.Providers;
using System.Web.Http.Validation.Validators;
using Moq;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Http.Validation.Providers
{
public class DataAnnotationsModelValidatorProviderTest
{
private static DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
private static IEnumerable<ModelValidatorProvider> _noValidatorProviders = Enumerable.Empty<ModelValidatorProvider>();
// Validation attribute adapter registration
private class MyValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
throw new NotImplementedException();
}
}
private class MyValidationAttributeAdapter : DataAnnotationsModelValidator
{
public MyValidationAttributeAdapter(IEnumerable<ModelValidatorProvider> validatorProviders, ValidationAttribute attribute)
: base(validatorProviders, attribute)
{
}
}
private class MyValidationAttributeAdapterBadCtor : ModelValidator
{
public MyValidationAttributeAdapterBadCtor(IEnumerable<ModelValidatorProvider> validatorProviders)
: base(validatorProviders)
{
}
public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
{
throw new NotImplementedException();
}
}
private class MyDefaultValidationAttributeAdapter : DataAnnotationsModelValidator
{
public MyDefaultValidationAttributeAdapter(IEnumerable<ModelValidatorProvider> validatorProviders, ValidationAttribute attribute)
: base(validatorProviders, attribute)
{
}
}
[MyValidation]
private class MyValidatedClass
{
}
[Fact]
public void RegisterAdapter()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
provider.AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>();
// Act
provider.RegisterAdapter(typeof(MyValidationAttribute), typeof(MyValidationAttributeAdapter));
// Assert
var type = provider.AttributeFactories.Keys.Single();
Assert.Equal(typeof(MyValidationAttribute), type);
var factory = provider.AttributeFactories.Values.Single();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(object));
var attribute = new MyValidationAttribute();
var validator = factory(_noValidatorProviders, attribute);
Assert.IsType<MyValidationAttributeAdapter>(validator);
}
[Fact]
public void RegisterAdapterGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Attribute type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterAdapter(null, typeof(MyValidationAttributeAdapter)),
"attributeType");
// Adapter type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterAdapter(typeof(MyValidationAttribute), null),
"adapterType");
// Validation attribute must derive from ValidationAttribute
Assert.ThrowsArgument(
() => provider.RegisterAdapter(typeof(object), typeof(MyValidationAttributeAdapter)),
"attributeType",
"The type Object must derive from ValidationAttribute");
// Adapter must derive from ModelValidator
Assert.ThrowsArgument(
() => provider.RegisterAdapter(typeof(MyValidationAttribute), typeof(object)),
"adapterType",
"The type Object must derive from ModelValidator");
// Adapter must have the expected constructor
Assert.ThrowsArgument(
() => provider.RegisterAdapter(typeof(MyValidationAttribute), typeof(MyValidationAttributeAdapterBadCtor)),
"adapterType",
"The type MyValidationAttributeAdapterBadCtor must have a public constructor which accepts three parameters of types ModelMetadata, IEnumerable<ModelValidatorProvider>, and MyValidationAttribute");
}
[Fact]
public void RegisterAdapterFactory()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
provider.AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>();
DataAnnotationsModelValidationFactory factory = delegate { return null; };
// Act
provider.RegisterAdapterFactory(typeof(MyValidationAttribute), factory);
// Assert
var type = provider.AttributeFactories.Keys.Single();
Assert.Equal(typeof(MyValidationAttribute), type);
Assert.Same(factory, provider.AttributeFactories.Values.Single());
}
[Fact]
public void RegisterAdapterFactoryGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
DataAnnotationsModelValidationFactory factory = (validatorProviders, attribute) => null;
// Attribute type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterAdapterFactory(null, factory),
"attributeType");
// Factory cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterAdapterFactory(typeof(MyValidationAttribute), null),
"factory");
// Validation attribute must derive from ValidationAttribute
Assert.ThrowsArgument(
() => provider.RegisterAdapterFactory(typeof(object), factory),
"attributeType",
"The type Object must derive from ValidationAttribute");
}
[Fact]
public void RegisterDefaultAdapter()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(MyValidatedClass));
provider.RegisterDefaultAdapter(typeof(MyDefaultValidationAttributeAdapter));
// Act
var result = provider.GetValidators(metadata, _noValidatorProviders).Single();
// Assert
Assert.IsType<MyDefaultValidationAttributeAdapter>(result);
}
[Fact]
public void RegisterDefaultAdapterGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Adapter type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterDefaultAdapter(null),
"adapterType");
// Adapter must derive from ModelValidator
Assert.ThrowsArgument(
() => provider.RegisterDefaultAdapter(typeof(object)),
"adapterType",
"The type Object must derive from ModelValidator");
// Adapter must have the expected constructor
Assert.ThrowsArgument(
() => provider.RegisterDefaultAdapter(typeof(MyValidationAttributeAdapterBadCtor)),
"adapterType",
"The type MyValidationAttributeAdapterBadCtor must have a public constructor which accepts three parameters of types ModelMetadata, IEnumerable<ModelValidatorProvider>, and ValidationAttribute");
}
[Fact]
public void RegisterDefaultAdapterFactory()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(MyValidatedClass));
ModelValidator validator = new Mock<ModelValidator>(_noValidatorProviders).Object;
DataAnnotationsModelValidationFactory factory = delegate { return validator; };
provider.RegisterDefaultAdapterFactory(factory);
// Act
var result = provider.GetValidators(metadata, _noValidatorProviders).Single();
// Assert
Assert.Same(validator, result);
}
[Fact]
public void RegisterDefaultAdapterFactoryGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Factory cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterDefaultAdapterFactory(null),
"factory");
}
// IValidatableObject adapter registration
private class MyValidatableAdapter : ModelValidator
{
public MyValidatableAdapter(IEnumerable<ModelValidatorProvider> validatorProviders)
: base(validatorProviders)
{
}
public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
{
throw new NotImplementedException();
}
}
private class MyValidatableAdapterBadCtor : ModelValidator
{
public MyValidatableAdapterBadCtor(IEnumerable<ModelValidatorProvider> validatorProviders, int unused)
: base(validatorProviders)
{
}
public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
{
throw new NotImplementedException();
}
}
private class MyValidatableClass : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
throw new NotImplementedException();
}
}
[Fact]
public void RegisterValidatableObjectAdapter()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
provider.ValidatableFactories = new Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory>();
IValidatableObject validatable = new Mock<IValidatableObject>().Object;
// Act
provider.RegisterValidatableObjectAdapter(validatable.GetType(), typeof(MyValidatableAdapter));
// Assert
var type = provider.ValidatableFactories.Keys.Single();
Assert.Equal(validatable.GetType(), type);
var factory = provider.ValidatableFactories.Values.Single();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(object));
var validator = factory(_noValidatorProviders);
Assert.IsType<MyValidatableAdapter>(validator);
}
[Fact]
public void RegisterValidatableObjectAdapterGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Attribute type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterValidatableObjectAdapter(null, typeof(MyValidatableAdapter)),
"modelType");
// Adapter type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterValidatableObjectAdapter(typeof(MyValidatableClass), null),
"adapterType");
// Validation attribute must derive from ValidationAttribute
Assert.ThrowsArgument(
() => provider.RegisterValidatableObjectAdapter(typeof(object), typeof(MyValidatableAdapter)),
"modelType",
"The type Object must derive from IValidatableObject.");
// Adapter must derive from ModelValidator
Assert.ThrowsArgument(
() => provider.RegisterValidatableObjectAdapter(typeof(MyValidatableClass), typeof(object)),
"adapterType",
"The type Object must derive from ModelValidator");
// Adapter must have the expected constructor
Assert.ThrowsArgument(
() => provider.RegisterValidatableObjectAdapter(typeof(MyValidatableClass), typeof(MyValidatableAdapterBadCtor)),
"adapterType",
"The type MyValidatableAdapterBadCtor must have a public constructor which accepts two parameters of types ModelMetadata and IEnumerable<ModelValidatorProvider>");
}
[Fact]
public void RegisterValidatableObjectAdapterFactory()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
provider.ValidatableFactories = new Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory>();
DataAnnotationsValidatableObjectAdapterFactory factory = delegate { return null; };
// Act
provider.RegisterValidatableObjectAdapterFactory(typeof(MyValidatableClass), factory);
// Assert
var type = provider.ValidatableFactories.Keys.Single();
Assert.Equal(typeof(MyValidatableClass), type);
Assert.Same(factory, provider.ValidatableFactories.Values.Single());
}
[Fact]
public void RegisterValidatableObjectAdapterFactoryGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
DataAnnotationsValidatableObjectAdapterFactory factory = (context) => null;
// Attribute type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterValidatableObjectAdapterFactory(null, factory),
"modelType");
// Factory cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterValidatableObjectAdapterFactory(typeof(MyValidatableClass), null),
"factory");
// Validation attribute must derive from ValidationAttribute
Assert.ThrowsArgument(
() => provider.RegisterValidatableObjectAdapterFactory(typeof(object), factory),
"modelType",
"The type Object must derive from IValidatableObject");
}
[Fact]
public void RegisterDefaultValidatableObjectAdapter()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(MyValidatableClass));
provider.RegisterDefaultValidatableObjectAdapter(typeof(MyValidatableAdapter));
// Act
var result = provider.GetValidators(metadata, _noValidatorProviders).Single();
// Assert
Assert.IsType<MyValidatableAdapter>(result);
}
[Fact]
public void RegisterDefaultValidatableObjectAdapterGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Adapter type cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterDefaultValidatableObjectAdapter(null),
"adapterType");
// Adapter must derive from ModelValidator
Assert.ThrowsArgument(
() => provider.RegisterDefaultValidatableObjectAdapter(typeof(object)),
"adapterType",
"The type Object must derive from ModelValidator");
// Adapter must have the expected constructor
Assert.ThrowsArgument(
() => provider.RegisterDefaultValidatableObjectAdapter(typeof(MyValidatableAdapterBadCtor)),
"adapterType",
"The type MyValidatableAdapterBadCtor must have a public constructor which accepts two parameters of types ModelMetadata and IEnumerable<ModelValidatorProvider>");
}
[Fact]
public void RegisterDefaultValidatableObjectAdapterFactory()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(MyValidatableClass));
ModelValidator validator = new Mock<ModelValidator>(_noValidatorProviders).Object;
DataAnnotationsValidatableObjectAdapterFactory factory = delegate { return validator; };
provider.RegisterDefaultValidatableObjectAdapterFactory(factory);
// Act
var result = provider.GetValidators(metadata, _noValidatorProviders).Single();
// Assert
Assert.Same(validator, result);
}
[Fact]
public void RegisterDefaultValidatableObjectAdapterFactoryGuardClauses()
{
var provider = new DataAnnotationsModelValidatorProvider();
// Factory cannot be null
Assert.ThrowsArgumentNull(
() => provider.RegisterDefaultValidatableObjectAdapterFactory(null),
"factory");
}
// Default adapter factory for unknown attribute type
[Fact]
public void UnknownValidationAttributeGetsDefaultAdapter()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(DummyClassWithDummyValidationAttribute));
// Act
IEnumerable<ModelValidator> validators = provider.GetValidators(metadata, _noValidatorProviders);
// Assert
var validator = validators.Single();
Assert.IsType<DataAnnotationsModelValidator>(validator);
}
private class DummyValidationAttribute : ValidationAttribute
{
}
[DummyValidation]
private class DummyClassWithDummyValidationAttribute
{
}
// Default IValidatableObject adapter factory
[Fact]
public void IValidatableObjectGetsAValidator()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var mockValidatable = new Mock<IValidatableObject>();
var metadata = _metadataProvider.GetMetadataForType(() => null, mockValidatable.Object.GetType());
// Act
IEnumerable<ModelValidator> validators = provider.GetValidators(metadata, _noValidatorProviders);
// Assert
Assert.Single(validators);
}
// Integration with metadata system
[Fact]
public void DoesNotReadPropertyValue()
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var model = new ObservableModel();
ModelMetadata metadata = _metadataProvider.GetMetadataForProperty(() => model.TheProperty, typeof(ObservableModel), "TheProperty");
// Act
ModelValidator[] validators = provider.GetValidators(metadata, _noValidatorProviders).ToArray();
ModelValidationResult[] results = validators.SelectMany(o => o.Validate(metadata, model)).ToArray();
// Assert
Assert.Empty(validators);
Assert.False(model.PropertyWasRead());
}
private class ObservableModel
{
private bool _propertyWasRead;
public string TheProperty
{
get
{
_propertyWasRead = true;
return "Hello";
}
}
public bool PropertyWasRead()
{
return _propertyWasRead;
}
}
private class BaseModel
{
public virtual string MyProperty { get; set; }
}
private class DerivedModel : BaseModel
{
[StringLength(10)]
public override string MyProperty
{
get { return base.MyProperty; }
set { base.MyProperty = value; }
}
}
}
}