public async Task CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndNotIsFirstChanceBinding(
            bool isTopLevelObject,
            bool isFirstChanceBinding,
            string binderModelName,
            string modelName,
            bool expectedCanCreate)
        {
            var mockValueProvider = new Mock<IValueProvider>();
            mockValueProvider
                .Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                .Returns(Task.FromResult(false));

            var metadataProvider = new TestModelMetadataProvider();
            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    IsTopLevelObject = isTopLevelObject,
                    IsFirstChanceBinding = isFirstChanceBinding,

                    // Random type.
                    ModelMetadata = metadataProvider.GetMetadataForType(typeof(Person)),
                    ValueProvider = mockValueProvider.Object,
                    OperationBindingContext = new OperationBindingContext
                    {
                        ValueProvider = mockValueProvider.Object,
                        MetadataProvider = metadataProvider,
                        ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    },

                    // CanCreateModel() ignores the BinderModelName and ModelName properties.
                    BinderModelName = binderModelName,
                    ModelName = modelName,
                },
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata =
                mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();

            // Act
            var canCreate = await mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.Equal(expectedCanCreate, canCreate);
        }
        public void SetProperty_PropertyIsReadOnly_DoesNothing()
        {
            // Arrange
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            ModelMetadata         propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty");
            ModelValidationNode   validationNode   = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult        = new ComplexModelDtoResult(null /* model */, validationNode);

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(null, bindingContext, propertyMetadata, dtoResult, requiredValidator: null);

            // Assert
            // If didn't throw, success!
        }
        public void BindModel()
        {
            // Arrange
            ControllerContext             controllerContext = new ControllerContext();
            ExtensibleModelBindingContext bindingContext    = new ExtensibleModelBindingContext
            {
                ModelBinderProviders = new ModelBinderProviderCollection(),
                ModelMetadata        = GetMetadataForObject(new Person()),
                ModelName            = "someName"
            };

            Mock <IExtensibleModelBinder> mockDtoBinder = new Mock <IExtensibleModelBinder>();

            mockDtoBinder
            .Setup(o => o.BindModel(controllerContext, It.IsAny <ExtensibleModelBindingContext>()))
            .Returns(
                delegate(ControllerContext cc, ExtensibleModelBindingContext mbc2)
            {
                return(true);        // just return the DTO unchanged
            });
            bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(ComplexModelDto), mockDtoBinder.Object, true /* suppressPrefixCheck */);

            Mock <TestableMutableObjectModelBinder> mockTestableBinder = new Mock <TestableMutableObjectModelBinder> {
                CallBase = true
            };

            mockTestableBinder.Setup(o => o.EnsureModelPublic(controllerContext, bindingContext)).Verifiable();
            mockTestableBinder.Setup(o => o.GetMetadataForPropertiesPublic(controllerContext, bindingContext)).Returns(new ModelMetadata[0]).Verifiable();
            TestableMutableObjectModelBinder testableBinder = mockTestableBinder.Object;

            testableBinder.MetadataProvider = new DataAnnotationsModelMetadataProvider();

            // Act
            bool retValue = testableBinder.BindModel(controllerContext, bindingContext);

            // Assert
            Assert.True(retValue);
            Assert.IsType <Person>(bindingContext.Model);
            Assert.True(bindingContext.ValidationNode.ValidateAllProperties);
            mockTestableBinder.Verify();
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model             = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = CreateContext(containerMetadata);

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The Age field is required.", modelError.ErrorMessage);

            // Check City error.
            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The City field is required.", modelError.ErrorMessage);
        }
