public async Task BindModelAsync_ForProperty_UsesValidationFromActualModel_WhenDerivedModelIsSet() { // Arrange var property = typeof(TestController).GetProperty(nameof(TestController.Model)); var parameterDescriptor = new ControllerBoundPropertyDescriptor { PropertyInfo = property, Name = property.Name, }; var actionContext = GetControllerContext(); var modelMetadataProvider = new TestModelMetadataProvider(); var model = new DerivedModel(); var modelBindingResult = ModelBindingResult.Success(model); var parameterBinder = new ParameterBinder( modelMetadataProvider, Mock.Of <IModelBinderFactory>(), new DefaultObjectValidator( modelMetadataProvider, new[] { TestModelValidatorProvider.CreateDefaultProvider() }, new MvcOptions()), _optionsAccessor, NullLoggerFactory.Instance); var modelMetadata = modelMetadataProvider.GetMetadataForProperty(property.DeclaringType, property.Name); var modelBinder = CreateMockModelBinder(modelBindingResult); // Act var result = await parameterBinder.BindModelAsync( actionContext, modelBinder, CreateMockValueProvider(), parameterDescriptor, modelMetadata, value : null); // Assert Assert.True(result.IsModelSet); Assert.Same(model, result.Model); Assert.False(actionContext.ModelState.IsValid); Assert.Collection( actionContext.ModelState, kvp => { Assert.Equal($"{property.Name}.{nameof(DerivedPerson.DerivedProperty)}", kvp.Key); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("The DerivedProperty field is required.", error.ErrorMessage); }); }
public async Task BindModelAsync_ForOverlappingParameters_InValid_WithInValidFirstParameterAndSecondNull() { // Arrange var parameterDescriptor = new ParameterDescriptor { BindingInfo = new BindingInfo { BinderModelName = "id", }, Name = "identifier", ParameterType = typeof(string), }; var actionContext = GetControllerContext(); var modelState = actionContext.ModelState; // Mimic ModelStateEntry when first parameter is [FromRoute] int id and request URI is /api/values/notAnInt modelState.SetModelValue("id", "notAnInt", "notAnInt"); modelState.AddModelError("id", "This is not valid."); var modelMetadataProvider = new TestModelMetadataProvider(); var modelMetadata = modelMetadataProvider.GetMetadataForType(typeof(string)); var parameterBinder = new ParameterBinder( modelMetadataProvider, Mock.Of <IModelBinderFactory>(), new DefaultObjectValidator( modelMetadataProvider, new[] { TestModelValidatorProvider.CreateDefaultProvider() }), _optionsAccessor, NullLoggerFactory.Instance); // Mimic result when second parameter is [FromQuery(Name = "id")] string identifier and query is ?id var modelBindingResult = ModelBindingResult.Success(null); var modelBinder = CreateMockModelBinder(modelBindingResult); // Act var result = await parameterBinder.BindModelAsync( actionContext, modelBinder, new SimpleValueProvider(), parameterDescriptor, modelMetadata, value : null); // Assert Assert.True(result.IsModelSet); Assert.False(modelState.IsValid); var keyValuePair = Assert.Single(modelState); Assert.Equal("id", keyValuePair.Key); Assert.Equal(ModelValidationState.Invalid, keyValuePair.Value.ValidationState); }
public async Task BindModelAsync_ForOverlappingParametersWithSuppressions_InValid_WithInValidSecondParameter() { // Arrange var parameterDescriptor = new ParameterDescriptor { Name = "patchDocument", ParameterType = typeof(IJsonPatchDocument), }; var actionContext = GetControllerContext(); var modelState = actionContext.ModelState; // First ModelState key is not empty to match SimpleTypeModelBinder. modelState.SetModelValue("id", "notAGuid", "notAGuid"); modelState.AddModelError("id", "This is not valid."); // Second ModelState key is empty to match BodyModelBinder. modelState.AddModelError(string.Empty, "This is also not valid."); var modelMetadataProvider = new TestModelMetadataProvider(); modelMetadataProvider.ForType <IJsonPatchDocument>().ValidationDetails(v => v.ValidateChildren = false); var modelMetadata = modelMetadataProvider.GetMetadataForType(typeof(IJsonPatchDocument)); var parameterBinder = new ParameterBinder( modelMetadataProvider, Mock.Of <IModelBinderFactory>(), new DefaultObjectValidator( modelMetadataProvider, new[] { TestModelValidatorProvider.CreateDefaultProvider() }, new MvcOptions()), _optionsAccessor, NullLoggerFactory.Instance); var modelBindingResult = ModelBindingResult.Failed(); var modelBinder = CreateMockModelBinder(modelBindingResult); // Act var result = await parameterBinder.BindModelAsync( actionContext, modelBinder, new SimpleValueProvider(), parameterDescriptor, modelMetadata, value : null); // Assert Assert.False(result.IsModelSet); Assert.False(modelState.IsValid); Assert.Collection( modelState, kvp => { Assert.Empty(kvp.Key); Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("This is also not valid.", error.ErrorMessage); }, kvp => { Assert.Equal("id", kvp.Key); Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("This is not valid.", error.ErrorMessage); }); }
public async Task ObsoleteBindModelAsync_PassesExpectedBindingInfoAndMetadata_IfPrefixDoesNotMatch( BindingInfo parameterBindingInfo, string metadataBinderModelName, string parameterName, string expectedModelName) { // Arrange var binderExecuted = false; var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType <Person>().BindingDetails(binding => { binding.BinderModelName = metadataBinderModelName; }); var metadata = metadataProvider.GetMetadataForType(typeof(Person)); var modelBinder = new Mock <IModelBinder>(); modelBinder .Setup(b => b.BindModelAsync(It.IsAny <ModelBindingContext>())) .Callback((ModelBindingContext context) => { Assert.Equal(expectedModelName, context.ModelName, StringComparer.Ordinal); }) .Returns(Task.CompletedTask); var parameterDescriptor = new ParameterDescriptor { BindingInfo = parameterBindingInfo, Name = parameterName, ParameterType = typeof(Person), }; var factory = new Mock <IModelBinderFactory>(MockBehavior.Strict); factory .Setup(f => f.CreateBinder(It.IsAny <ModelBinderFactoryContext>())) .Callback((ModelBinderFactoryContext context) => { binderExecuted = true; // Confirm expected data is passed through to ModelBindingFactory. Assert.Same(parameterDescriptor.BindingInfo, context.BindingInfo); Assert.Same(parameterDescriptor, context.CacheToken); Assert.Equal(metadata, context.Metadata); }) .Returns(modelBinder.Object); var parameterBinder = new ParameterBinder( metadataProvider, factory.Object, Mock.Of <IObjectModelValidator>(), _optionsAccessor, NullLoggerFactory.Instance); var controllerContext = GetControllerContext(); // Act & Assert #pragma warning disable CS0618 // Type or member is obsolete await parameterBinder.BindModelAsync(controllerContext, new SimpleValueProvider(), parameterDescriptor); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(binderExecuted); }