// 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; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.OptionDescriptors; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc { public class InputObjectBindingTests { [Fact] public async Task GetArguments_UsingInputFormatter_DeserializesWithoutErrors_WhenValidationAttributesAreAbsent() { // Arrange var sampleName = "SampleName"; var input = "" + "" + sampleName + ""; var modelStateDictionary = new ModelStateDictionary(); var invoker = GetControllerActionInvoker( input, typeof(Person), new XmlSerializerInputFormatter(), "application/xml"); // Act var result = await invoker.GetActionArguments(modelStateDictionary); // Assert Assert.True(modelStateDictionary.IsValid); Assert.Equal(0, modelStateDictionary.ErrorCount); var model = result["foo"] as Person; Assert.Equal(sampleName, model.Name); } [Fact] public async Task GetArguments_UsingInputFormatter_DeserializesWithValidationError() { // Arrange var sampleName = "SampleName"; var sampleUserName = "No5"; var input = "" + "" + sampleName + "" + sampleUserName + ""; var modelStateDictionary = new ModelStateDictionary(); var invoker = GetControllerActionInvoker(input, typeof(User), new XmlSerializerInputFormatter(), "application/xml"); // Act var result = await invoker.GetActionArguments(modelStateDictionary); // Assert Assert.False(modelStateDictionary.IsValid); Assert.Equal(1, modelStateDictionary.ErrorCount); Assert.Equal( ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"), Assert.Single(Assert.Single(modelStateDictionary.Values).Errors).ErrorMessage); var model = result["foo"] as User; Assert.Equal(sampleName, model.Name); Assert.Equal(sampleUserName, model.UserName); } [Fact] public async Task GetArguments_UsingInputFormatter_DeserializesArrays() { // Arrange var sampleFirstUser = "FirstUser"; var sampleFirstUserName = "fuser"; var sampleSecondUser = "SecondUser"; var sampleSecondUserName = "suser"; var input = "{'Users': [{Name : '" + sampleFirstUser + "', UserName: '" + sampleFirstUserName + "'}, {Name: '" + sampleSecondUser + "', UserName: '" + sampleSecondUserName + "'}]}"; var modelStateDictionary = new ModelStateDictionary(); var invoker = GetControllerActionInvoker(input, typeof(Customers), new JsonInputFormatter(), "application/xml"); // Act var result = await invoker.GetActionArguments(modelStateDictionary); // Assert Assert.True(modelStateDictionary.IsValid); Assert.Equal(0, modelStateDictionary.ErrorCount); var model = result["foo"] as Customers; Assert.Equal(2, model.Users.Count); Assert.Equal(sampleFirstUser, model.Users[0].Name); Assert.Equal(sampleFirstUserName, model.Users[0].UserName); Assert.Equal(sampleSecondUser, model.Users[1].Name); Assert.Equal(sampleSecondUserName, model.Users[1].UserName); } [Fact] public async Task GetArguments_UsingInputFormatter_DeserializesArrays_WithErrors() { // Arrange var sampleFirstUser = "FirstUser"; var sampleFirstUserName = "fusr"; var sampleSecondUser = "SecondUser"; var sampleSecondUserName = "susr"; var input = "{'Users': [{Name : '" + sampleFirstUser + "', UserName: '" + sampleFirstUserName + "'}, {Name: '" + sampleSecondUser + "', UserName: '" + sampleSecondUserName + "'}]}"; var modelStateDictionary = new ModelStateDictionary(); var invoker = GetControllerActionInvoker(input, typeof(Customers), new JsonInputFormatter(), "application/xml"); // Act var result = await invoker.GetActionArguments(modelStateDictionary); // Assert Assert.False(modelStateDictionary.IsValid); Assert.Equal(2, modelStateDictionary.ErrorCount); var model = result["foo"] as Customers; Assert.Equal( ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"), modelStateDictionary["foo.Users[0].UserName"].Errors[0].ErrorMessage); Assert.Equal( ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"), modelStateDictionary["foo.Users[1].UserName"].Errors[0].ErrorMessage); Assert.Equal(2, model.Users.Count); Assert.Equal(sampleFirstUser, model.Users[0].Name); Assert.Equal(sampleFirstUserName, model.Users[0].UserName); Assert.Equal(sampleSecondUser, model.Users[1].Name); Assert.Equal(sampleSecondUserName, model.Users[1].UserName); } private static ControllerActionInvoker GetControllerActionInvoker( string input, Type parameterType, IInputFormatter selectedFormatter, string contentType) { var mvcOptions = new MvcOptions(); var setup = new MvcOptionsSetup(); setup.Configure(mvcOptions); var accessor = new Mock>(); accessor.SetupGet(a => a.Options) .Returns(mvcOptions); var validatorProvider = new DefaultModelValidatorProviderProvider( accessor.Object, Mock.Of(), Mock.Of()); Func method = x => 1; var actionDescriptor = new ControllerActionDescriptor { MethodInfo = method.Method, Parameters = new List { new ParameterDescriptor { Name = "foo", BodyParameterInfo = new BodyParameterInfo(parameterType) } } }; var metadataProvider = new EmptyModelMetadataProvider(); var actionContext = GetActionContext( Encodings.UTF8EncodingWithoutBOM.GetBytes(input), actionDescriptor, contentType); var inputFormatterSelector = new Mock(); inputFormatterSelector.Setup(a => a.SelectFormatter(It.IsAny())) .Returns(selectedFormatter); var bindingContext = new ActionBindingContext(actionContext, metadataProvider, Mock.Of(), Mock.Of(), inputFormatterSelector.Object, new CompositeModelValidatorProvider(validatorProvider)); var actionBindingContextProvider = new Mock(); actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny())) .Returns(Task.FromResult(bindingContext)); var inputFormattersProvider = new Mock(); inputFormattersProvider.SetupGet(o => o.InputFormatters) .Returns(new List()); return new ControllerActionInvoker(actionContext, actionBindingContextProvider.Object, Mock.Of>(), Mock.Of(), actionDescriptor, inputFormattersProvider.Object, new DefaultBodyModelValidator()); } private static ActionContext GetActionContext(byte[] contentBytes, ActionDescriptor actionDescriptor, string contentType) { return new ActionContext(GetHttpContext(contentBytes, contentType), new RouteData(), actionDescriptor); } private static HttpContext GetHttpContext(byte[] contentBytes, string contentType) { var request = new Mock(); var headers = new Mock(); request.SetupGet(r => r.Headers).Returns(headers.Object); request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes)); request.SetupGet(f => f.ContentType).Returns(contentType); var httpContext = new Mock(); httpContext.SetupGet(c => c.Request).Returns(request.Object); httpContext.SetupGet(c => c.Request).Returns(request.Object); return httpContext.Object; } } public class Person { public string Name { get; set; } } public class User : Person { [MinLength(5)] public string UserName { get; set; } } public class Customers { [Required] public List Users { get; set; } } }