示例#5
0
        public void GetMetadataForProperties_WithoutBindAttribute()
        {
            // Arrange
            string[] expectedPropertyNames = new[] { "DateOfBirth", "DateOfDeath", "ValueTypeRequired", "FirstName", "LastName", "PropertyWithDefaultValue" };

            HttpActionContext   actionContext  = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            IEnumerable <ModelMetadata> propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(actionContext, bindingContext);

            string[] returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
示例#6
0
        public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString()
        {
            // Arrange
            var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));

            bindingContext.ModelName = "foo";
            var propertyMetadata  = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name");
            var validationNode    = new ModelValidationNode(propertyMetadata, "foo.Name");
            var dtoResult         = new ComplexModelDtoResult(model: null, validationNode: validationNode);
            var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            Assert.Equal(false, bindingContext.ModelState.IsValid);
            Assert.Equal(1, bindingContext.ModelState["foo.Name"].Errors.Count);
            Assert.Equal("This message comes from the [Required] attribute.", bindingContext.ModelState["foo.Name"].Errors[0].ErrorMessage);
        }
        public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError()
        {
            // Arrange
            Person model = new Person {
                DateOfBirth = new DateTime(1900, 1, 1)
            };
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(model)
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "DateOfDeath"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                new DateTime(1800, 1, 1),
                validationNode
                );

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                null,
                bindingContext,
                propertyMetadata,
                dtoResult,
                requiredValidator: null
                );

            // Assert
            Assert.Equal(
                "Date of death can't be before date of birth."
                + Environment.NewLine
                + "Parameter name: value",
                bindingContext.ModelState["foo"].Errors[0].Exception.Message
                );
        }
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            Person              model          = new Person();
            HttpActionContext   context        = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(model)
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "DateOfBirth"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                new DateTime(2001, 1, 1),
                validationNode
                );
            ModelValidator requiredValidator = context
                                               .GetValidators(propertyMetadata)
                                               .Where(v => v.IsRequired)
                                               .FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                context,
                bindingContext,
                propertyMetadata,
                dtoResult,
                requiredValidator
                );

            // Assert
            validationNode.Validate(context);
            Assert.True(context.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
        public void ProcessDto_RequiredFieldNull_RaisesModelError()
        {
            // Arrange
            var model             = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = CreateContext(containerMetadata);

            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make Age valid and City invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");

            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(23, new ModelValidationNode(propertyMetadata, "theModel.Age"));
            propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.City"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check City error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The City field is required.", modelError.ErrorMessage);
        }
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback()
        {
            // Arrange
            HttpActionContext   context        = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person()),
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "DateOfBirth"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                null /* model */
                ,
                validationNode
                );
            ModelValidator requiredValidator = context
                                               .GetValidators(propertyMetadata)
                                               .Where(v => v.IsRequired)
                                               .FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                context,
                bindingContext,
                propertyMetadata,
                dtoResult,
                requiredValidator
                );

            // Assert
            Assert.True(context.ModelState.IsValid);
            validationNode.Validate(context, bindingContext.ValidationNode);
            Assert.False(context.ModelState.IsValid);
        }
