392 lines
15 KiB
C#
392 lines
15 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;
|
|||
|
using System.ComponentModel.DataAnnotations;
|
|||
|
using System.Linq;
|
|||
|
using System.Web.Mvc;
|
|||
|
using Xunit;
|
|||
|
using Assert = Microsoft.TestCommon.AssertEx;
|
|||
|
|
|||
|
namespace Microsoft.Web.Mvc.ModelBinding.Test
|
|||
|
{
|
|||
|
public class ModelValidationNodeTest
|
|||
|
{
|
|||
|
[Fact]
|
|||
|
public void ConstructorSetsCollectionInstance()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ModelMetadata metadata = GetModelMetadata();
|
|||
|
string modelStateKey = "someKey";
|
|||
|
ModelValidationNode[] childNodes = new[]
|
|||
|
{
|
|||
|
new ModelValidationNode(metadata, "someKey0"),
|
|||
|
new ModelValidationNode(metadata, "someKey1")
|
|||
|
};
|
|||
|
|
|||
|
// Act
|
|||
|
ModelValidationNode node = new ModelValidationNode(metadata, modelStateKey, childNodes);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(childNodes, node.ChildNodes.ToArray());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ConstructorThrowsIfModelMetadataIsNull()
|
|||
|
{
|
|||
|
// Act & assert
|
|||
|
Assert.ThrowsArgumentNull(
|
|||
|
delegate { new ModelValidationNode(null, "someKey"); }, "modelMetadata");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ConstructorThrowsIfModelStateKeyIsNull()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ModelMetadata metadata = GetModelMetadata();
|
|||
|
|
|||
|
// Act & assert
|
|||
|
Assert.ThrowsArgumentNull(
|
|||
|
delegate { new ModelValidationNode(metadata, null); }, "modelStateKey");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void PropertiesAreSet()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ModelMetadata metadata = GetModelMetadata();
|
|||
|
string modelStateKey = "someKey";
|
|||
|
|
|||
|
// Act
|
|||
|
ModelValidationNode 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
|
|||
|
List<string> log = new List<string>();
|
|||
|
|
|||
|
ModelValidationNode[] allChildNodes = new[]
|
|||
|
{
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key1"),
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key2"),
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key3"),
|
|||
|
};
|
|||
|
|
|||
|
ModelValidationNode parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
|
|||
|
parentNode1.ChildNodes.Add(allChildNodes[0]);
|
|||
|
parentNode1.Validating += delegate { log.Add("Validating parent1."); };
|
|||
|
parentNode1.Validated += delegate { log.Add("Validated parent1."); };
|
|||
|
|
|||
|
ModelValidationNode parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
|
|||
|
parentNode2.ChildNodes.Add(allChildNodes[1]);
|
|||
|
parentNode2.ChildNodes.Add(allChildNodes[2]);
|
|||
|
parentNode2.Validating += delegate { log.Add("Validating parent2."); };
|
|||
|
parentNode2.Validated += delegate { log.Add("Validated parent2."); };
|
|||
|
|
|||
|
// Act
|
|||
|
parentNode1.CombineWith(parentNode2);
|
|||
|
parentNode1.Validate(new ControllerContext { Controller = new EmptyController() });
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }, log.ToArray());
|
|||
|
Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void CombineWith_OtherNodeIsSuppressed_DoesNothing()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
List<string> log = new List<string>();
|
|||
|
|
|||
|
ModelValidationNode[] allChildNodes = new[]
|
|||
|
{
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key1"),
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key2"),
|
|||
|
new ModelValidationNode(GetModelMetadata(), "key3"),
|
|||
|
};
|
|||
|
|
|||
|
ModelValidationNode[] expectedChildNodes = new[]
|
|||
|
{
|
|||
|
allChildNodes[0]
|
|||
|
};
|
|||
|
|
|||
|
ModelValidationNode parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
|
|||
|
parentNode1.ChildNodes.Add(allChildNodes[0]);
|
|||
|
parentNode1.Validating += delegate { log.Add("Validating parent1."); };
|
|||
|
parentNode1.Validated += delegate { log.Add("Validated parent1."); };
|
|||
|
|
|||
|
ModelValidationNode parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
|
|||
|
parentNode2.ChildNodes.Add(allChildNodes[1]);
|
|||
|
parentNode2.ChildNodes.Add(allChildNodes[2]);
|
|||
|
parentNode2.Validating += delegate { log.Add("Validating parent2."); };
|
|||
|
parentNode2.Validated += delegate { log.Add("Validated parent2."); };
|
|||
|
parentNode2.SuppressValidation = true;
|
|||
|
|
|||
|
// Act
|
|||
|
parentNode1.CombineWith(parentNode2);
|
|||
|
parentNode1.Validate(new ControllerContext { Controller = new EmptyController() });
|
|||
|
|
|||
|
// 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
|
|||
|
List<string> log = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log);
|
|||
|
ModelMetadata modelMetadata = GetModelMetadata(model);
|
|||
|
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey");
|
|||
|
|
|||
|
ModelMetadata childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty");
|
|||
|
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.ValidStringProperty"));
|
|||
|
|
|||
|
node.Validating += delegate { log.Add("In OnValidating()"); };
|
|||
|
node.Validated += delegate { log.Add("In OnValidated()"); };
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new[] { "In OnValidating()", "In IDataErrorInfo.get_Item('ValidStringProperty')", "In IDataErrorInfo.get_Error()", "In OnValidated()" }, log.ToArray());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_PassesNullContainerInstanceIfCannotBeConvertedToProperType()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
List<string> log1 = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model1 = new LoggingDataErrorInfoModel(log1);
|
|||
|
ModelMetadata modelMetadata1 = GetModelMetadata(model1);
|
|||
|
|
|||
|
List<string> log2 = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model2 = new LoggingDataErrorInfoModel(log2);
|
|||
|
ModelMetadata modelMetadata2 = GetModelMetadata(model2);
|
|||
|
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata1, "theKey");
|
|||
|
node.ChildNodes.Add(new ModelValidationNode(modelMetadata2, "theKey.SomeProperty"));
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new[] { "In IDataErrorInfo.get_Error()" }, log1.ToArray());
|
|||
|
Assert.Equal(new[] { "In IDataErrorInfo.get_Error()" }, log2.ToArray());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_SkipsRemainingValidationIfModelStateIsInvalid()
|
|||
|
{
|
|||
|
// Because a property validator fails, the model validator shouldn't run
|
|||
|
|
|||
|
// Arrange
|
|||
|
List<string> log = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log);
|
|||
|
ModelMetadata modelMetadata = GetModelMetadata(model);
|
|||
|
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey");
|
|||
|
|
|||
|
ModelMetadata childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "InvalidStringProperty");
|
|||
|
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty"));
|
|||
|
|
|||
|
node.Validating += delegate { log.Add("In OnValidating()"); };
|
|||
|
node.Validated += delegate { log.Add("In OnValidated()"); };
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new[] { "In OnValidating()", "In IDataErrorInfo.get_Item('InvalidStringProperty')", "In OnValidated()" }, log.ToArray());
|
|||
|
Assert.Equal("Sample error message", controllerContext.Controller.ViewData.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_SkipsValidationIfHandlerCancels()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
List<string> log = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log);
|
|||
|
ModelMetadata modelMetadata = GetModelMetadata(model);
|
|||
|
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey");
|
|||
|
|
|||
|
node.Validating += (sender, e) =>
|
|||
|
{
|
|||
|
log.Add("In OnValidating()");
|
|||
|
e.Cancel = true;
|
|||
|
};
|
|||
|
node.Validated += delegate { log.Add("In OnValidated()"); };
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new[] { "In OnValidating()" }, log.ToArray());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_SkipsValidationIfSuppressed()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
List<string> log = new List<string>();
|
|||
|
LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log);
|
|||
|
ModelMetadata modelMetadata = GetModelMetadata(model);
|
|||
|
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey")
|
|||
|
{
|
|||
|
SuppressValidation = true
|
|||
|
};
|
|||
|
|
|||
|
node.Validating += (sender, e) => { log.Add("In OnValidating()"); };
|
|||
|
node.Validated += delegate { log.Add("In OnValidated()"); };
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(log);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_ThrowsIfControllerContextIsNull()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ModelValidationNode node = new ModelValidationNode(GetModelMetadata(), "someKey");
|
|||
|
|
|||
|
// Act & assert
|
|||
|
Assert.ThrowsArgumentNull(
|
|||
|
delegate { node.Validate(null); }, "controllerContext");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Validate_ValidateAllProperties_AddsValidationErrors()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ValidateAllPropertiesModel model = new ValidateAllPropertiesModel
|
|||
|
{
|
|||
|
RequiredString = null /* error */,
|
|||
|
RangedInt = 0 /* error */,
|
|||
|
ValidString = "dog"
|
|||
|
};
|
|||
|
|
|||
|
ModelMetadata modelMetadata = GetModelMetadata(model);
|
|||
|
ControllerContext controllerContext = new ControllerContext
|
|||
|
{
|
|||
|
Controller = new EmptyController()
|
|||
|
};
|
|||
|
ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey")
|
|||
|
{
|
|||
|
ValidateAllProperties = true
|
|||
|
};
|
|||
|
|
|||
|
controllerContext.Controller.ViewData.ModelState.AddModelError("theKey.RequiredString.Dummy", "existing Error Text");
|
|||
|
|
|||
|
// Act
|
|||
|
node.Validate(controllerContext);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey.RequiredString"]);
|
|||
|
Assert.Equal("existing Error Text", controllerContext.Controller.ViewData.ModelState["theKey.RequiredString.Dummy"].Errors[0].ErrorMessage);
|
|||
|
Assert.Equal("The field RangedInt must be between 10 and 30.", controllerContext.Controller.ViewData.ModelState["theKey.RangedInt"].Errors[0].ErrorMessage);
|
|||
|
Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey.ValidString"]);
|
|||
|
Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey"]);
|
|||
|
}
|
|||
|
|
|||
|
private static ModelMetadata GetModelMetadata()
|
|||
|
{
|
|||
|
EmptyModelMetadataProvider provider = new EmptyModelMetadataProvider();
|
|||
|
return provider.GetMetadataForType(null, typeof(object));
|
|||
|
}
|
|||
|
|
|||
|
private static ModelMetadata GetModelMetadata(object o)
|
|||
|
{
|
|||
|
DataAnnotationsModelMetadataProvider provider = new DataAnnotationsModelMetadataProvider();
|
|||
|
return provider.GetMetadataForType(() => o, o.GetType());
|
|||
|
}
|
|||
|
|
|||
|
private sealed class EmptyController : Controller
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
private sealed class LoggingDataErrorInfoModel : IDataErrorInfo
|
|||
|
{
|
|||
|
private readonly IList<string> _log;
|
|||
|
|
|||
|
public LoggingDataErrorInfoModel(IList<string> log)
|
|||
|
{
|
|||
|
_log = log;
|
|||
|
}
|
|||
|
|
|||
|
string IDataErrorInfo.Error
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
_log.Add("In IDataErrorInfo.get_Error()");
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
string IDataErrorInfo.this[string columnName]
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
_log.Add("In IDataErrorInfo.get_Item('" + columnName + "')");
|
|||
|
return (columnName == "ValidStringProperty") ? null : "Sample error message";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string ValidStringProperty { get; set; }
|
|||
|
public string InvalidStringProperty { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
private class ValidateAllPropertiesModel
|
|||
|
{
|
|||
|
[Required]
|
|||
|
public string RequiredString { get; set; }
|
|||
|
|
|||
|
[Range(10, 30)]
|
|||
|
public int RangedInt { get; set; }
|
|||
|
|
|||
|
[RegularExpression("dog")]
|
|||
|
public string ValidString { get; set; }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|