public async Task BindModelAsync_PassesExpectedBindingInfoAndMetadata_IfPrefixMatches() { // Arrange var expectedModelName = "expectedName"; var binderExecuted = false; var metadataProvider = new TestModelMetadataProvider(); 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 { Name = expectedModelName, 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.Null(context.BindingInfo); Assert.Same(parameterDescriptor, context.CacheToken); Assert.Equal(metadata, context.Metadata); }) .Returns(modelBinder.Object); var argumentBinder = new ParameterBinder( metadataProvider, factory.Object, Mock.Of <IObjectModelValidator>(), NullLoggerFactory.Instance); var valueProvider = new SimpleValueProvider { { expectedModelName, new object() }, }; var valueProviderFactory = new SimpleValueProviderFactory(valueProvider); var controllerContext = GetControllerContext(); // Act & Assert await argumentBinder.BindModelAsync(controllerContext, valueProvider, parameterDescriptor); Assert.True(binderExecuted); }
public async Task BindModelAsync_ForOverlappingParametersWithSuppressions_InValid_WithValidSecondParameter() { // 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."); 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); // BodyModelBinder does not update ModelState in success case. var modelBindingResult = ModelBindingResult.Success(new JsonPatchDocument()); 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); Assert.Collection( modelState, 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 BindModelAsync_ForParameter_UsesValidationFromParameter_WhenDerivedModelIsSet() { // Arrange var method = GetType().GetMethod(nameof(TestMethodWithAttributes), BindingFlags.NonPublic | BindingFlags.Instance); var parameter = method.GetParameters()[0]; var parameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameter, Name = parameter.Name, }; var actionContext = GetControllerContext(); var modelMetadataProvider = new TestModelMetadataProvider(); var model = new DerivedPerson { DerivedProperty = "SomeValue" }; 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.GetMetadataForParameter(parameter); 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(parameter.Name, kvp.Key); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("Always Invalid", error.ErrorMessage); }); }
public async Task BindModelAsync_ForProperty_UsesValidationOnProperty_WhenDerivedModelIsSet() { // Arrange var property = typeof(TestControllerWithValidatedProperties).GetProperty(nameof(TestControllerWithValidatedProperties.Model)); var parameterDescriptor = new ControllerBoundPropertyDescriptor { PropertyInfo = property, Name = property.Name, }; var actionContext = GetControllerContext(); var modelMetadataProvider = new TestModelMetadataProvider(); var model = new DerivedModel { DerivedProperty = "some value" }; 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}", kvp.Key); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("Always Invalid", 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() }, new MvcOptions()), _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 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); }