context: // 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.
  
  #if DNX451
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.DataAnnotations;
  using System.Threading.Tasks;
  using Microsoft.AspNet.Mvc.ModelBinding.Validation;
  using Moq;
  using Xunit;
  
  namespace Microsoft.AspNet.Mvc.ModelBinding.Test
  {
      public class CompositeModelBinderTest
      {
          [Fact]
          public async Task BindModel_SuccessfulBind_ReturnsModel()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = true,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var mockIntBinder = new Mock<IModelBinder>();
              mockIntBinder
                  .Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Returns(
                      delegate (ModelBindingContext context)
                      {
                          Assert.Same(bindingContext.ModelMetadata, context.ModelMetadata);
                          Assert.Equal("someName", context.ModelName);
                          Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
  
  
                          return Task.FromResult(
                              new ModelBindingResult(model: 42, key: "someName", isModelSet: true));
                      });
              var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
  
              // Act
              var result = await shimBinder.BindModelAsync(bindingContext);
  
              // Assert
              Assert.NotNull(result);
              Assert.True(result.IsModelSet);
              Assert.Equal(42, result.Model);
          }
  
          [Fact]
          public async Task BindModel_SuccessfulBind_ComplexTypeFallback_ReturnsModel()
          {
              // Arrange
              var expectedModel = new List<int> { 1, 2, 3, 4, 5 };
  
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = true,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var mockIntBinder = new Mock<IModelBinder>();
              mockIntBinder
                  .Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Returns(
                      delegate (ModelBindingContext mbc)
                      {
                          if (!string.IsNullOrEmpty(mbc.ModelName))
                          {
                              return Task.FromResult<ModelBindingResult>(null);
                          }
  
                          Assert.Same(bindingContext.ModelMetadata, mbc.ModelMetadata);
                          Assert.Equal("", mbc.ModelName);
                          Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider);
  
                          return Task.FromResult(new ModelBindingResult(expectedModel, string.Empty, true));
                      });
  
              var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
  
              // Act
              var result = await shimBinder.BindModelAsync(bindingContext);
  
              // Assert
              Assert.NotNull(result);
              Assert.True(result.IsModelSet);
              Assert.Equal(string.Empty, result.Key);
              Assert.Equal(expectedModel, result.Model);
          }
  
          [Fact]
          public async Task ModelBinder_ReturnsNull_IfBinderMatchesButDoesNotSetModel()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = true,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var modelBinder = new Mock<IModelBinder>();
              modelBinder
                  .Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Returns(Task.FromResult(new ModelBindingResult(model: null, key: "someName", isModelSet: false)));
  
              var composite = CreateCompositeBinder(modelBinder.Object);
  
              // Act
              var result = await composite.BindModelAsync(bindingContext);
  
              // Assert
              Assert.Null(result);
          }
  
          [Fact]
          public async Task ModelBinder_FallsBackToEmpty_IfBinderMatchesButDoesNotSetModel()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = true,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var count = 0;
              var modelBinder = new Mock<IModelBinder>();
              modelBinder
                  .Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Callback<ModelBindingContext>(context =>
                  {
                      // Expect two calls; the second with empty ModelName.
                      Assert.InRange(count, 0, 1);
                      count++;
                      if (count == 1)
                      {
                          Assert.Equal("someName", context.ModelName);
                      }
                      else
                      {
                          Assert.Empty(context.ModelName);
                      }
                  })
                  .Returns(Task.FromResult(new ModelBindingResult(model: null, key: "someName", isModelSet: false)));
  
              var composite = CreateCompositeBinder(modelBinder.Object);
  
              // Act & Assert
              var result = await composite.BindModelAsync(bindingContext);
          }
  
          [Fact]
          public async Task ModelBinder_DoesNotFallBackToEmpty_IfFallbackToEmptyPrefixFalse()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = false,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var modelBinder = new Mock<IModelBinder>();
              modelBinder
                  .Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Callback<ModelBindingContext>(context =>
                  {
                      Assert.Equal("someName", context.ModelName);
                  })
                  .Returns(Task.FromResult(new ModelBindingResult(model: null, key: "someName", isModelSet: false)))
                  .Verifiable();
  
              var composite = CreateCompositeBinder(modelBinder.Object);
  
              // Act & Assert
              var result = await composite.BindModelAsync(bindingContext);
              modelBinder.Verify(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()), Times.Once);
          }
  
          [Fact]
          public async Task ModelBinder_DoesNotFallBackToEmpty_IfErrorsAreAdded()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = false,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var modelBinder = new Mock<IModelBinder>();
              modelBinder
                  .Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Callback<ModelBindingContext>(context =>
                  {
                      Assert.Equal("someName", context.ModelName);
                      context.ModelState.AddModelError(context.ModelName, "this is an error message");
                  })
                  .Returns(Task.FromResult(new ModelBindingResult(model: null, key: "someName", isModelSet: false)))
                  .Verifiable();
  
              var composite = CreateCompositeBinder(modelBinder.Object);
  
              // Act & Assert
              var result = await composite.BindModelAsync(bindingContext);
              modelBinder.Verify(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()), Times.Once);
          }
  
          [Fact]
          public async Task ModelBinder_ReturnsNotNull_SetsNullValue_SetsModelStateKey()
          {
              // Arrange
              var bindingContext = new ModelBindingContext
              {
                  FallbackToEmptyPrefix = true,
                  ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
                  ModelName = "someName",
                  ModelState = new ModelStateDictionary(),
                  ValueProvider = new SimpleHttpValueProvider
                  {
                      { "someOtherName", "dummyValue" }
                  },
                  OperationBindingContext = new OperationBindingContext
                  {
                      ValidatorProvider = GetValidatorProvider()
                  }
              };
  
              var modelBinder = new Mock<IModelBinder>();
              modelBinder
                  .Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Returns(Task.FromResult(new ModelBindingResult(model: null, key: "someName", isModelSet: true)));
  
  
gt:             var composite = CreateCompositeBinder(modelBinder.Object);