Additional tests
This commit is contained in:
parent
f575677c95
commit
6bd4b87d2c
|
|
@ -83,6 +83,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
path = path.Substring(2);
|
||||
}
|
||||
|
||||
if (path.StartsWith("$[", StringComparison.Ordinal))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
// Handle path combinations such as ""+"Property", "Parent"+"Property", or "Parent"+"[12]".
|
||||
var key = ModelNames.CreatePropertyModelName(context.ModelName, path);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,20 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||
{
|
||||
public abstract class JsonInputFormatterTestBase
|
||||
public abstract class JsonInputFormatterTestBase : LoggedTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("application/json", true)]
|
||||
|
|
@ -199,6 +203,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.Equal(new int[] { 0, 23, 300 }, (IEnumerable<int>)result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual async Task ReadAsync_ArrayOfObjects_HasCorrectKey()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var content = "[{\"Age\": 5}, {\"Age\": 3}, {\"Age\": \"Cheese\"} ]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(List<ComplexModel>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError, "Model should have had an error!");
|
||||
Assert.Single(formatterContext.ModelState["[2].Age"].Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual async Task ReadAsync_AddsModelValidationErrorsToModelState()
|
||||
{
|
||||
|
|
@ -215,10 +239,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal(
|
||||
"Could not convert string to decimal: not-an-age. Path 'Age', line 1, position 44.",
|
||||
formatterContext.ModelState["Age"].Errors[0].ErrorMessage);
|
||||
Assert.True(result.HasError, "Model should have had an error!");
|
||||
Assert.Single(formatterContext.ModelState["Age"].Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -227,19 +249,18 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var content = "[0, 23, 300]";
|
||||
var content = "[0, 23, 33767]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(byte[]), httpContext);
|
||||
var formatterContext = CreateInputFormatterContext(typeof(short[]), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal("The supplied value is invalid.", formatterContext.ModelState["[2]"].Errors[0].ErrorMessage);
|
||||
Assert.Null(formatterContext.ModelState["[2]"].Errors[0].Exception);
|
||||
Assert.True(result.HasError, "Model should have produced an error!");
|
||||
Assert.True(formatterContext.ModelState.ContainsKey("[2]"), "Should have contained key '[2]'");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -259,9 +280,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal(
|
||||
"Error converting value 300 to type 'System.Byte'. Path '[1].Small', line 1, position 69.",
|
||||
formatterContext.ModelState["names[1].Small"].Errors[0].ErrorMessage);
|
||||
Assert.Single(formatterContext.ModelState["names[1].Small"].Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -318,6 +337,45 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_ComplexPoco()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var content = "{ \"Id\": 5, \"Person\": { \"Name\": \"name\", \"Numbers\": [3, 2, \"Hamburger\"]} }";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(ComplexPoco), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError, "Model should have had an error!");
|
||||
Assert.Single(formatterContext.ModelState["Person.Numbers[2]"].Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual async Task ReadAsync_RequiredAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetInputFormatter();
|
||||
var content = "{ \"Id\": 5, \"Person\": {\"Numbers\": [3]} }";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(ComplexPoco), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError, "Model should have had an error!");
|
||||
Assert.Single(formatterContext.ModelState["Person.Name"].Errors);
|
||||
}
|
||||
|
||||
protected abstract TextInputFormatter GetInputFormatter();
|
||||
|
||||
protected static HttpContext GetHttpContext(
|
||||
|
|
@ -356,6 +414,20 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue);
|
||||
}
|
||||
|
||||
protected sealed class ComplexPoco
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Person Person{ get; set; }
|
||||
}
|
||||
|
||||
protected sealed class Person
|
||||
{
|
||||
[Required]
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string Name { get; set; }
|
||||
public IEnumerable<int> Numbers { get; set; }
|
||||
}
|
||||
|
||||
protected sealed class ComplexModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,40 +1,80 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||
{
|
||||
public class SystemTextJsonInputFormatterTest : JsonInputFormatterTestBase
|
||||
{
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
|
||||
[Fact]
|
||||
public override Task ReadAsync_AddsModelValidationErrorsToModelState()
|
||||
{
|
||||
return base.ReadAsync_AddsModelValidationErrorsToModelState();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
|
||||
[Fact]
|
||||
public override Task ReadAsync_InvalidArray_AddsOverflowErrorsToModelState()
|
||||
{
|
||||
return base.ReadAsync_InvalidArray_AddsOverflowErrorsToModelState();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
|
||||
[Fact]
|
||||
public override Task ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState()
|
||||
{
|
||||
return base.ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
|
||||
[Fact]
|
||||
public override Task ReadAsync_UsesTryAddModelValidationErrorsToModelState()
|
||||
{
|
||||
return base.ReadAsync_UsesTryAddModelValidationErrorsToModelState();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/corefx/issues/38492")]
|
||||
public override Task ReadAsync_RequiredAttribute()
|
||||
{
|
||||
// System.Text.Json does not yet support an equivalent of Required.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_SingleError()
|
||||
{
|
||||
// Arrange
|
||||
var failTotal = 0;
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var content = "[5, 'seven', 3, notnum ]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(List<int>), httpContext);
|
||||
|
||||
// Act
|
||||
await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
foreach(var modelState in formatterContext.ModelState)
|
||||
{
|
||||
foreach(var error in modelState.Value.Errors)
|
||||
{
|
||||
failTotal++;
|
||||
Assert.StartsWith("''' is an invalid start of a value", error.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(1, failTotal);
|
||||
}
|
||||
|
||||
protected override TextInputFormatter GetInputFormatter()
|
||||
{
|
||||
return new SystemTextJsonInputFormatter(new JsonOptions());
|
||||
return new SystemTextJsonInputFormatter(new JsonOptions(), LoggerFactory.CreateLogger<SystemTextJsonInputFormatter>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
}
|
||||
else
|
||||
{
|
||||
addMember = !path.EndsWith("." + member, StringComparison.Ordinal);
|
||||
addMember = !path.EndsWith($".{member}", StringComparison.Ordinal)
|
||||
&& !path.EndsWith($"[{member}]", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -235,6 +235,45 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.Equal(expectedMessage, modelError.ErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[5,7,3]", 0)]
|
||||
[InlineData("[5, 'seven', 3]", 1)]
|
||||
[InlineData("[5, 'seven', 3, 'notnum']", 2)]
|
||||
public async Task ReadAsync_AllowMultipleErrors(string content, int failCount)
|
||||
{
|
||||
// Arrange
|
||||
var failTotal = 0;
|
||||
var serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
Error = delegate (object sender, ErrorEventArgs args)
|
||||
{
|
||||
args.ErrorContext.Handled = true;
|
||||
}
|
||||
};
|
||||
|
||||
var formatter = CreateFormatter(serializerSettings: serializerSettings, allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(List<int>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
foreach (var modelState in formatterContext.ModelState)
|
||||
{
|
||||
foreach (var error in modelState.Value.Errors)
|
||||
{
|
||||
failTotal++;
|
||||
Assert.StartsWith("Could not convert string to integer:", error.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(failCount, failTotal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_DoNotAllowInputFormatterExceptionMessages_DoesNotWrapJsonInputExceptions()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,19 +5,14 @@ using Microsoft.AspNetCore.Mvc;
|
|||
|
||||
namespace MvcSandbox.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
[HttpPost("/")]
|
||||
public IActionResult Index(Person person)
|
||||
[ModelBinder]
|
||||
public string Id { get; set; }
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return Ok(person);
|
||||
return View();
|
||||
}
|
||||
}
|
||||
|
||||
public class Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue