public async Task AllComponentsProvider_BindsSuccessfully(Type modelType)
        {
            // Arrange
            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
                {
                    { "TheModelName.Day", "1" },
                    { "TheModelName.Month", "4" },
                    { "TheModelName.Year", "2020" }
                }
            };

            var modelBinder = new DateInputModelBinder();

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.True(bindingContext.Result.IsModelSet);
            Assert.IsType <Date>(bindingContext.Result.Model);

            Date date = (Date)bindingContext.Result.Model;

            Assert.Equal(2020, date.Year);
            Assert.Equal(4, date.Month);
            Assert.Equal(1, date.Day);
        }
        public async Task BindModelAsync_AllComponentsEmpty_DoesNotBind()
        {
            // Arrange
            var modelType = typeof(Date);

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ActionContext = CreateActionContextWithServices(),
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
            };

            var converterMock = new Mock <DateInputModelConverter>();

            converterMock.Setup(mock => mock.CanConvertModelType(modelType)).Returns(true);

            var modelBinder = new DateInputModelBinder(converterMock.Object);

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.False(bindingContext.Result.IsModelSet);
        }
        public async Task BindModelAsync_MissingOrInvalidComponentsAndConverterCanCreateModelFromErrors_PassesValuesToConverterAndBindsResult()
        {
            // Arrange
            var modelType = typeof(CustomDateType);

            var day   = "1";
            var month = "4";
            var year  = "-1";

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ActionContext = CreateActionContextWithServices(),
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
                {
                    { "TheModelName.Day", day },
                    { "TheModelName.Month", month },
                    { "TheModelName.Year", year }
                }
            };

            var    parseErrors     = DateInputParseErrors.InvalidYear;
            object modelFromErrors = new CustomDateType()
            {
                ParseErrors = parseErrors
            };

            var converterMock = new Mock <DateInputModelConverter>();

            converterMock.Setup(mock => mock.CanConvertModelType(modelType)).Returns(true);

            converterMock
            .Setup(mock => mock.TryCreateModelFromErrors(modelType, parseErrors, out modelFromErrors))
            .Returns(true)
            .Verifiable();

            var modelBinder = new DateInputModelBinder(converterMock.Object);

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            converterMock.Verify();

            Assert.True(bindingContext.Result.IsModelSet);

            Assert.Same(modelFromErrors, bindingContext.Result.Model);

            Assert.Equal(day, bindingContext.ModelState["TheModelName.Day"].AttemptedValue);
            Assert.Equal(month, bindingContext.ModelState["TheModelName.Month"].AttemptedValue);
            Assert.Equal(year, bindingContext.ModelState["TheModelName.Year"].AttemptedValue);

            Assert.Equal(0, bindingContext.ModelState.ErrorCount);
        }
        public async Task BindModelAsync_AllComponentsProvided_PassesValuesToConverterAndBindsResult()
        {
            // Arrange
            var modelType = typeof(Date);

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ActionContext = CreateActionContextWithServices(),
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
                {
                    { "TheModelName.Day", "1" },
                    { "TheModelName.Month", "4" },
                    { "TheModelName.Year", "2020" }
                }
            };

            var converterMock = new Mock <DateInputModelConverter>();

            converterMock.Setup(mock => mock.CanConvertModelType(modelType)).Returns(true);

            converterMock
            .Setup(mock => mock.CreateModelFromDate(modelType, new Date(2020, 4, 1)))
            .Returns(new Date(2020, 4, 1))
            .Verifiable();

            var modelBinder = new DateInputModelBinder(converterMock.Object);

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            converterMock.Verify();

            Assert.True(bindingContext.Result.IsModelSet);

            var date = Assert.IsType <Date>(bindingContext.Result.Model);

            Assert.Equal(2020, date.Year);
            Assert.Equal(4, date.Month);
            Assert.Equal(1, date.Day);

            Assert.Null(bindingContext.ModelState["TheModelName.Day"]?.AttemptedValue);
            Assert.Null(bindingContext.ModelState["TheModelName.Month"]?.AttemptedValue);
            Assert.Null(bindingContext.ModelState["TheModelName.Year"]?.AttemptedValue);

            Assert.Equal(0, bindingContext.ModelState.ErrorCount);
        }
        public async Task BindModelAsync_MissingOrInvalidComponents_FailsBinding(string day, string month, string year)
        {
            // Arrange
            var modelType = typeof(Date);

            var valueProvider = new SimpleValueProvider();

            if (day != null)
            {
                valueProvider.Add("TheModelName.Day", day);
            }

            if (month != null)
            {
                valueProvider.Add("TheModelName.Month", month);
            }

            if (year != null)
            {
                valueProvider.Add("TheModelName.Year", year);
            }

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ActionContext = CreateActionContextWithServices(),
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = valueProvider
            };

            var converterMock = new Mock <DateInputModelConverter>();

            converterMock.Setup(mock => mock.CanConvertModelType(modelType)).Returns(true);

            var modelBinder = new DateInputModelBinder(converterMock.Object);

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result);

            Assert.Equal(day, bindingContext.ModelState["TheModelName.Day"].AttemptedValue);
            Assert.Equal(month, bindingContext.ModelState["TheModelName.Month"].AttemptedValue);
            Assert.Equal(year, bindingContext.ModelState["TheModelName.Year"].AttemptedValue);
        }
        public async Task NonNullableModelTypeAllComponentsEmpty_FailsBinding()
        {
            // Arrange
            var modelType = typeof(Date);

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
            };

            var modelBinder = new DateInputModelBinder();

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result);
            Assert.Null(bindingContext.ModelState["TheModelName"]?.Errors);
        }
        public async Task NullableModelTypeAllComponentsEmpty_BindsNull()
        {
            // Arrange
            var modelType = typeof(Date?);

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = new SimpleValueProvider()
            };

            var modelBinder = new DateInputModelBinder();

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.True(bindingContext.Result.IsModelSet);
            Assert.Null(bindingContext.Result.Model);
        }
        public async Task MissingOrInvalidComponents_FailsBinding(
            string day,
            string month,
            string year,
            string expectedDayModelError,
            string expectedMonthModelError,
            string expectedYearModelError)
        {
            // Arrange
            var modelType = typeof(Date);

            var valueProvider = new SimpleValueProvider();

            if (day != null)
            {
                valueProvider.Add("TheModelName.Day", day);
            }

            if (month != null)
            {
                valueProvider.Add("TheModelName.Month", month);
            }

            if (year != null)
            {
                valueProvider.Add("TheModelName.Year", year);
            }

            ModelBindingContext bindingContext = new DefaultModelBindingContext()
            {
                ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
                ModelName     = "TheModelName",
                ModelState    = new ModelStateDictionary(),
                ValueProvider = valueProvider
            };

            var modelBinder = new DateInputModelBinder();

            // Act
            await modelBinder.BindModelAsync(bindingContext);

            // Assert
            Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result);

            var topLevelError = Assert.Single(bindingContext.ModelState["TheModelName"].Errors);

            Assert.Equal("Invalid date specified.", topLevelError.Exception.Message);

            if (expectedDayModelError != null)
            {
                var dayError = Assert.Single(bindingContext.ModelState["TheModelName.Day"].Errors);
                Assert.Equal(expectedDayModelError, dayError.Exception.Message);
            }

            if (expectedMonthModelError != null)
            {
                var monthError = Assert.Single(bindingContext.ModelState["TheModelName.Month"].Errors);
                Assert.Equal(expectedMonthModelError, monthError.Exception.Message);
            }

            if (expectedYearModelError != null)
            {
                var yearError = Assert.Single(bindingContext.ModelState["TheModelName.Year"].Errors);
                Assert.Equal(expectedYearModelError, yearError.Exception.Message);
            }

            Assert.Equal(day, bindingContext.ModelState["TheModelName.Day"].AttemptedValue);
            Assert.Equal(month, bindingContext.ModelState["TheModelName.Month"].AttemptedValue);
            Assert.Equal(year, bindingContext.ModelState["TheModelName.Year"].AttemptedValue);
        }