public void SetProperty_PropertySetterThrows_CapturesException()
        {
            // Arrange
            var model = new ModelWhosePropertySetterThrows();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
            bindingContext.ModelName = "foo";

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)];

            var result = new ModelBindingResult(model: null, isModelSet: true, key: "foo.NameNoAttribute");
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal(1, bindingContext.ModelState["foo.NameNoAttribute"].Errors.Count);
            Assert.Equal("This is a different exception." + Environment.NewLine
                       + "Parameter name: value",
                         bindingContext.ModelState["foo.NameNoAttribute"].Errors[0].Exception.Message);
        }
        public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError()
        {
            // Arrange
            var model = new Person
            {
                DateOfBirth = new DateTime(1900, 1, 1)
            };

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

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)];

            var result = new ModelBindingResult(new DateTime(1800, 1, 1), isModelSet: true, key: "foo");
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // 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_SettingNonNullableValueTypeToNull_CapturesException()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];

            var result = new ModelBindingResult(model: null, isModelSet: true, key: "foo.DateOfBirth");
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);

            var entry = Assert.Single(bindingContext.ModelState, kvp => kvp.Key == "foo.DateOfBirth").Value;
            var error = Assert.Single(entry.Errors);
            Assert.Equal(string.Empty, error.ErrorMessage);
            Assert.IsType<NullReferenceException>(error.Exception);
        }
        public void SetProperty_CollectionProperty_UpdatesModel(
            string propertyName,
            Func<object, object> propertyAccessor,
            object collection)
        {
            // Arrange
            var model = new CollectionContainer();
            var type = model.GetType();
            var bindingContext = CreateContext(GetMetadataForType(type), model);
            var modelState = bindingContext.ModelState;
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);

            var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
            var result = new ModelBindingResult(model: collection, isModelSet: true, key: propertyName);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            Assert.Equal(collection, propertyAccessor(model));
            Assert.True(modelState.IsValid);
            Assert.Empty(modelState);
        }
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];

            var result = new ModelBindingResult(new DateTime(2001, 1, 1), key: "foo", isModelSet: true);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            Assert.True(bindingContext.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
        public void SetProperty_PropertyIsReadOnly_DoesNothing()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NonUpdateableProperty)];

            var result = new ModelBindingResult(model: null, isModelSet: false, key: "foo");
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            // If didn't throw, success!
        }
        public void SetProperty_ValueProvidedAndCanUpdatePropertyTrue_DoesNothing(
            string propertyName,
            Func<object, object> propertAccessor)
        {
            // Arrange
            var model = new MyModelTestingCanUpdateProperty();
            var type = model.GetType();
            var bindingContext = CreateContext(GetMetadataForType(type), model);
            var modelState = bindingContext.ModelState;
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);

            var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
            var result = new ModelBindingResult(
                model: new Simple { Name = "Hanna" },
                isModelSet: true,
                key: propertyName);

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            Assert.Equal("Joe", propertAccessor(model));
            Assert.True(modelState.IsValid);
            Assert.Empty(modelState);
        }
        public void SetProperty_PropertyHasDefaultValue_DefaultValueAttributeDoesNothing()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithDefaultValue)];

            var result = new ModelBindingResult(model: null, isModelSet: false, key: "foo");
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            var person = Assert.IsType<Person>(bindingContext.Model);
            Assert.Equal(0m, person.PropertyWithDefaultValue);
            Assert.True(bindingContext.ModelState.IsValid);
        }
        public void SetProperty_PropertyIsPreinitialized_DefaultValueAttributeDoesNothing()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata =
                bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithInitializedValueAndDefault)];

            // This value won't be used because IsModelBound = false.
            var result = new ModelBindingResult(model: "bad-value", isModelSet: false, key: "foo");

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);

            // Assert
            var person = Assert.IsType<Person>(bindingContext.Model);
            Assert.Equal("preinitialized", person.PropertyWithInitializedValueAndDefault);
            Assert.True(bindingContext.ModelState.IsValid);
        }
        public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString()
        {
            // Arrange
            var model = new ModelWhosePropertySetterThrows();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
            bindingContext.ModelName = "foo";

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["Name"];

            var dtoResult = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "foo.Name");

            var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            var error = Assert.Single(bindingContext.ModelState["foo.Name"].Errors);
            Assert.Equal("This message comes from the [Required] attribute.", error.ErrorMessage);
        }
        public void SetProperty_PropertyIsPreinitialized_NoValue_DoesNothing()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var metadata = metadataProvider.GetMetadataForType(typeof(Person));
            var propertyMetadata = metadata.Properties[nameof(model.PropertyWithInitializedValue)];

            // The null model value won't be used because IsModelBound = false.
            var result = ModelBindingResult.Failed("foo");

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);

            // Assert
            var person = Assert.IsType<Person>(bindingContext.Model);
            Assert.Equal("preinitialized", person.PropertyWithInitializedValue);
            Assert.True(bindingContext.ModelState.IsValid);
        }
        public void SetProperty_SettingNullableTypeToNull_RequiredValidatorNotPresent_PropertySetterThrows_AddsRequiredMessageString()
        {
            // Arrange
            var model = new ModelWhosePropertySetterThrows();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
            bindingContext.ModelName = "foo";

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["NameNoAttribute"];

            var dtoResult = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "foo.NameNoAttribute");

            var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal(1, bindingContext.ModelState["foo.NameNoAttribute"].Errors.Count);
            Assert.Equal("This is a different exception." + Environment.NewLine
                       + "Parameter name: value",
                         bindingContext.ModelState["foo.NameNoAttribute"].Errors[0].Exception.Message);
        }
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
            bindingContext.ModelName = " foo";

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["ValueTypeRequired"];

            var dtoResult = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "foo.ValueTypeRequired");

            var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
            Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage);
        }
        public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"];

            var dtoResult = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "foo");

            var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            Assert.False(bindingContext.ModelState.IsValid);
        }
        public void SetProperty_PropertyIsSettable_CallsSetter()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"];

            var dtoResult = new ModelBindingResult(
                new DateTime(2001, 1, 1),
                key: "foo",
                isModelSet: true);

            var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider;
            var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
            validatorProvider.GetValidators(validatorProviderContext);

            var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            Assert.True(bindingContext.ModelState.IsValid);
            Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth);
        }
        public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue()
        {
            // Arrange
            var model = new Person();
            var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
            var propertyMetadata = bindingContext.ModelMetadata.Properties["PropertyWithDefaultValue"];

            var dtoResult = new ModelBindingResult(
                model: null,
                isModelSet: false,
                key: "foo");

            var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider;
            var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
            validatorProvider.GetValidators(validatorProviderContext);

            var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);

            var testableBinder = new TestableMutableObjectModelBinder();

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

            // Assert
            var person = Assert.IsType<Person>(bindingContext.Model);
            Assert.Equal(123.456m, person.PropertyWithDefaultValue);
            Assert.True(bindingContext.ModelState.IsValid);
        }