a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
520 lines
20 KiB
C#
520 lines
20 KiB
C#
// 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; }
|
|
}
|
|
}
|
|
}
|
|
}
|