Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Add integration and functional tests of [BindRequired] on page prop…
Browse files Browse the repository at this point in the history
…erties (#8677)

- #7353
  • Loading branch information
dougbu authored Oct 31, 2018
1 parent f2af66b commit a6199bb
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -420,14 +419,16 @@ public async Task PageConventions_CustomizedModelCanPostToHandlers()
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel")
{
["__RequestVerificationToken"] = token,
["ConfirmPassword"] = "",
["Password"] = "",
["Email"] = ""
});
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["ConfirmPassword"] = "",
["Password"] = "",
["Email"] = ""
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
Expand All @@ -443,18 +444,20 @@ public async Task PageConventions_CustomizedModelCanPostToHandlers()
public async Task PageConventions_CustomizedModelCanWorkWithModelState()
{
// Arrange
var getPage = await Client.GetAsync("/CustomModelTypeModel");
var getPage = await Client.GetAsync("/CustomModelTypeModel?Attempts=0");
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel?Attempts=3")
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
});
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
Expand All @@ -465,6 +468,37 @@ public async Task PageConventions_CustomizedModelCanWorkWithModelState()
Assert.Equal("/", response.Headers.Location.ToString());
}

[Fact]
public async Task PageConventions_CustomizedModelCanWorkWithModelState_EnforcesBindRequired()
{
// Arrange
var getPage = await Client.GetAsync("/CustomModelTypeModel?Attempts=0");
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel")
{
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
var response = await Client.SendAsync(message);

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Contains(
"A value for the &#x27;Attempts&#x27; parameter or property was not provided.",
responseText);
}

[Fact]
public async Task ValidationAttributes_OnTopLevelProperties()
{
Expand Down Expand Up @@ -642,10 +676,12 @@ public async Task RoundTrippingFormFileInputWorks()

var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(response);

var content = new MultipartFormDataContent();
content.Add(new StringContent("property1-value"), property1);
content.Add(new StringContent("test-value1"), file1, "test1.txt");
content.Add(new StringContent("test-value2"), file3, "test2.txt");
var content = new MultipartFormDataContent
{
{ new StringContent("property1-value"), property1 },
{ new StringContent("test-value1"), file1, "test1.txt" },
{ new StringContent("test-value2"), file3, "test2.txt" }
};

var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Primitives;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Expand Down Expand Up @@ -179,6 +181,74 @@ public async Task BindModelAsync_WithBindProperty_EnforcesBindRequired(int? inpu
}
}

[Theory]
[InlineData(null, false)]
[InlineData(123, true)]
public async Task BindModelAsync_WithBindPageProperty_EnforcesBindRequired(int? input, bool isValid)
{
// Arrange
var propertyInfo = typeof(TestPage).GetProperty(nameof(TestPage.BindRequiredProperty));
var propertyDescriptor = new PageBoundPropertyDescriptor
{
BindingInfo = BindingInfo.GetBindingInfo(new[]
{
new FromQueryAttribute { Name = propertyInfo.Name },
}),
Name = propertyInfo.Name,
ParameterType = propertyInfo.PropertyType,
Property = propertyInfo,
};

var typeInfo = typeof(TestPage).GetTypeInfo();
var actionDescriptor = new CompiledPageActionDescriptor
{
BoundProperties = new[] { propertyDescriptor },
HandlerTypeInfo = typeInfo,
ModelTypeInfo = typeInfo,
PageTypeInfo = typeInfo,
};

var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.Method = "POST";
if (input.HasValue)
{
request.QueryString = new QueryString($"?{propertyDescriptor.Name}={input.Value}");
}
});

var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(modelMetadataProvider);
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(modelMetadataProvider);
var modelMetadata = modelMetadataProvider
.GetMetadataForProperty(typeof(TestPage), propertyDescriptor.Name);

var pageBinder = PageBinderFactory.CreatePropertyBinder(
parameterBinder,
modelMetadataProvider,
modelBinderFactory,
actionDescriptor);
var pageContext = new PageContext
{
ActionDescriptor = actionDescriptor,
HttpContext = testContext.HttpContext,
RouteData = testContext.RouteData,
ValueProviderFactories = testContext.ValueProviderFactories,
};

var page = new TestPage();

// Act
await pageBinder(pageContext, page);

// Assert
Assert.Equal(isValid, pageContext.ModelState.IsValid);
if (isValid)
{
Assert.Equal(input.Value, page.BindRequiredProperty);
}
}

[Theory]
[InlineData("RequiredAndStringLengthProp", null, false)]
[InlineData("RequiredAndStringLengthProp", "", false)]
Expand Down Expand Up @@ -231,12 +301,18 @@ public async Task BindModelAsync_WithBindProperty_EnforcesDataAnnotationsAttribu
}
}

class TestController
private class TestController
{
[BindNever] public string BindNeverProp { get; set; }
[BindRequired] public int BindRequiredProp { get; set; }
[Required, StringLength(3)] public string RequiredAndStringLengthProp { get; set; }
[DisplayName("My Display Name"), StringLength(3)] public string DisplayNameStringLengthProp { get; set; }
}

private class TestPage : PageModel
{
[BindRequired]
public int BindRequiredProperty { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

Expand All @@ -13,6 +14,10 @@ public class CustomModelTypeModel : PageModel

public string ReturnUrl { get; set; }

[BindRequired]
[FromQuery(Name = nameof(Attempts))]
public int Attempts { get; set; }

public class InputModel
{
[Required]
Expand Down Expand Up @@ -69,10 +74,13 @@ public override IActionResult OnPostAsync(string returnUrl = null)
{
if (!ModelState.IsValid)
{
Attempts++;
RouteData.Values.Add(nameof(Attempts), Attempts);

return Page();
}

return Redirect("~/");
}
}
}
}

0 comments on commit a6199bb

Please sign in to comment.