示例#11
0
        public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue()
        {
            // Arrange
            ControllerContext controllerContext = new ControllerContext
            {
                Controller = new EmptyController()
            };
            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person())
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "PropertyWithDefaultValue"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                null /* model */
                ,
                validationNode
                );

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                controllerContext,
                bindingContext,
                propertyMetadata,
                dtoResult
                );

            // Assert
            var person = Assert.IsType <Person>(bindingContext.Model);

            Assert.Equal(123.456m, person.PropertyWithDefaultValue);
            Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid);
        }
        public async Task CanCreateModel_CreatesModel_ForTopLevelObjectIfThereIsExplicitPrefix(
            Type modelType,
            bool isPrefixProvided)
        {
            var mockValueProvider = new Mock<IValueProvider>();
            mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                             .Returns(Task.FromResult(false));

            var metadataProvider = new TestModelMetadataProvider();
            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    // Random type.
                    ModelMetadata = metadataProvider.GetMetadataForType(typeof(Person)),
                    ValueProvider = mockValueProvider.Object,
                    OperationBindingContext = new OperationBindingContext
                    {
                        ValueProvider = mockValueProvider.Object,
                        MetadataProvider = metadataProvider,
                        ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    },

                    // Setting it to empty ensures that model does not get created becasue of no model name.
                    ModelName = "dummyModelName",
                    BinderModelName = isPrefixProvided ? "prefix" : null,
                }
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
                                                                bindingContext.ModelBindingContext);

            // Act
            var retModel = await mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.Equal(isPrefixProvided, retModel);
        }
        public void CanCreateModel_ReturnsTrue_IfIsTopLevelObject(
            bool isTopLevelObject,
            bool expectedCanCreate)
        {
            var mockValueProvider = new Mock<IValueProvider>();
            mockValueProvider
                .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
                .Returns(false);

            var metadataProvider = new TestModelMetadataProvider();
            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    IsTopLevelObject = isTopLevelObject,

                    // Random type.
                    ModelMetadata = metadataProvider.GetMetadataForType(typeof(Person)),
                    ValueProvider = mockValueProvider.Object,
                    OperationBindingContext = new OperationBindingContext
                    {
                        ValueProvider = mockValueProvider.Object,
                        MetadataProvider = metadataProvider,
                        ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    },
                    ModelState = new ModelStateDictionary(),
                },
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata =
                mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();

            // Act
            var canCreate = mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.Equal(expectedCanCreate, canCreate);
        }
        public void ProcessDto_RequiredFieldNull_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model             = new Person();
            var containerMetadata = GetMetadataForObject(model);

            var bindingContext = CreateContext(containerMetadata);

            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");

            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelErrorWithMessage()
        {
            // Arrange
            Person            model             = new Person();
            ModelMetadata     containerMetadata = GetMetadataForObject(model);
            HttpActionContext context           = ContextUtil.CreateActionContext();

            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName     = "theModel"
            };

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;

            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void ProcessDto_Success()
        {
            // Arrange
            DateTime dob   = new DateTime(2001, 1, 1);
            Person   model = new Person
            {
                DateOfBirth = dob
            };
            ModelMetadata containerMetadata = GetMetadataForObject(model);

            ControllerContext             controllerContext = new ControllerContext();
            ExtensibleModelBindingContext bindingContext    = new ExtensibleModelBindingContext
            {
                ModelMetadata = containerMetadata
            };
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            ModelMetadata firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");

            dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, ""));
            ModelMetadata lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");

            dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, ""));
            ModelMetadata dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");

            dto.Results[dobProperty] = null;

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(controllerContext, bindingContext, dto);

            // Assert
            Assert.Equal("John", model.FirstName);
            Assert.Equal("Doe", model.LastName);
            Assert.Equal(dob, model.DateOfBirth);
            Assert.True(bindingContext.ModelState.IsValid);
        }
示例#17
0
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            Person            model             = new Person();
            ControllerContext controllerContext = new ControllerContext
            {
                Controller = new EmptyController()
            };
            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(model)
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "DateOfBirth"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                new DateTime(2001, 1, 1),
                validationNode
                );

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                controllerContext,
                bindingContext,
                propertyMetadata,
                dtoResult
                );

            // Assert
            validationNode.Validate(controllerContext);
            Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
示例#18
0
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback()
        {
            // Arrange
            ControllerContext controllerContext = new ControllerContext
            {
                Controller = new EmptyController()
            };
            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person()),
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
                o => o.PropertyName == "DateOfBirth"
                );
            ModelValidationNode   validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult      = new ComplexModelDtoResult(
                null /* model */
                ,
                validationNode
                );

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(
                controllerContext,
                bindingContext,
                propertyMetadata,
                dtoResult
                );

            // Assert
            Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid);
            validationNode.Validate(controllerContext, bindingContext.ValidationNode);
            Assert.False(controllerContext.Controller.ViewData.ModelState.IsValid);
        }
