File: DataAnnotationsModelValidatorProviderTest.cs
Web Access
Project: src\src\Mvc\Mvc.DataAnnotations\test\Microsoft.AspNetCore.Mvc.DataAnnotations.Test.csproj (Microsoft.AspNetCore.Mvc.DataAnnotations.Test)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Options;
using Moq;
 
namespace Microsoft.AspNetCore.Mvc.DataAnnotations;
 
public class DataAnnotationsModelValidatorProviderTest
{
    private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
 
    [Fact]
    public void CreateValidators_ReturnsValidatorForIValidatableObject()
    {
        // Arrange
        var provider = new DataAnnotationsModelValidatorProvider(
            new ValidationAttributeAdapterProvider(),
            Options.Create(new MvcDataAnnotationsLocalizationOptions()),
            stringLocalizerFactory: null);
        var mockValidatable = Mock.Of<IValidatableObject>();
        var metadata = _metadataProvider.GetMetadataForType(mockValidatable.GetType());
 
        var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
 
        // Act
        provider.CreateValidators(providerContext);
 
        // Assert
        var validatorItem = Assert.Single(providerContext.Results);
        Assert.IsType<ValidatableObjectAdapter>(validatorItem.Validator);
    }
 
    [Fact]
    public void CreateValidators_InsertsRequiredValidatorsFirst()
    {
        var provider = new DataAnnotationsModelValidatorProvider(
            new ValidationAttributeAdapterProvider(),
            Options.Create(new MvcDataAnnotationsLocalizationOptions()),
            stringLocalizerFactory: null);
        var metadata = _metadataProvider.GetMetadataForProperty(
            typeof(ClassWithProperty),
            "PropertyWithMultipleValidationAttributes");
 
        var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
 
        // Act
        provider.CreateValidators(providerContext);
 
        // Assert
        Assert.Equal(4, providerContext.Results.Count);
        Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Results[0].Validator).Attribute);
        Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Results[1].Validator).Attribute);
    }
 
    [Fact]
    public void UnknownValidationAttributeGetsDefaultAdapter()
    {
        // Arrange
        var provider = new DataAnnotationsModelValidatorProvider(
            new ValidationAttributeAdapterProvider(),
            Options.Create(new MvcDataAnnotationsLocalizationOptions()),
            stringLocalizerFactory: null);
        var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
 
        var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
 
        // Act
        provider.CreateValidators(providerContext);
 
        // Assert
        var validatorItem = providerContext.Results.Single();
        Assert.IsType<DataAnnotationsModelValidator>(validatorItem.Validator);
    }
 
    private class DummyValidationAttribute : ValidationAttribute
    {
    }
 
    [DummyValidation]
    private class DummyClassWithDummyValidationAttribute
    {
    }
 
    // Default IValidatableObject adapter factory
 
    [Fact]
    public void IValidatableObjectGetsAValidator()
    {
        // Arrange
        var provider = new DataAnnotationsModelValidatorProvider(
            new ValidationAttributeAdapterProvider(),
            Options.Create(new MvcDataAnnotationsLocalizationOptions()),
            stringLocalizerFactory: null);
        var mockValidatable = new Mock<IValidatableObject>();
        var metadata = _metadataProvider.GetMetadataForType(mockValidatable.Object.GetType());
 
        var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
 
        // Act
        provider.CreateValidators(providerContext);
 
        // Assert
        Assert.Single(providerContext.Results);
    }
 
    [Fact]
    public void HasValidators_ReturnsTrue_IfModelIsIValidatableObject()
    {
        // Arrange
        var provider = GetProvider();
        var mockValidatable = Mock.Of<IValidatableObject>();
 
        // Act
        var result = provider.HasValidators(mockValidatable.GetType(), Array.Empty<object>());
 
        // Assert
        Assert.True(result);
    }
 
    [Fact]
    public void HasValidators_ReturnsTrue_IfMetadataContainsValidationAttribute()
    {
        // Arrange
        var provider = GetProvider();
        var attributes = new object[] { new BindNeverAttribute(), new DummyValidationAttribute() };
 
        // Act
        var result = provider.HasValidators(typeof(object), attributes);
 
        // Assert
        Assert.True(result);
    }
 
    [Fact]
    public void HasValidators_ReturnsFalse_IfNoDataAnnotationsValidationIsAvailable()
    {
        // Arrange
        var provider = GetProvider();
        var attributes = new object[] { new BindNeverAttribute(), };
 
        // Act
        var result = provider.HasValidators(typeof(object), attributes);
 
        // Assert
        Assert.False(result);
    }
 
    private static DataAnnotationsModelValidatorProvider GetProvider()
    {
        return new DataAnnotationsModelValidatorProvider(
            new ValidationAttributeAdapterProvider(),
            Options.Create(new MvcDataAnnotationsLocalizationOptions()),
            stringLocalizerFactory: null);
    }
 
    private IList<ValidatorItem> GetValidatorItems(ModelMetadata metadata)
    {
        var items = new List<ValidatorItem>(metadata.ValidatorMetadata.Count);
        for (var i = 0; i < metadata.ValidatorMetadata.Count; i++)
        {
            items.Add(new ValidatorItem(metadata.ValidatorMetadata[i]));
        }
 
        return items;
    }
 
    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; }
        }
    }
 
    private class DummyRequiredAttributeHelperClass
    {
        [Required(ErrorMessage = "Custom Required Message")]
        public int WithAttribute { get; set; }
 
        public int WithoutAttribute { get; set; }
    }
 
    private class ClassWithProperty
    {
        [CustomNonRequiredAttribute1]
        [CustomNonRequiredAttribute2]
        [CustomRequiredAttribute1]
        [CustomRequiredAttribute2]
        public string PropertyWithMultipleValidationAttributes { get; set; }
    }
 
    public class CustomRequiredAttribute1 : RequiredAttribute
    {
    }
 
    public class CustomRequiredAttribute2 : RequiredAttribute
    {
    }
 
    public class CustomNonRequiredAttribute1 : ValidationAttribute
    {
    }
 
    public class CustomNonRequiredAttribute2 : ValidationAttribute
    {
    }
}