// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web.Mvc; using Microsoft.Web.UnitTestUtil; using Moq; using Xunit; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class CollectionModelBinderTest { [Fact] public void BindComplexCollectionFromIndexes_FiniteIndexes() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[foo]", "42" }, { "someName[baz]", "200" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindComplexCollectionFromIndexes(controllerContext, bindingContext, new[] { "foo", "bar", "baz" }); // Assert Assert.Equal(new[] { 42, 0, 200 }, boundCollection.ToArray()); Assert.Equal(new[] { "someName[foo]", "someName[baz]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray()); } [Fact] public void BindComplexCollectionFromIndexes_InfiniteIndexes() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "100" }, { "someName[3]", "400" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindComplexCollectionFromIndexes(controllerContext, bindingContext, null /* indexNames */); // Assert Assert.Equal(new[] { 42, 100 }, boundCollection.ToArray()); Assert.Equal(new[] { "someName[0]", "someName[1]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray()); } [Fact] public void BindModel_ComplexCollection() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName.index", new[] { "foo", "bar", "baz" } }, { "someName[foo]", "42" }, { "someName[bar]", "100" }, { "someName[baz]", "200" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); CollectionModelBinder modelBinder = new CollectionModelBinder(); // Act bool retVal = modelBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Equal(new[] { 42, 100, 200 }, ((List)bindingContext.Model).ToArray()); } [Fact] public void BindModel_SimpleCollection() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName", new[] { "42", "100", "200" } } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); CollectionModelBinder modelBinder = new CollectionModelBinder(); // Act bool retVal = modelBinder.BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); Assert.Equal(new[] { 42, 100, 200 }, ((List)bindingContext.Model).ToArray()); } [Fact] public void BindSimpleCollection_RawValueIsEmptyCollection_ReturnsEmptyList() { // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(null, null, new object[0], null); // Assert Assert.NotNull(boundCollection); Assert.Empty(boundCollection); } [Fact] public void BindSimpleCollection_RawValueIsNull_ReturnsNull() { // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(null, null, null, null); // Assert Assert.Null(boundCollection); } [Fact] public void BindSimpleCollection_SubBinderDoesNotExist() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(controllerContext, bindingContext, new int[1], culture); // Assert Assert.Equal(new[] { 0 }, boundCollection.ToArray()); Assert.Empty(bindingContext.ValidationNode.ChildNodes); } [Fact] public void BindSimpleCollection_SubBindingSucceeds() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; ModelValidationNode childValidationNode = null; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal("someName", mbc.ModelName); childValidationNode = mbc.ValidationNode; mbc.Model = 42; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(controllerContext, bindingContext, new int[1], culture); // Assert Assert.Equal(new[] { 42 }, boundCollection.ToArray()); Assert.Equal(new[] { childValidationNode }, bindingContext.ValidationNode.ChildNodes.ToArray()); } } }