context: namespace Microsoft.Web.Mvc.ModelBinding.Test {
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.ComponentModel.DataAnnotations;
      using System.Linq;
      using System.Web.Mvc;
      using System.Web.TestUtil;
      using Microsoft.VisualStudio.TestTools.UnitTesting;
  
      [TestClass]
      public class ModelValidationNodeTest {
  
          [TestMethod]
          public void ConstructorSetsCollectionInstance() {
              // Arrange
              ModelMetadata metadata = GetModelMetadata();
              string modelStateKey = "someKey";
              ModelValidationNode[] childNodes = new ModelValidationNode[] {
                  new ModelValidationNode(metadata, "someKey0"),
                  new ModelValidationNode(metadata, "someKey1")
              };
  
              // Act
              ModelValidationNode node = new ModelValidationNode(metadata, modelStateKey, childNodes);
  
              // Assert
              CollectionAssert.AreEqual(childNodes, (ICollection)node.ChildNodes);
          }
  
          [TestMethod]
          public void ConstructorThrowsIfModelMetadataIsNull() {
              // Act & assert
              ExceptionHelper.ExpectArgumentNullException(
                  delegate {
                      new ModelValidationNode(null, "someKey");
                  }, "modelMetadata");
          }
  
          [TestMethod]
          public void ConstructorThrowsIfModelStateKeyIsNull() {
              // Arrange
              ModelMetadata metadata = GetModelMetadata();
  
              // Act & assert
              ExceptionHelper.ExpectArgumentNullException(
                  delegate {
                      new ModelValidationNode(metadata, null);
                  }, "modelStateKey");
          }
  
          [TestMethod]
          public void PropertiesAreSet() {
              // Arrange
              ModelMetadata metadata = GetModelMetadata();
              string modelStateKey = "someKey";
  
              // Act
              ModelValidationNode node = new ModelValidationNode(metadata, modelStateKey);
  
              // Assert
              Assert.AreEqual(metadata, node.ModelMetadata);
              Assert.AreEqual(modelStateKey, node.ModelStateKey);
              Assert.IsNotNull(node.ChildNodes);
              Assert.AreEqual(0, node.ChildNodes.Count);
          }
  
          [TestMethod]
          public void CombineWith() {
              // Arrange
              List<string> log = new List<string>();
  
              ModelValidationNode[] allChildNodes = new ModelValidationNode[] {
                  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
              CollectionAssert.AreEqual(
                  new string[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." },
                  log);
              CollectionAssert.AreEqual(allChildNodes, parentNode1.ChildNodes.ToArray());
          }
  
          [TestMethod]
          public void CombineWith_OtherNodeIsSuppressed_DoesNothing() {
              // Arrange
              List<string> log = new List<string>();
  
              ModelValidationNode[] allChildNodes = new ModelValidationNode[] {
                  new ModelValidationNode(GetModelMetadata(), "key1"),
                  new ModelValidationNode(GetModelMetadata(), "key2"),
                  new ModelValidationNode(GetModelMetadata(), "key3"),
              };
  
              ModelValidationNode[] expectedChildNodes = new ModelValidationNode[] {
                  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
              CollectionAssert.AreEqual(
                  new string[] { "Validating parent1.", "Validated parent1." },
                  log);
              CollectionAssert.AreEqual(expectedChildNodes, parentNode1.ChildNodes.ToArray());
          }
  
          [TestMethod]
          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
              CollectionAssert.AreEqual(
                  new string[] { "In OnValidating()", "In IDataErrorInfo.get_Item('ValidStringProperty')", "In IDataErrorInfo.get_Error()", "In OnValidated()" },
                  log);
          }
  
          [TestMethod]
          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
              CollectionAssert.AreEqual(
                  new string[] { "In IDataErrorInfo.get_Error()" },
                  log1);
  
              CollectionAssert.AreEqual(
                  new string[] { "In IDataErrorInfo.get_Error()" },
                  log2);
          }
  
          [TestMethod]
          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
              CollectionAssert.AreEqual(
                  new string[] { "In OnValidating()", "In IDataErrorInfo.get_Item('InvalidStringProperty')", "In OnValidated()" },
                  log);
  
              Assert.AreEqual("Sample error message", controllerContext.Controller.ViewData.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage);
          }
  
          [TestMethod]
          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
              CollectionAssert.AreEqual(
                  new string[] { "In OnValidating()" },
                  log);
          }
  
          [TestMethod]
          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
              CollectionAssert.AreEqual(new string[0], log, "Validate() should have been a no-op.");
          }
  
          [TestMethod]
          public void Validate_ThrowsIfControllerContextIsNull() {
              // Arrange
              ModelValidationNode node = new ModelValidationNode(GetModelMetadata(), "someKey");
  
              // Act & assert
              ExceptionHelper.ExpectArgumentNullException(
                  delegate {
                      node.Validate(null);
                  }, "controllerContext");
          }
  
          [TestMethod]
          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() {
  
gt:                 Controller = new EmptyController()