示例#19
0
        public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue()
        {
            // Arrange
            var bindingContext = CreateContext(GetMetadataForObject(new Person()));

            var propertyMetadata  = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue");
            var validationNode    = new ModelValidationNode(propertyMetadata, "foo");
            var dtoResult         = new ComplexModelDtoResult(model: null, validationNode: validationNode);
            var requiredValidator = bindingContext.ValidatorProvider
                                    .GetValidators(propertyMetadata)
                                    .FirstOrDefault(v => v.IsRequired);

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            var person = Assert.IsType <Person>(bindingContext.Model);

            Assert.Equal(123.456m, person.PropertyWithDefaultValue);
            Assert.Equal(true, bindingContext.ModelState.IsValid);
        }
        public void EnsureModel_ModelIsNotNull_DoesNothing()
        {
            // Arrange
            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person())
            };

            Mock <TestableMutableObjectModelBinder> mockTestableBinder = new Mock <TestableMutableObjectModelBinder> {
                CallBase = true
            };
            TestableMutableObjectModelBinder testableBinder = mockTestableBinder.Object;

            // Act
            object originalModel = bindingContext.Model;

            testableBinder.EnsureModelPublic(null, bindingContext);
            object newModel = bindingContext.Model;

            // Assert
            Assert.Same(originalModel, newModel);
            mockTestableBinder.Verify(o => o.CreateModelPublic(null, bindingContext), Times.Never());
        }
示例#21
0
        public void GetMetadataForProperties_WithBindAttribute()
        {
            // Arrange
            string[] expectedPropertyNames = new[] { "FirstName", "LastName" };

            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion))
            };

            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            // Act
            IEnumerable <ModelMetadata> propertyMetadatas =
                testableBinder.GetMetadataForPropertiesPublic(null, bindingContext);

            string[] returnedPropertyNames = propertyMetadatas
                                             .Select(o => o.PropertyName)
                                             .ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
