aspnetcore/test/Microsoft.AspNet.Mvc.ModelB.../Validation/ModelValidationNodeTest.cs

387 lines
14 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.
#if ASPNET50
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelValidationNodeTest
{
[Fact]
public void ConstructorSetsCollectionInstance()
{
// Arrange
var metadata = GetModelMetadata();
var modelStateKey = "someKey";
var childNodes = new[]
{
new ModelValidationNode(metadata, "someKey0"),
new ModelValidationNode(metadata, "someKey1")
};
// Act
var node = new ModelValidationNode(metadata, modelStateKey, childNodes);
// Assert
Assert.Equal(childNodes, node.ChildNodes.ToArray());
}
[Fact]
public void PropertiesAreSet()
{
// Arrange
var metadata = GetModelMetadata();
var modelStateKey = "someKey";
// Act
var node = new ModelValidationNode(metadata, modelStateKey);
// Assert
Assert.Equal(metadata, node.ModelMetadata);
Assert.Equal(modelStateKey, node.ModelStateKey);
Assert.NotNull(node.ChildNodes);
Assert.Empty(node.ChildNodes);
}
[Fact]
public void CombineWith()
{
// Arrange
var expected = new[]
{
"Validating parent1.",
"Validating parent2.",
"Validated parent1.",
"Validated parent2."
};
var log = new List<string>();
var allChildNodes = new[]
{
new ModelValidationNode(GetModelMetadata(), "key1"),
new ModelValidationNode(GetModelMetadata(), "key2"),
new ModelValidationNode(GetModelMetadata(), "key3"),
};
var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
parentNode1.ChildNodes.Add(allChildNodes[0]);
parentNode1.Validating += (sender, e) => log.Add("Validating parent1.");
parentNode1.Validated += (sender, e) => log.Add("Validated parent1.");
var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
parentNode2.ChildNodes.Add(allChildNodes[1]);
parentNode2.ChildNodes.Add(allChildNodes[2]);
parentNode2.Validating += (sender, e) => log.Add("Validating parent2.");
parentNode2.Validated += (sender, e) => log.Add("Validated parent2.");
var context = CreateContext();
// Act
parentNode1.CombineWith(parentNode2);
parentNode1.Validate(context);
// Assert
Assert.Equal(expected, log);
Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray());
}
[Fact]
public void CombineWith_OtherNodeIsSuppressed_DoesNothing()
{
// Arrange
var log = new List<string>();
var allChildNodes = new[]
{
new ModelValidationNode(GetModelMetadata(), "key1"),
new ModelValidationNode(GetModelMetadata(), "key2"),
new ModelValidationNode(GetModelMetadata(), "key3"),
};
var expectedChildNodes = new[]
{
allChildNodes[0]
};
var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
parentNode1.ChildNodes.Add(allChildNodes[0]);
parentNode1.Validating += (sender, e) => log.Add("Validating parent1.");
parentNode1.Validated += (sender, e) => log.Add("Validated parent1.");
var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
parentNode2.ChildNodes.Add(allChildNodes[1]);
parentNode2.ChildNodes.Add(allChildNodes[2]);
parentNode2.Validating += (sender, e) => log.Add("Validating parent2.");
parentNode2.Validated += (sender, e) => log.Add("Validated parent2.");
parentNode2.SuppressValidation = true;
var context = CreateContext();
// Act
parentNode1.CombineWith(parentNode2);
parentNode1.Validate(context);
// Assert
Assert.Equal(new[] { "Validating parent1.", "Validated parent1." }, log.ToArray());
Assert.Equal(expectedChildNodes, parentNode1.ChildNodes.ToArray());
}
[Fact]
public void Validate_Ordering()
{
// Proper order of invocation:
// 1. OnValidating()
// 2. Child validators
// 3. This validator
// 4. OnValidated()
// Arrange
var expected = new[]
{
"In OnValidating()",
"In LoggingValidatonAttribute.IsValid()",
"In IValidatableObject.Validate()",
"In OnValidated()"
};
var log = new List<string>();
var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model);
var provider = new EmptyModelMetadataProvider();
var childMetadata = provider.GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty");
var node = new ModelValidationNode(modelMetadata, "theKey");
node.Validating += (sender, e) => log.Add("In OnValidating()");
node.Validated += (sender, e) => log.Add("In OnValidated()");
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.ValidStringProperty"));
var context = CreateContext(modelMetadata);
// Act
node.Validate(context);
// Assert
Assert.Equal(expected, log);
}
[Fact]
public void Validate_SkipsRemainingValidationIfModelStateIsInvalid()
{
// Because a property validator fails, the model validator shouldn't run
// Arrange
var expected = new[]
{
"In OnValidating()",
"In IValidatableObject.Validate()",
"In OnValidated()"
};
var log = new List<string>();
var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model);
var provider = new EmptyModelMetadataProvider();
var childMetadata = provider.GetMetadataForProperty(() => model,
model.GetType(),
"InvalidStringProperty");
var node = new ModelValidationNode(modelMetadata, "theKey");
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty"));
node.Validating += (sender, e) => log.Add("In OnValidating()");
node.Validated += (sender, e) => log.Add("In OnValidated()");
var context = CreateContext(modelMetadata);
// Act
node.Validate(context);
// Assert
Assert.Equal(expected, log);
Assert.Equal("Sample error message",
context.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage);
}
[Fact]
public void Validate_SkipsValidationIfHandlerCancels()
{
// Arrange
var log = new List<string>();
var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model);
var node = new ModelValidationNode(modelMetadata, "theKey");
node.Validating += (sender, e) =>
{
log.Add("In OnValidating()");
e.Cancel = true;
};
node.Validated += (sender, e) => log.Add("In OnValidated()");
var context = CreateContext(modelMetadata);
// Act
node.Validate(context);
// Assert
Assert.Equal(new[] { "In OnValidating()" }, log.ToArray());
}
[Fact]
public void Validate_SkipsValidationIfSuppressed()
{
// Arrange
var log = new List<string>();
var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model);
var node = new ModelValidationNode(modelMetadata, "theKey")
{
SuppressValidation = true
};
node.Validating += (sender, e) => log.Add("In OnValidating()");
node.Validated += (sender, e) => log.Add("In OnValidated()");
var context = CreateContext();
// Act
node.Validate(context);
// Assert
Assert.Empty(log);
}
[Fact]
[ReplaceCulture]
public void Validate_ValidateAllProperties_AddsValidationErrors()
{
// Arrange
var model = new ValidateAllPropertiesModel
{
RequiredString = null /* error */,
RangedInt = 0 /* error */,
ValidString = "dog"
};
var modelMetadata = GetModelMetadata(model);
var node = new ModelValidationNode(modelMetadata, "theKey")
{
ValidateAllProperties = true
};
var context = CreateContext(modelMetadata);
context.ModelState.AddModelError("theKey.RequiredString.Dummy", "existing Error Text");
// Act
node.Validate(context);
// Assert
Assert.False(context.ModelState.ContainsKey("theKey.RequiredString"));
Assert.Equal("existing Error Text",
context.ModelState["theKey.RequiredString.Dummy"].Errors[0].ErrorMessage);
Assert.Equal("The field RangedInt must be between 10 and 30.",
context.ModelState["theKey.RangedInt"].Errors[0].ErrorMessage);
Assert.False(context.ModelState.ContainsKey("theKey.ValidString"));
Assert.False(context.ModelState.ContainsKey("theKey"));
}
[Fact]
[ReplaceCulture]
public void Validate_ShortCircuits_IfModelStateHasReachedMaxNumberOfErrors()
{
// Arrange
var model = new ValidateAllPropertiesModel
{
RequiredString = null /* error */,
RangedInt = 0 /* error */,
ValidString = "cat" /* error */
};
var modelMetadata = GetModelMetadata(model);
var node = new ModelValidationNode(modelMetadata, "theKey")
{
ValidateAllProperties = true
};
var context = CreateContext(modelMetadata);
context.ModelState.MaxAllowedErrors = 3;
context.ModelState.AddModelError("somekey", "error text");
// Act
node.Validate(context);
// Assert
Assert.Equal(3, context.ModelState.Count);
Assert.IsType<TooManyModelErrorsException>(context.ModelState[""].Errors[0].Exception);
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("RequiredString"),
context.ModelState["theKey.RequiredString"].Errors[0].ErrorMessage);
Assert.False(context.ModelState.ContainsKey("theKey.RangedInt"));
Assert.False(context.ModelState.ContainsKey("theKey.ValidString"));
}
private static ModelMetadata GetModelMetadata()
{
return new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object));
}
private static ModelMetadata GetModelMetadata(object o)
{
return new DataAnnotationsModelMetadataProvider().GetMetadataForType(() => o, o.GetType());
}
private static ModelValidationContext CreateContext(ModelMetadata metadata = null)
{
var providers = new IModelValidatorProvider[]
{
new DataAnnotationsModelValidatorProvider(),
new DataMemberModelValidatorProvider()
};
var provider = new Mock<IModelValidatorProviderProvider>();
provider.SetupGet(p => p.ModelValidatorProviders)
.Returns(providers);
return new ModelValidationContext(new EmptyModelMetadataProvider(),
new CompositeModelValidatorProvider(provider.Object),
new ModelStateDictionary(),
metadata,
null);
}
private sealed class LoggingValidatableObject : IValidatableObject
{
private readonly IList<string> _log;
public LoggingValidatableObject(IList<string> log)
{
_log = log;
}
[LoggingValidation]
public string ValidStringProperty { get; set; }
public string InvalidStringProperty { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
_log.Add("In IValidatableObject.Validate()");
yield return new ValidationResult("Sample error message", new[] { "InvalidStringProperty" });
}
private sealed class LoggingValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
LoggingValidatableObject lvo = (LoggingValidatableObject)value;
lvo._log.Add("In LoggingValidatonAttribute.IsValid()");
return ValidationResult.Success;
}
}
}
private class ValidateAllPropertiesModel
{
[Required]
public string RequiredString { get; set; }
[Range(10, 30)]
public int RangedInt { get; set; }
[RegularExpression("dog")]
public string ValidString { get; set; }
}
}
}
#endif