1128 lines
43 KiB
C#
1128 lines
43 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
#if DNXCORE50
|
|
using System.Reflection;
|
|
#endif
|
|
using Microsoft.AspNet.Http;
|
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
|
using Microsoft.AspNet.Testing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNet.Mvc.Internal
|
|
{
|
|
public class DefaultObjectValidatorTests
|
|
{
|
|
private IModelMetadataProvider MetadataProvider { get; } = TestModelMetadataProvider.CreateDefaultProvider();
|
|
|
|
[Fact]
|
|
public void Validate_SimpleValueType_Valid_WithPrefix()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)15;
|
|
|
|
modelState.SetModelValue("parameter", "15", "15");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
AssertKeysEqual(modelState, "parameter");
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_SimpleReferenceType_Valid_WithPrefix()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)"test";
|
|
|
|
modelState.SetModelValue("parameter", "test", "test");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter");
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_SimpleType_MaxErrorsReached()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)"test";
|
|
|
|
modelState.MaxAllowedErrors = 1;
|
|
modelState.AddModelError("other.Model", "error");
|
|
modelState.SetModelValue("parameter", "test", "test");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, string.Empty, "parameter");
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_SimpleType_SuppressValidation()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)"test";
|
|
|
|
modelState.SetModelValue("parameter", "test", "test");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter", SuppressValidation = true });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter");
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
|
|
[Fact]
|
|
public void Validate_ComplexValueType_Valid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new ValueType() { Reference = "ref", Value = 256 };
|
|
|
|
modelState.SetModelValue("parameter.Reference", "ref", "ref");
|
|
modelState.SetModelValue("parameter.Value", "256", "256");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter.Reference", "parameter.Value");
|
|
|
|
var entry = modelState["parameter.Reference"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["parameter.Value"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ComplexReferenceType_Valid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new ReferenceType() { Reference = "ref", Value = 256 };
|
|
|
|
modelState.SetModelValue("parameter.Reference", "ref", "ref");
|
|
modelState.SetModelValue("parameter.Value", "256", "256");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter.Reference", "parameter.Value");
|
|
|
|
var entry = modelState["parameter.Reference"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["parameter.Value"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ComplexReferenceType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Person();
|
|
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "Name", "Profession");
|
|
|
|
var entry = modelState["Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ComplexType_SuppressValidation()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = new Person2()
|
|
{
|
|
Name = "Billy",
|
|
Address = new Address { Street = "GreaterThan5Characters" }
|
|
};
|
|
|
|
modelState.SetModelValue("person.Name", "Billy", "Billy");
|
|
modelState.SetModelValue("person.Address.Street", "GreaterThan5Characters", "GreaterThan5Characters");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "person" });
|
|
validationState.Add(model.Address, new ValidationStateEntry()
|
|
{
|
|
Key = "person.Address",
|
|
SuppressValidation = true
|
|
});
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "person", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "person.Name", "person.Address.Street");
|
|
|
|
var entry = modelState["person.Name"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["person.Address.Street"];
|
|
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_ComplexReferenceType_Invalid_MultipleErrorsOnProperty()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Address() { Street = "Microsoft Way" };
|
|
|
|
modelState.SetModelValue("parameter.Street", "Microsoft Way", "Microsoft Way");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter.Street");
|
|
|
|
var entry = modelState["parameter.Street"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
|
|
Assert.Equal(2, entry.Errors.Count);
|
|
var errorMessages = entry.Errors.Select(e => e.ErrorMessage);
|
|
Assert.Contains(ValidationAttributeUtil.GetStringLengthErrorMessage(null, 5, "Street"), errorMessages);
|
|
Assert.Contains(ValidationAttributeUtil.GetRegExErrorMessage("hehehe", "Street"), errorMessages);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_ComplexReferenceType_Invalid_MultipleErrorsOnProperty_EmptyPrefix()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Address() { Street = "Microsoft Way" };
|
|
|
|
modelState.SetModelValue("Street", "Microsoft Way", "Microsoft Way");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "Street");
|
|
|
|
var entry = modelState["Street"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
|
|
Assert.Equal(2, entry.Errors.Count);
|
|
var errorMessages = entry.Errors.Select(e => e.ErrorMessage);
|
|
Assert.Contains(ValidationAttributeUtil.GetStringLengthErrorMessage(null, 5, "Street"), errorMessages);
|
|
Assert.Contains(ValidationAttributeUtil.GetRegExErrorMessage("hehehe", "Street"), errorMessages);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_NestedComplexReferenceType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Person() { Name = "Rick", Friend = new Person() };
|
|
|
|
modelState.SetModelValue("Name", "Rick", "Rick");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "Name", "Profession", "Friend.Name", "Friend.Profession");
|
|
|
|
var entry = modelState["Name"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
|
|
entry = modelState["Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
|
|
entry = modelState["Friend.Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["Friend.Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
}
|
|
|
|
// IValidatableObject is significant because the validators are on the object
|
|
// itself, not just the properties.
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_ComplexType_IValidatableObject_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new ValidatableModel();
|
|
|
|
modelState.SetModelValue("parameter", "model", "model");
|
|
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter", "parameter.Property1", "parameter.Property2", "parameter.Property3");
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal("Error1", error.ErrorMessage);
|
|
|
|
entry = modelState["parameter.Property1"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal("Error2", error.ErrorMessage);
|
|
|
|
entry = modelState["parameter.Property2"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal("Error3", error.ErrorMessage);
|
|
|
|
entry = modelState["parameter.Property3"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal("Error3", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ComplexType_IValidatableObject_CanUseRequestServices()
|
|
{
|
|
// Arrange
|
|
var service = new Mock<IExampleService>();
|
|
service.Setup(x => x.DoSomething()).Verifiable();
|
|
|
|
var provider = new ServiceCollection().AddSingleton(service.Object).BuildServiceProvider();
|
|
|
|
var httpContext = new Mock<HttpContext>();
|
|
httpContext.SetupGet(x => x.RequestServices).Returns(provider);
|
|
|
|
var actionContext = new ActionContext { HttpContext = httpContext.Object };
|
|
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = new Mock<IValidatableObject>();
|
|
model
|
|
.Setup(x => x.Validate(It.IsAny<ValidationContext>()))
|
|
.Callback((ValidationContext context) =>
|
|
{
|
|
var receivedService = context.GetService<IExampleService>();
|
|
Assert.Equal(service.Object, receivedService);
|
|
receivedService.DoSomething();
|
|
})
|
|
.Returns(new List<ValidationResult>());
|
|
|
|
// Act
|
|
validator.Validate(
|
|
actionContext,
|
|
validatorProvider,
|
|
validationState,
|
|
null,
|
|
model.Object);
|
|
|
|
// Assert
|
|
service.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_ComplexType_FieldsAreIgnored_Valid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new VariableTest() { test = 5 };
|
|
|
|
modelState.SetModelValue("parameter", "5", "5");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
Assert.Equal(1, modelState.Count);
|
|
|
|
var entry = modelState["parameter"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_ComplexType_CyclesNotFollowed_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var person = new Person() { Name = "Billy" };
|
|
person.Friend = person;
|
|
|
|
var model = (object)person;
|
|
|
|
modelState.SetModelValue("parameter.Name", "Billy", "Billy");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter.Name", "parameter.Profession");
|
|
|
|
var entry = modelState["parameter.Name"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["parameter.Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(error.ErrorMessage, ValidationAttributeUtil.GetRequiredErrorMessage("Profession"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ComplexType_ShortCircuit_WhenMaxErrorCountIsSet()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator(typeof(string));
|
|
|
|
var model = new User()
|
|
{
|
|
Password = "password-val",
|
|
ConfirmPassword = "not-password-val"
|
|
};
|
|
|
|
modelState.MaxAllowedErrors = 2;
|
|
modelState.AddModelError("key1", "error1");
|
|
modelState.SetModelValue("user.Password", "password-val", "password-val");
|
|
modelState.SetModelValue("user.ConfirmPassword", "not-password-val", "not-password-val");
|
|
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "user", });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "user", model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, string.Empty, "key1", "user.ConfirmPassword", "user.Password");
|
|
|
|
var entry = modelState[string.Empty];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_CollectionType_ArrayOfSimpleType_Valid_DefaultKeyPattern()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new int[] { 5, 17 };
|
|
|
|
modelState.SetModelValue("parameter[0]", "5", "17");
|
|
modelState.SetModelValue("parameter[1]", "17", "5");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "parameter" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "parameter", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "parameter[0]", "parameter[1]");
|
|
|
|
var entry = modelState["parameter[0]"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["parameter[0]"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_CollectionType_ArrayOfComplexType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Person[] { new Person(), new Person() };
|
|
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "[0].Name", "[0].Profession", "[1].Name", "[1].Profession");
|
|
|
|
var entry = modelState["[0].Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["[0].Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
|
|
entry = modelState["[1].Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["[1].Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_CollectionType_ListOfComplexType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new List<Person> { new Person(), new Person() };
|
|
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "[0].Name", "[0].Profession", "[1].Name", "[1].Profession");
|
|
|
|
var entry = modelState["[0].Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["[0].Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
|
|
entry = modelState["[1].Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Name"), error.ErrorMessage);
|
|
|
|
entry = modelState["[1].Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
|
}
|
|
|
|
public static TheoryData<object, Type> ValidCollectionData
|
|
{
|
|
get
|
|
{
|
|
return new TheoryData<object, Type>()
|
|
{
|
|
{ new int[] { 1, 2, 3 }, typeof(int[]) },
|
|
{ new string[] { "Foo", "Bar", "Baz" }, typeof(string[]) },
|
|
{ new List<string> { "Foo", "Bar", "Baz" }, typeof(IList<string>)},
|
|
{ new HashSet<string> { "Foo", "Bar", "Baz" }, typeof(string[]) },
|
|
{
|
|
new List<DateTime>
|
|
{
|
|
DateTime.Parse("1/1/14"),
|
|
DateTime.Parse("2/1/14"),
|
|
DateTime.Parse("3/1/14"),
|
|
},
|
|
typeof(ICollection<DateTime>)
|
|
},
|
|
{
|
|
new HashSet<Uri>
|
|
{
|
|
new Uri("http://example.com/1"),
|
|
new Uri("http://example.com/2"),
|
|
new Uri("http://example.com/3"),
|
|
},
|
|
typeof(HashSet<Uri>)
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(ValidCollectionData))]
|
|
public void Validate_IndexedCollectionTypes_Valid(object model, Type type)
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
modelState.Add("items[0]", new ModelStateEntry());
|
|
modelState.Add("items[1]", new ModelStateEntry());
|
|
modelState.Add("items[2]", new ModelStateEntry());
|
|
validationState.Add(model, new ValidationStateEntry()
|
|
{
|
|
Key = "items",
|
|
|
|
// Force the validator to treat it as the specified type.
|
|
Metadata = MetadataProvider.GetMetadataForType(type),
|
|
});
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "items", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "items[0]", "items[1]", "items[2]");
|
|
|
|
var entry = modelState["items[0]"];
|
|
Assert.Equal(entry.ValidationState, ModelValidationState.Valid);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["items[1]"];
|
|
Assert.Equal(entry.ValidationState, ModelValidationState.Valid);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["items[2]"];
|
|
Assert.Equal(entry.ValidationState, ModelValidationState.Valid);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_CollectionType_DictionaryOfSimpleType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = new Dictionary<string, string>()
|
|
{
|
|
{ "FooKey", "FooValue" },
|
|
{ "BarKey", "BarValue" }
|
|
};
|
|
|
|
modelState.Add("items[0].Key", new ModelStateEntry());
|
|
modelState.Add("items[0].Value", new ModelStateEntry());
|
|
modelState.Add("items[1].Key", new ModelStateEntry());
|
|
modelState.Add("items[1].Value", new ModelStateEntry());
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "items" });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "items", model);
|
|
|
|
// Assert
|
|
Assert.True(modelState.IsValid);
|
|
AssertKeysEqual(modelState, "items[0].Key", "items[0].Value", "items[1].Key", "items[1].Value");
|
|
|
|
var entry = modelState["items[0].Key"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["items[0].Value"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["items[1].Key"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["items[1].Value"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_CollectionType_DictionaryOfComplexType_Invalid()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = (object)new Dictionary<string, Person> { { "Joe", new Person() }, { "Mark", new Person() } };
|
|
|
|
modelState.SetModelValue("[0].Key", "Joe", "Joe");
|
|
modelState.SetModelValue("[1].Key", "Mark", "Mark");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = string.Empty });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
|
|
// Assert
|
|
Assert.False(modelState.IsValid);
|
|
AssertKeysEqual(
|
|
modelState,
|
|
"[0].Key",
|
|
"[0].Value.Name",
|
|
"[0].Value.Profession",
|
|
"[1].Key",
|
|
"[1].Value.Name",
|
|
"[1].Value.Profession");
|
|
|
|
var entry = modelState["[0].Key"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["[1].Key"];
|
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
|
|
entry = modelState["[0].Value.Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
var error = Assert.Single(entry.Errors);
|
|
Assert.Equal(error.ErrorMessage, ValidationAttributeUtil.GetRequiredErrorMessage("Name"));
|
|
|
|
entry = modelState["[0].Value.Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(error.ErrorMessage, ValidationAttributeUtil.GetRequiredErrorMessage("Profession"));
|
|
|
|
entry = modelState["[1].Value.Name"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(error.ErrorMessage, ValidationAttributeUtil.GetRequiredErrorMessage("Name"));
|
|
|
|
entry = modelState["[1].Value.Profession"];
|
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
|
error = Assert.Single(entry.Errors);
|
|
Assert.Equal(error.ErrorMessage, ValidationAttributeUtil.GetRequiredErrorMessage("Profession"));
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void Validate_DoesntCatchExceptions_FromPropertyAccessors()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = new ThrowingProperty();
|
|
|
|
// Act & Assert
|
|
Assert.Throws(
|
|
typeof(InvalidTimeZoneException),
|
|
() =>
|
|
{
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
});
|
|
}
|
|
|
|
// We use the reference equality comparer for breaking cycles
|
|
[Fact]
|
|
public void Validate_DoesNotUseOverridden_GetHashCodeOrEquals()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator();
|
|
|
|
var model = new TypeThatOverridesEquals[]
|
|
{
|
|
new TypeThatOverridesEquals { Funny = "hehe" },
|
|
new TypeThatOverridesEquals { Funny = "hehe" }
|
|
};
|
|
|
|
// Act & Assert (does not throw)
|
|
validator.Validate(actionContext, validatorProvider, validationState, string.Empty, model);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ForExcludedComplexType_PropertiesMarkedAsSkipped()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator(typeof(User));
|
|
|
|
var model = new User()
|
|
{
|
|
Password = "password-val",
|
|
ConfirmPassword = "not-password-val"
|
|
};
|
|
|
|
// Note that user.ConfirmPassword has no entry in modelstate - we should not
|
|
// create one just to mark it as skipped.
|
|
modelState.SetModelValue("user.Password", "password-val", "password-val");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "user", });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "user", model);
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, modelState.ValidationState);
|
|
AssertKeysEqual(modelState, "user.Password");
|
|
|
|
var entry = modelState["user.Password"];
|
|
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ForExcludedCollectionType_PropertiesMarkedAsSkipped()
|
|
{
|
|
// Arrange
|
|
var validatorProvider = CreateValidatorProvider();
|
|
var actionContext = new ActionContext();
|
|
var modelState = actionContext.ModelState;
|
|
var validationState = new ValidationStateDictionary();
|
|
|
|
var validator = CreateValidator(typeof(List<string>));
|
|
|
|
var model = new List<string>()
|
|
{
|
|
"15",
|
|
};
|
|
|
|
modelState.SetModelValue("userIds[0]", "15", "15");
|
|
validationState.Add(model, new ValidationStateEntry() { Key = "userIds", });
|
|
|
|
// Act
|
|
validator.Validate(actionContext, validatorProvider, validationState, "userIds", model);
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, modelState.ValidationState);
|
|
AssertKeysEqual(modelState, "userIds[0]");
|
|
|
|
var entry = modelState["userIds[0]"];
|
|
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
Assert.Empty(entry.Errors);
|
|
}
|
|
|
|
private static IModelValidatorProvider CreateValidatorProvider()
|
|
{
|
|
return TestModelValidatorProvider.CreateDefaultProvider();
|
|
}
|
|
|
|
private static DefaultObjectValidator CreateValidator(Type excludedType)
|
|
{
|
|
var excludeFilters = new List<ValidationExcludeFilter>();
|
|
if (excludedType != null)
|
|
{
|
|
excludeFilters.Add(new ValidationExcludeFilter(excludedType));
|
|
}
|
|
|
|
var provider = TestModelMetadataProvider.CreateDefaultProvider(excludeFilters.ToArray());
|
|
return new DefaultObjectValidator(provider);
|
|
}
|
|
|
|
private static DefaultObjectValidator CreateValidator(params IMetadataDetailsProvider[] providers)
|
|
{
|
|
var provider = TestModelMetadataProvider.CreateDefaultProvider(providers);
|
|
return new DefaultObjectValidator(provider);
|
|
}
|
|
|
|
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)
|
|
{
|
|
Assert.Equal<string>(keys.OrderBy(k => k).ToArray(), modelState.Keys.OrderBy(k => k).ToArray());
|
|
}
|
|
|
|
private class ThrowingProperty
|
|
{
|
|
public string WatchOut
|
|
{
|
|
get
|
|
{
|
|
throw new InvalidTimeZoneException();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class Person
|
|
{
|
|
[Required, StringLength(10)]
|
|
public string Name { get; set; }
|
|
|
|
[Required]
|
|
public string Profession { get; set; }
|
|
|
|
public Person Friend { get; set; }
|
|
}
|
|
|
|
private class Person2
|
|
{
|
|
public string Name { get; set; }
|
|
public Address Address { get; set; }
|
|
}
|
|
|
|
private class Address
|
|
{
|
|
[StringLength(5)]
|
|
[RegularExpression("hehehe")]
|
|
public string Street { get; set; }
|
|
}
|
|
|
|
private struct ValueType
|
|
{
|
|
public int Value { get; set; }
|
|
public string Reference { get; set; }
|
|
}
|
|
|
|
private class ReferenceType
|
|
{
|
|
public int Value { get; set; }
|
|
public string Reference { get; set; }
|
|
}
|
|
|
|
private class ValidatableModel : IValidatableObject
|
|
{
|
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
yield return new ValidationResult("Error1", new string[] { });
|
|
yield return new ValidationResult("Error2", new[] { "Property1" });
|
|
yield return new ValidationResult("Error3", new[] { "Property2", "Property3" });
|
|
}
|
|
}
|
|
|
|
private class TypeThatOverridesEquals
|
|
{
|
|
[StringLength(2)]
|
|
public string Funny { get; set; }
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
private class VariableTest
|
|
{
|
|
[Range(15, 25)]
|
|
public int test;
|
|
}
|
|
|
|
private class User : IValidatableObject
|
|
{
|
|
public string Password { get; set; }
|
|
|
|
[Compare("Password")]
|
|
public string ConfirmPassword { get; set; }
|
|
|
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (Password == "password")
|
|
{
|
|
yield return new ValidationResult("Password does not meet complexity requirements.");
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface IExampleService
|
|
{
|
|
void DoSomething();
|
|
}
|
|
}
|
|
}
|