示例#22
0
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            var model          = new Person();
            var bindingContext = CreateContext(GetMetadataForObject(model));

            var propertyMetadata  = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
            var validationNode    = new ModelValidationNode(propertyMetadata, "foo");
            var dtoResult         = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode);
            var requiredValidator = bindingContext.ValidatorProvider
                                    .GetValidators(propertyMetadata)
                                    .FirstOrDefault(v => v.IsRequired);
            var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            validationNode.Validate(validationContext);
            Assert.Equal(true, bindingContext.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
示例#23
0
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError()
        {
            // Arrange
            HttpActionContext   context        = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person()),
                ModelName     = "foo"
            };

            ModelMetadata         propertyMetadata  = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired");
            ModelValidationNode   validationNode    = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired");
            ComplexModelDtoResult dtoResult         = new ComplexModelDtoResult(null /* model */, validationNode);
            ModelValidator        requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage);
        }
        public void ProcessDto_BindRequiredFieldNull_RaisesModelError()
        {
            // Arrange
            ModelWithBindRequired model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            ModelMetadata containerMetadata = GetMetadataForObject(model);
            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext()
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                ModelState = context.ModelState,
            };

            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            ModelMetadata propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult("John Doe", new ModelValidationNode(propertyMetadata, "theModel.Name"));

            // Attempt to set non-Nullable property to null.  HttpBindRequiredAttribute should not be relevant in this
            // case because the binding exists.
            propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.Age"));

            // Act; must also Validate because null-check error handler is late-bound
            testableBinder.ProcessDto(context, bindingContext, dto);
            bindingContext.ValidationNode.Validate(context);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value is required.", modelError.ErrorMessage);
        }
        public void SetProperty_PropertyIsReadOnly_DoesNothing()
        {
            // Arrange
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode);

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(null, bindingContext, propertyMetadata, dtoResult, requiredValidator: null);

            // Assert
            // If didn't throw, success!
        }
        public void ProcessResults_NullableValueTypeProperty_NoValueSet_NoError()
        {
            // Arrange
            var model = new NullableValueTypeProperty();
            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = CreateContext(containerMetadata, model);

            var results = containerMetadata.Properties.ToDictionary(
                property => property,
                property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false));

            var testableBinder = new TestableMutableObjectModelBinder();
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessResults(bindingContext, results, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.True(modelStateDictionary.IsValid);
        }
        public void ProcessResults_ValueTypeProperty_TriesToSetNullModel_CapturesException()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var results = containerMetadata.Properties.ToDictionary(
                property => property,
                property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false));
            var testableBinder = new TestableMutableObjectModelBinder();

            // The [DefaultValue] on ValueTypeRequiredWithDefaultValue is ignored by model binding.
            var expectedValue = 0;

            // Make ValueTypeRequired invalid.
            var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
            results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequired));

            // Make ValueTypeRequiredWithDefaultValue invalid
            propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
            results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessResults(bindingContext, results, modelValidationNode);

            // Assert
            Assert.False(modelStateDictionary.IsValid);

            // Check ValueTypeRequired error.
            var modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequired));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequired), modelStateEntry.Key);

            var modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var error = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, error.ErrorMessage);
            Assert.IsType<NullReferenceException>(error.Exception);

            // Check ValueTypeRequiredWithDefaultValue error.
            modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue), modelStateEntry.Key);

            modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            error = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, error.ErrorMessage);
            Assert.IsType<NullReferenceException>(error.Exception);

            Assert.Equal(0, model.ValueTypeRequired);
            Assert.Equal(expectedValue, model.ValueTypeRequiredWithDefaultValue);
        }
        public void ProcessResults_DataMemberIsRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithDataMemberIsRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = new ModelBindingContext
            {
                Model = model,
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                OperationBindingContext = new OperationBindingContext
                {
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>()
                }
            };

            var results = containerMetadata.Properties.ToDictionary(
                property => property,
                property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false));
            var nameProperty = containerMetadata.Properties[nameof(model.Name)];
            results[nameProperty] = new ModelBindingResult("John Doe", isModelSet: true, key: string.Empty);

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessResults(bindingContext, results, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Single(modelStateDictionary);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value for the 'Age' property was not provided.", modelError.ErrorMessage);
        }
        public void ProcessResults_ValueTypePropertyWithBindRequired_SetToNull_CapturesException()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = new ModelBindingContext()
            {
                Model = model,
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                ModelState = new ModelStateDictionary(),
                OperationBindingContext = new OperationBindingContext
                {
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>()
                }
            };

            var results = containerMetadata.Properties.ToDictionary(
                property => property,
                property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false));
            var propertyMetadata = containerMetadata.Properties[nameof(model.Name)];
            results[propertyMetadata] = new ModelBindingResult("John Doe", isModelSet: true, key: "theModel.Name");

            // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
            // case because the binding exists.
            propertyMetadata = containerMetadata.Properties[nameof(model.Age)];
            results[propertyMetadata] = new ModelBindingResult(model: null, isModelSet: true, key: "theModel.Age");

            var testableBinder = new TestableMutableObjectModelBinder();
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessResults(bindingContext, results, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var modelError = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, modelError.ErrorMessage);
            Assert.IsType<NullReferenceException>(modelError.Exception);
        }
        public void GetMetadataForProperties_ReturnsOnlyIncludedProperties_UsingBindAttributeInclude()
        {
            // Arrange
            var expectedPropertyNames = new[] { "IncludedExplicitly1", "IncludedExplicitly2" };
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(TypeWithIncludedPropertiesUsingBindAttribute)),
                OperationBindingContext = new OperationBindingContext
                {
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                }
            };

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
            var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelErrorWithMessage()
        {
            // Arrange
            Person model = new Person();
            ModelMetadata containerMetadata = GetMetadataForObject(model);
            HttpActionContext context = ContextUtil.CreateActionContext();

            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel"
            };

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError()
        {
            // Arrange
            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person()),
                ModelName = "foo"
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode);
            ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage);
        }
