diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs new file mode 100644 index 0000000000..fa25f866bc --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs @@ -0,0 +1,473 @@ +// 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. + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Mvc.ModelBinding; +using Xunit; + +namespace Microsoft.AspNet.Mvc.IntegrationTests +{ + public class ValidationIntegrationTests + { + private class Order1 + { + [Required] + public string CustomerName { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnSimpleTypeProperty_WithData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order1) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter.CustomerName=bill"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal("bill", model.CustomerName); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter.CustomerName").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + Assert.Empty(entry.Errors); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnSimpleTypeProperty_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order1) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Null(model.CustomerName); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(1, modelState.ErrorCount); + Assert.False(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "CustomerName").Value; + Assert.Null(entry.Value); + Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); + + var error = Assert.Single(entry.Errors); + AssertRequiredError("CustomerName", error); + } + + + + private class Order2 + { + [Required] + public Person2 Customer { get; set; } + } + + private class Person2 + { + public string Name { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnPOCOProperty_WithData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order2) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter.Customer.Name=bill"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.NotNull(model.Customer); + Assert.Equal("bill", model.Customer.Name); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + Assert.Empty(entry.Errors); + } + + [Fact] + public async Task Validation_RequiredAttribute_OnPOCOProperty_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order2) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Null(model.Customer); + + Assert.Equal(1, modelState.Count); + Assert.Equal(1, modelState.ErrorCount); + Assert.False(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "Customer").Value; + Assert.Null(entry.Value); + Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); + + var error = Assert.Single(entry.Errors); + AssertRequiredError("Customer", error); + } + + private class Order3 + { + public Person3 Customer { get; set; } + } + + private class Person3 + { + public int Age { get; set; } + + [Required] + public string Name { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnNestedSimpleTypeProperty_WithData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order3) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter.Customer.Name=bill"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.NotNull(model.Customer); + Assert.Equal("bill", model.Customer.Name); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + Assert.Empty(entry.Errors); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnNestedSimpleTypeProperty_NoDataForRequiredProperty() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order3) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + // Force creation of the Customer model. + request.QueryString = new QueryString("?parameter.Customer.Age=17"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.NotNull(model.Customer); + Assert.Equal(17, model.Customer.Age); + Assert.Null(model.Customer.Name); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(1, modelState.ErrorCount); + Assert.False(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; + Assert.Null(entry.Value); + Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); + + var error = Assert.Single(entry.Errors); + AssertRequiredError("Name", error); + } + + private class Order4 + { + [Required] + public List Items { get; set; } + } + + private class Item4 + { + public int ItemId { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnCollectionProperty_WithData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order4) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?Items[0].ItemId=17"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.NotNull(model.Items); + Assert.Equal(17, Assert.Single(model.Items).ItemId); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "Items[0].ItemId").Value; + Assert.Equal("17", entry.Value.AttemptedValue); + Assert.Equal("17", entry.Value.RawValue); + Assert.Empty(entry.Errors); + } + + [Fact] + public async Task Validation_RequiredAttribute_OnCollectionProperty_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Order4) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + // Force creation of the Customer model. + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Null(model.Items); + + Assert.Equal(1, modelState.Count); + Assert.Equal(1, modelState.ErrorCount); + Assert.False(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "Items").Value; + Assert.Null(entry.Value); + Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); + + var error = Assert.Single(entry.Errors); + AssertRequiredError("Items", error); + } + + private class Order5 + { + [Required] + public int? ProductId { get; set; } + + public string Name { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task Validation_RequiredAttribute_OnPOCOPropertyOfBoundElement_WithData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(List) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter[0].ProductId=17"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(17, Assert.Single(model).ProductId); + + Assert.Equal(1, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter[0].ProductId").Value; + Assert.Equal("17", entry.Value.AttemptedValue); + Assert.Equal("17", entry.Value.RawValue); + Assert.Empty(entry.Errors); + } + + [Fact] + public async Task Validation_RequiredAttribute_OnPOCOPropertyOfBoundElement_NoDataForRequiredProperty() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(List) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + // Force creation of the Customer model. + request.QueryString = new QueryString("?parameter[0].Name=bill"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + var item = Assert.Single(model); + Assert.Null(item.ProductId); + Assert.Equal("bill", item.Name); + + Assert.Equal(2, modelState.Count); + Assert.Equal(1, modelState.ErrorCount); + Assert.False(modelState.IsValid); + + var entry = Assert.Single(modelState, e => e.Key == "parameter[0].ProductId").Value; + Assert.Null(entry.Value); + Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); + + var error = Assert.Single(entry.Errors); + AssertRequiredError("ProductId", error); + } + + private static void AssertRequiredError(string key, ModelError error) + { + Assert.Equal(string.Format("The {0} field is required.", key), error.ErrorMessage); + Assert.Null(error.Exception); + } + } +}