// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using Microsoft.Web.UnitTestUtil; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelBinderProviderCollectionTest { [Fact] public void ListWrappingConstructor() { // Arrange ModelBinderProvider[] providers = new[] { new Mock().Object, new Mock().Object }; // Act ModelBinderProviderCollection collection = new ModelBinderProviderCollection(providers); // Assert Assert.Equal(providers, collection.ToArray()); } [Fact] public void DefaultConstructor() { // Act ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Assert Assert.Empty(collection); } [Fact] public void AddNullProviderThrows() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & Assert Assert.ThrowsArgumentNull( delegate { collection.Add(null); }, "item"); } [Fact] public void RegisterBinderForGenericType_Factory() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), _ => mockBinder); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForGenericType_Instance() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), mockBinder); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForGenericType_Type() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), typeof(CollectionModelBinder<>)); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Factory() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForType(typeof(int), () => mockBinder); // Assert var simpleProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(int), simpleProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Instance() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForType(typeof(int), mockBinder); // Assert var simpleProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(int), simpleProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Instance_InsertsNewProviderBehindFrontOfListProviders() { // Arrange ModelBinderProvider frontOfListProvider = new ProviderAtFront(); IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { frontOfListProvider }; // Act collection.RegisterBinderForType(typeof(int), mockBinder); // Assert Assert.Equal( new[] { typeof(ProviderAtFront), typeof(SimpleModelBinderProvider) }, collection.Select(o => o.GetType()).ToArray()); } [Fact] public void SetItem() { // Arrange ModelBinderProvider provider0 = new Mock().Object; ModelBinderProvider provider1 = new Mock().Object; ModelBinderProvider provider2 = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); collection.Add(provider0); collection.Add(provider1); // Act collection[1] = provider2; // Assert Assert.Equal(new[] { provider0, provider2 }, collection.ToArray()); } [Fact] public void SetNullProviderThrows() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); collection.Add(new Mock().Object); // Act & Assert Assert.ThrowsArgumentNull( delegate { collection[0] = null; }, "item"); } [Fact] public void GetBinder_FromAttribute_BadAttribute_Throws() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_BadAttribute)) }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); // Act & assert Assert.Throws( delegate { providers.GetBinder(controllerContext, bindingContext); }, @"The type 'System.Object' does not subclass Microsoft.Web.Mvc.ModelBinding.ModelBinderProvider or implement the interface Microsoft.Web.Mvc.ModelBinding.IExtensibleModelBinder."); } [Fact] public void GetBinder_FromAttribute_Binder_Generic_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder_Generic)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "fooValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder_Generic), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_FromAttribute_Binder_SuppressPrefixCheck_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder_SuppressPrefix)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "bar", "barValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder_SuppressPrefix), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinder_FromAttribute_Binder_ValueNotPresent_ReturnsNull() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "bar", "barValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_FromAttribute_Binder_ValuePresent_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "fooValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinder_FromAttribute_Provider_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Provider)) }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Provider), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinderReturnsFirstBinderFromProviders() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)) }; IExtensibleModelBinder expectedBinder = new Mock().Object; Mock mockProvider = new Mock(); mockProvider.Setup(p => p.GetBinder(controllerContext, bindingContext)).Returns(expectedBinder); ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, mockProvider.Object, new Mock().Object }); // Act IExtensibleModelBinder returned = collection.GetBinder(controllerContext, bindingContext); // Assert Assert.Equal(expectedBinder, returned); } [Fact] public void GetBinderReturnsNullIfNoProviderMatches() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, }); // Act IExtensibleModelBinder returned = collection.GetBinder(controllerContext, bindingContext); // Assert Assert.Null(returned); } [Fact] public void GetBinderThrowsIfBindingContextIsNull() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.ThrowsArgumentNull( delegate { collection.GetBinder(new ControllerContext(), null); }, "bindingContext"); } [Fact] public void GetBinderThrowsIfControllerContextIsNull() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.ThrowsArgumentNull( delegate { collection.GetBinder(null, new ExtensibleModelBindingContext()); }, "controllerContext"); } [Fact] public void GetBinderThrowsIfModelTypeHasBindAttribute() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithBindAttribute)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.Throws( delegate { collection.GetBinder(controllerContext, bindingContext); }, @"The model of type 'Microsoft.Web.Mvc.ModelBinding.Test.ModelBinderProviderCollectionTest+ModelWithBindAttribute' has a [Bind] attribute. The new model binding system cannot be used with models that have type-level [Bind] attributes. Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead."); } [Fact] public void GetRequiredBinderThrowsIfNoProviderMatches() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, }); // Act & assert Assert.Throws( delegate { collection.GetRequiredBinder(controllerContext, bindingContext); }, @"A binder for type System.Int32 could not be located."); } [MetadataType(typeof(ModelWithBindAttribute_Buddy))] private class ModelWithBindAttribute { [Bind] private class ModelWithBindAttribute_Buddy { } } [ModelBinderProviderOptions(FrontOfList = true)] private class ProviderAtFront : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } [ExtensibleModelBinder(typeof(object))] private class ModelWithProviderAttribute_BadAttribute { } [ExtensibleModelBinder(typeof(CustomBinder))] private class ModelWithProviderAttribute_Binder { } [ExtensibleModelBinder(typeof(CustomGenericBinder<>))] private class ModelWithProviderAttribute_Binder_Generic { } [ExtensibleModelBinder(typeof(CustomBinder), SuppressPrefixCheck = true)] private class ModelWithProviderAttribute_Binder_SuppressPrefix { } [ExtensibleModelBinder(typeof(CustomProvider))] private class ModelWithProviderAttribute_Provider { } private class CustomProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return new CustomBinder(); } } private class CustomBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } private class CustomGenericBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } } }