public void ProcessDto_BindRequiredFieldMissing_Throws()
        {
            // Arrange
            ModelWithBindRequired model = new ModelWithBindRequired
            {
                Name = "original value",
                Age  = -20
            };

            ModelMetadata containerMetadata = GetMetadataForObject(model);

            ControllerContext             controllerContext = new ControllerContext();
            ExtensibleModelBindingContext bindingContext    = new ExtensibleModelBindingContext
            {
                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 & assert
            Assert.Throws <InvalidOperationException>(
                delegate { testableBinder.ProcessDto(controllerContext, bindingContext, dto); },
                @"A value for 'theModel.Age' is required but was not present in the request.");

            Assert.Equal("original value", model.Name);
            Assert.Equal(-20, model.Age);
        }
예제 #2
0
        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 & assert
            testableBinder.ProcessDto(context, bindingContext, dto);

            Assert.False(bindingContext.ModelState.IsValid);
        }
        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 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.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("The Age property is required.", modelError.ErrorMessage);
        }
예제 #5
0
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age  = -20
            };

            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = new ModelBindingContext
            {
                ModelMetadata     = containerMetadata,
                ModelName         = "theModel",
                ValidatorProvider = Mock.Of <IModelValidatorProvider>()
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

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

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

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(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);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The 'Age' property is required.", 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 ProcessResults_BindRequiredFieldMissing_RaisesModelError()
        {
            // 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",
                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 ProcessDto_BindRequiredFieldMissing_Throws()
        {
            // Arrange
            ModelWithBindRequired model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            ModelMetadata containerMetadata = GetMetadataForObject(model);

            ControllerContext controllerContext = new ControllerContext();
            ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext
            {
                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 & assert
            Assert.Throws<InvalidOperationException>(
                delegate { testableBinder.ProcessDto(controllerContext, bindingContext, dto); },
                @"A value for 'theModel.Age' is required but was not present in the request.");

            Assert.Equal("original value", model.Name);
            Assert.Equal(-20, model.Age);
        }
        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 & assert
            testableBinder.ProcessDto(context, bindingContext, dto);

            Assert.False(bindingContext.ModelState.IsValid);
        }
예제 #10
0
        public async Task BindModelAsync_ValueTypePropertyWithBindRequired_SetToNull_CapturesException()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var binder = CreateBinder(bindingContext.ModelMetadata);

            // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
            // case because the property did have a result.
            var property = GetMetadataForProperty(model.GetType(), nameof(ModelWithBindRequired.Age));
            binder.Results[property] = ModelBindingResult.Success(model: null);

            // Act
            await binder.BindModelAsync(bindingContext);

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

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

            var modelError = Assert.Single(entry.Errors);
            Assert.Equal(string.Empty, modelError.ErrorMessage);
            Assert.IsType<NullReferenceException>(modelError.Exception);
        }
예제 #11
0
        public async Task BindModelAsync_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var property = GetMetadataForProperty(model.GetType(), nameof(ModelWithBindRequired.Age));

            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var binder = CreateBinder(bindingContext.ModelMetadata);
            binder.Results[property] = ModelBindingResult.Failed();

            // Act
            await binder.BindModelAsync(bindingContext);

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

            // Check Age error.
            ModelStateEntry entry;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out entry));
            var modelError = Assert.Single(entry.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value for the 'Age' property was not provided.", modelError.ErrorMessage);
        }
예제 #12
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(),
                ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
            };
            var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(),
                                                               bindingContext.ValidatorProviders,
                                                               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);
        }
예제 #13
0
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForObject(model);
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

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

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(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);

            var modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The 'Age' property is required.", modelError.ErrorMessage);
        }
예제 #14
0
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // 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",
                OperationBindingContext = new OperationBindingContext
                {
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>()
                }
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[nameProperty] = new ModelBindingResult(
                "John Doe",
                isModelSet: true,
                key: "");
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto, 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("The 'Age' property is required.", modelError.ErrorMessage);
        }
        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);
        }
예제 #16
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_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 ProcessDto_BindRequiredFieldNull_RaisesModelError()
        {
            // 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 dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.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 = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: true,
                key: "theModel.Age");

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

            // 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.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value is required.", modelError.ErrorMessage);
        }