示例#33
0
        public void ProcessDto_BindRequiredFieldNull_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age  = -20
            };

            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = new ModelBindingContext()
            {
                ModelMetadata     = containerMetadata,
                ModelName         = "theModel",
                ModelState        = new ModelStateDictionary(),
                ValidatorProvider = Mock.Of <IModelValidatorProvider>()
            };
            var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(),
                                                               bindingContext.ValidatorProvider,
                                                               bindingContext.ModelState,
                                                               containerMetadata,
                                                               null);

            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");

            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult("John Doe", new ModelValidationNode(propertyMetadata, "theModel.Name"));

            // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
            // case because the binding exists.
            propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.Age"));

            // Act; must also Validate because null-check error handler is late-bound
            testableBinder.ProcessDto(bindingContext, dto);
            bindingContext.ValidationNode.Validate(validationContext);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Name field
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.Name", out modelState));
            Assert.Equal(0, modelState.Errors.Count);
            Assert.Equal(ModelValidationState.Valid, modelState.ValidationState);

            // Check Age error.
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value is required.", modelError.ErrorMessage);
        }
        public void ProcessDto_Success()
        {
            // Arrange
            DateTime dob = new DateTime(2001, 1, 1);
            PersonWithBindExclusion model = new PersonWithBindExclusion
            {
                DateOfBirth = dob
            };
            ModelMetadata containerMetadata = GetMetadataForObject(model);

            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata
            };
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            ModelMetadata firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
            dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, ""));
            ModelMetadata lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");
            dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, ""));
            ModelMetadata dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");
            dto.Results[dobProperty] = null;

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            Assert.Equal("John", model.FirstName);
            Assert.Equal("Doe", model.LastName);
            Assert.Equal(dob, model.DateOfBirth);
            Assert.True(bindingContext.ModelState.IsValid);
        }
        public void GetMetadataForProperties_WithoutBindAttribute()
        {
            // Arrange
            string[] expectedPropertyNames = new[] { "DateOfBirth", "DateOfDeath", "ValueTypeRequired", "FirstName", "LastName", "PropertyWithDefaultValue" };

            HttpActionContext actionContext = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            IEnumerable<ModelMetadata> propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(actionContext, bindingContext);
            string[] returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
        public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError()
        {
            // Arrange
            Person model = new Person
            {
                DateOfBirth = new DateTime(1900, 1, 1)
            };
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(model)
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(new DateTime(1800, 1, 1), validationNode);

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(null, bindingContext, propertyMetadata, dtoResult, requiredValidator: null);

            // Assert
            Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine
                       + "Parameter name: value",
                         bindingContext.ModelState["foo"].Errors[0].Exception.Message);
        }
        public void CreateModel()
        {
            // Arrange
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            object retModel = testableBinder.CreateModelPublic(null, bindingContext);

            // Assert
            Assert.IsType<Person>(retModel);
        }
        public async Task CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndModelHasNoProperties()
        {
            // Arrange
            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    IsTopLevelObject = true,
                    ModelMetadata = GetMetadataForType(typeof(PersonWithNoProperties))
                },
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata =
                mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();

            // Act
            var retModel = await mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.True(retModel);
        }
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            ModelWithBindRequired model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            ModelMetadata containerMetadata = GetMetadataForObject(model);

            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel"
            };
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            ModelMetadata nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[nameProperty] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(nameProperty, ""));

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The Age property is required.", modelError.ErrorMessage);
        }
        public async Task CanCreateModel_ForExplicitValueProviderMetadata_UsesOriginalValueProvider(
            Type modelType,
            bool originalValueProviderProvidesValue)
        {
            var mockValueProvider = new Mock<IValueProvider>();
            mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                             .Returns(Task.FromResult(false));

            var mockOriginalValueProvider = new Mock<IBindingSourceValueProvider>();
            mockOriginalValueProvider
                .Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                .Returns(Task.FromResult(originalValueProviderProvidesValue));

            mockOriginalValueProvider
                .Setup(o => o.Filter(It.IsAny<BindingSource>()))
                .Returns<BindingSource>(source =>
                {
                    if (source == BindingSource.Query)
                    {
                        return mockOriginalValueProvider.Object;
                    }

                    return null;
                });

            var modelMetadata = GetMetadataForType(modelType);
            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    ModelMetadata = modelMetadata,
                    ValueProvider = mockValueProvider.Object,
                    OperationBindingContext = new OperationBindingContext
                    {
                        ValueProvider = mockOriginalValueProvider.Object,
                        MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                        ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    },

                    // Setting it to empty ensures that model does not get created becasue of no model name.
                    ModelName = "dummyName",
                    BindingSource = modelMetadata.BindingSource,
                    BinderModelName = modelMetadata.BinderModelName
                }
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata =
                mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();

            // Act
            var retModel = await mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.Equal(originalValueProviderProvidesValue, retModel);
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            ModelWithRequired model = new ModelWithRequired();
            ModelMetadata containerMetadata = GetMetadataForObject(model);
            HttpActionContext context = ContextUtil.CreateActionContext();

            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel"
            };

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The Age field is required.", modelError.ErrorMessage);

            // Check City error.
            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The City field is required.", modelError.ErrorMessage);
        }
        public async Task CanCreateModel_UnmarkedProperties_UsesCurrentValueProvider(
            Type modelType,
            bool valueProviderProvidesValue)
        {
            var mockValueProvider = new Mock<IValueProvider>();
            mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                             .Returns(Task.FromResult(valueProviderProvidesValue));

            var mockOriginalValueProvider = new Mock<IValueProvider>();
            mockOriginalValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
                                     .Returns(Task.FromResult(false));

            var bindingContext = new MutableObjectBinderContext
            {
                ModelBindingContext = new ModelBindingContext
                {
                    ModelMetadata = GetMetadataForType(modelType),
                    ValueProvider = mockValueProvider.Object,
                    OperationBindingContext = new OperationBindingContext
                    {
                        ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                        ValueProvider = mockOriginalValueProvider.Object,
                        MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    },
                    // Setting it to empty ensures that model does not get created becasue of no model name.
                    ModelName = "dummyName"
                }
            };

            var mutableBinder = new TestableMutableObjectModelBinder();
            bindingContext.PropertyMetadata =
                mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();

            // Act
            var retModel = await mutableBinder.CanCreateModel(bindingContext);

            // Assert
            Assert.Equal(valueProviderProvidesValue, retModel);
        }
        public void ProcessDto_RequiredFieldNull_RaisesModelErrorWithMessage()
        {
            // Arrange
            Person model = new Person();
            ModelMetadata containerMetadata = GetMetadataForObject(model);
            HttpActionContext context = ContextUtil.CreateActionContext();

            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel"
            };

            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired invalid.
            ModelMetadata propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));

            // Act
            testableBinder.ProcessDto(context, bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void CreateModel_InstantiatesInstanceOfMetadataType()
        {
            // Arrange
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person))
            };

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            var retModel = testableBinder.CreateModelPublic(bindingContext);

            // Assert
            Assert.IsType<Person>(retModel);
        }
        public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue()
        {
            // Arrange
            HttpActionContext context = ContextUtil.CreateActionContext();

            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person())
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "PropertyWithDefaultValue");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode);
            ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            var person = Assert.IsType<Person>(bindingContext.Model);
            Assert.Equal(123.456m, person.PropertyWithDefaultValue);
            Assert.True(context.ModelState.IsValid);
        }
        public void GetMetadataForProperties_WithBindAttribute()
        {
            // Arrange
            var expectedPropertyNames = new[] { "FirstName", "LastName" };
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion)),
                OperationBindingContext = new OperationBindingContext
                {
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider()
                }
            };

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
            var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            Person model = new Person();
            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(model)
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode);
            ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            validationNode.Validate(context);
            Assert.True(context.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
        public void GetMetadataForProperties_WithoutBindAttribute()
        {
            // Arrange
            var expectedPropertyNames = new[]
            {
                nameof(Person.DateOfBirth),
                nameof(Person.DateOfDeath),
                nameof(Person.ValueTypeRequired),
                nameof(Person.ValueTypeRequiredWithDefaultValue),
                nameof(Person.FirstName),
                nameof(Person.LastName),
                nameof(Person.PropertyWithDefaultValue),
                nameof(Person.PropertyWithInitializedValue),
                nameof(Person.PropertyWithInitializedValueAndDefault),
            };
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(Person)),
                OperationBindingContext = new OperationBindingContext
                {
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider()
                },
            };

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
            var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback()
        {
            // Arrange
            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new Person()),
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode);
            ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            Assert.True(context.ModelState.IsValid);
            validationNode.Validate(context, bindingContext.ValidationNode);
            Assert.False(context.ModelState.IsValid);
        }
        public void GetMetadataForProperties_DoesNotReturn_ExcludedProperties()
        {
            // Arrange
            var expectedPropertyNames = new[] { "IncludedByDefault1", "IncludedByDefault2" };
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForType(typeof(TypeWithExcludedPropertiesUsingBindAttribute)),
                OperationBindingContext = new OperationBindingContext
                {
                    HttpContext = new DefaultHttpContext
                    {
                        RequestServices = CreateServices()
                    },
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                }
            };

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
            var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();

            // Assert
            Assert.Equal(expectedPropertyNames, returnedPropertyNames);
        }
        public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString()
        {
            // Arrange
            HttpActionContext context = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext = new ModelBindingContext
            {
                ModelMetadata = GetMetadataForObject(new ModelWhosePropertySetterThrows()),
                ModelName = "foo"
            };

            ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name");
            ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo.Name");
            ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode);
            ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();

            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetPropertyPublic(context, bindingContext, propertyMetadata, dtoResult, requiredValidator);

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal(1, bindingContext.ModelState["foo.Name"].Errors.Count);
            Assert.Equal("This message comes from the [Required] attribute.", bindingContext.ModelState["foo.Name"].Errors[0].ErrorMessage);
        }
        public void ProcessDto_BindRequiredFieldNull_RaisesModelError()
        {
            // Arrange
            ModelWithBindRequired model = new ModelWithBindRequired
            {
                Name = "original value",
                Age  = -20
            };

            ModelMetadata       containerMetadata = GetMetadataForObject(model);
            HttpActionContext   context           = ContextUtil.CreateActionContext();
            ModelBindingContext bindingContext    = new ModelBindingContext()
            {
                ModelMetadata = containerMetadata,
                ModelName     = "theModel",
                ModelState    = context.ModelState,
            };

            ComplexModelDto dto = new ComplexModelDto(
                containerMetadata,
                containerMetadata.Properties
                );
            TestableMutableObjectModelBinder testableBinder =
                new TestableMutableObjectModelBinder();

            ModelMetadata propertyMetadata = dto.PropertyMetadata.Single(
                o => o.PropertyName == "Name"
                );

            dto.Results[propertyMetadata] = new ComplexModelDtoResult(
                "John Doe",
                new ModelValidationNode(propertyMetadata, "theModel.Name")
                );

            // Attempt to set non-Nullable property to null.  HttpBindRequiredAttribute should not be relevant in this
            // case because the binding exists.
            propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
            dto.Results[propertyMetadata] = new ComplexModelDtoResult(
                null,
                new ModelValidationNode(propertyMetadata, "theModel.Age")
                );

            // Act; must also Validate because null-check error handler is late-bound
            testableBinder.ProcessDto(context, bindingContext, dto);
            bindingContext.ValidationNode.Validate(context);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;

            Assert.False(modelStateDictionary.IsValid);
            Assert.Single(modelStateDictionary);

            // Check Age error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));

            ModelError modelError = Assert.Single(modelState.Errors);

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value is required.", modelError.ErrorMessage);
        }