/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Mvc/test/Mvc.IntegrationTests/TryUpdateModelIntegrationTest.cs
1 370 строк
41 KB
Shreyas Jejurkar
chore : Remove unwanted `using` statement (#39176)
02 янв 2022, 17:39
Не верифицирован
02 янв 2022, 17:39
c85baf8
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Mvc.IntegrationTests; public class TryUpdateModelIntegrationTest { private class Address { public string Street { get; set; } public string City { get; set; } } [Fact] public async Task TryUpdateModel_ExistingModel_EmptyPrefix_OverwritesBoundValues() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Address { Street = "DefaultStreet", City = "Toronto", }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.Equal("SomeStreet", model.Street); Assert.Equal("Toronto", model.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_ExistingModel_EmptyPrefix_GetsBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Address(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Equal("SomeStreet", model.Street); Assert.Null(model.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private class Person1 { public string Name { get; set; } public Address Address { get; set; } } [Fact] public async Task TryUpdateModel_TopLevelCollection_EmptyPrefix_BindsAfterClearing() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create(new Dictionary<string, string> { { "[0].Name", "One Name" }, { "[1].Address.Street", "Two Street" }, }); }); var modelState = testContext.ModelState; var model = new List<Person1> { new Person1 { Name = "One", Address = new Address { Street = "DefaultStreet", City = "Toronto", }, }, new Person1 { Name = "Two" }, new Person1 { Name = "Three" }, }; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Collection( model, element => { Assert.Equal("One Name", element.Name); Assert.Null(element.Address); }, element => { Assert.Null(element.Name); Assert.NotNull(element.Address); Assert.Equal("Two Street", element.Address.Street); Assert.Null(element.Address.City); }); // ModelState Assert.True(modelState.IsValid); Assert.Equal(2, modelState.Count); Assert.NotNull(modelState["[0].Name"]); Assert.NotNull(modelState["[1].Address.Street"]); } [Fact] public async Task TryUpdateModel_NestedPoco_EmptyPrefix_DoesNotTrounceUnboundValues() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person1 { Name = "Joe", Address = new Address { Street = "DefaultStreet", City = "Toronto", }, }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.Equal("Joe", model.Name); Assert.Equal("SomeStreet", model.Address.Street); Assert.Equal("Toronto", model.Address.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address.Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private class Person2 { public List<Address> Address { get; set; } } [Fact] public async Task TryUpdateModel_SettableCollectionModel_EmptyPrefix_CreatesCollection() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person2(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableCollectionModel_EmptyPrefix_MaintainsCollectionIfNonNull() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person2 { Address = new List<Address>(), }; var collection = model.Address; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Same(collection, model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private class Person3 { public Person3() { Address = new List<Address>(); } public List<Address> Address { get; } } [Fact] public async Task TryUpdateModel_NonSettableCollectionModel_EmptyPrefix_GetsBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person3 { Address = { new Address { Street = "Old street", City = "Redmond", }, new Address { Street = "Older street", City = "Toronto", }, }, }; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model (collection is cleared and new members created from scratch). Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private class Person6 { public CustomReadOnlyCollection<Address> Address { get; set; } } [Fact] public async Task TryUpdateModel_ReadOnlyCollectionModel_EmptyPrefix_DoesNotGetBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person6(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.NotNull(state); Assert.Equal(ModelValidationState.Valid, state.ValidationState); Assert.Equal("SomeStreet", state.RawValue); Assert.Equal("SomeStreet", state.AttemptedValue); } [Fact] public async Task TryUpdateModel_ReadOnlyCollectionModel_WithPrefix_DoesNotGetBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person6(); // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.NotNull(state); Assert.Equal(ModelValidationState.Valid, state.ValidationState); Assert.Equal("SomeStreet", state.RawValue); Assert.Equal("SomeStreet", state.AttemptedValue); } private class Person4 { public Address[] Address { get; set; } } [Fact] public async Task TryUpdateModel_SettableArrayModel_EmptyPrefix_CreatesArray() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person4(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableArrayModel_EmptyPrefix_OverwritesArray() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person4 { Address = new Address[] { new Address { Street = "Old street", City = "Toronto", }, }, }; var collection = model.Address; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.NotSame(collection, model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private class Person5 { public Address[] Address { get; } = new Address[] { }; } [Fact] public async Task TryUpdateModel_NonSettableArrayModel_EmptyPrefix_IsNotBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person5(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); // Arrays should not be updated. Assert.Empty(model.Address); // ModelState Assert.True(modelState.IsValid); Assert.Empty(modelState); } private class Person7 { public IEnumerable<Address> Address { get; } = new Address[] { new Address() { City = "Redmond", Street = "One Microsoft Way" } }; } [Fact] public async Task TryUpdateModel_NonSettableIEnumerableModel_EmptyPrefix_IsNotBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person7(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); // Arrays should not be updated. Assert.Single(model.Address); Assert.Collection( model.Address, (a) => { Assert.Equal("Redmond", a.City); Assert.Equal("One Microsoft Way", a.Street); }); // ModelState Assert.True(modelState.IsValid); } private class Person8 { public ICollection<Address> Address { get; } = new Address[] { new Address() { City = "Redmond", Street = "One Microsoft Way" } }; } [Fact] public async Task TryUpdateModel_NonSettableICollectionModel_EmptyPrefix_IsNotBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person8(); // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); // Arrays should not be updated. Assert.Single(model.Address); Assert.Collection( model.Address, (a) => { Assert.Equal("Redmond", a.City); Assert.Equal("One Microsoft Way", a.Street); }); // ModelState Assert.True(modelState.IsValid); } [Fact] public async Task TryUpdateModel_ExistingModel_WithPrefix_ValuesGetOverwritten() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Address { Street = "DefaultStreet", City = "Toronto", }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.Equal("SomeStreet", model.Street); Assert.Equal("Toronto", model.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_ExistingModel_WithPrefix_GetsBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Address(); // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.Equal("SomeStreet", model.Street); Assert.Null(model.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_TopLevelCollection_WithPrefix_BindsAfterClearing() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create(new Dictionary<string, string> { { "prefix[0].Name", "One Name" }, { "prefix[1].Address.Street", "Two Street" }, }); }); var modelState = testContext.ModelState; var model = new List<Person1> { new Person1 { Name = "One", Address = new Address { Street = "DefaultStreet", City = "Toronto", }, }, new Person1 { Name = "Two" }, new Person1 { Name = "Three" }, }; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.Collection( model, element => { Assert.Equal("One Name", element.Name); Assert.Null(element.Address); }, element => { Assert.Null(element.Name); Assert.NotNull(element.Address); Assert.Equal("Two Street", element.Address.Street); Assert.Null(element.Address.City); }); // ModelState Assert.True(modelState.IsValid); Assert.Equal(2, modelState.Count); Assert.NotNull(modelState["prefix[0].Name"]); Assert.NotNull(modelState["prefix[1].Address.Street"]); } [Fact] public async Task TryUpdateModel_NestedPoco_WithPrefix_DoesNotTrounceUnboundValues() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person1 { Name = "Joe", Address = new Address { Street = "DefaultStreet", City = "Toronto", }, }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.Equal("Joe", model.Name); Assert.Equal("SomeStreet", model.Address.Street); Assert.Equal("Toronto", model.Address.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address.Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableCollectionModel_WithPrefix_CreatesCollection() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person2(); // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableCollectionModel_WithPrefix_MaintainsCollectionIfNonNull() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person2 { Address = new List<Address>(), }; var collection = model.Address; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Same(collection, model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_NonSettableCollectionModel_WithPrefix_GetsBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person3 { Address = { new Address { Street = "Old street", City = "Redmond", }, new Address { Street = "Older street", City = "Toronto", }, }, }; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model (collection is cleared and new members created from scratch). Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableArrayModel_WithPrefix_CreatesArray() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person4(); // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_SettableArrayModel_WithPrefix_OverwritesArray() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person4 { Address = new Address[] { new Address { Street = "Old street", City = "Toronto", }, }, }; var collection = model.Address; // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); Assert.NotSame(collection, model.Address); Assert.Single(model.Address); Assert.Equal("SomeStreet", model.Address[0].Street); Assert.Null(model.Address[0].City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("prefix.Address[0].Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_NonSettableArrayModel_WithPrefix_GetsBound() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("prefix.Address[0].Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new Person5(); // Act var result = await TryUpdateModelAsync(model, "prefix", testContext); // Assert Assert.True(result); // Model Assert.NotNull(model.Address); // Arrays should not be updated. Assert.Empty(model.Address); // ModelState Assert.True(modelState.IsValid); Assert.Empty(modelState); } [Fact] public async Task TryUpdateModelAsync_TopLevelFormFileCollection_IsBound() { // Arrange var data = "some data"; var testContext = ModelBindingTestHelper.GetTestContext( request => UpdateRequest(request, data, "files")); var modelState = testContext.ModelState; var model = new List<IFormFile> { new FormFile(new MemoryStream(), baseStreamOffset: 0, length: 0, name: "file", fileName: "file1"), new FormFile(new MemoryStream(), baseStreamOffset: 0, length: 0, name: "file", fileName: "file2"), new FormFile(new MemoryStream(), baseStreamOffset: 0, length: 0, name: "file", fileName: "file3"), }; // Act var result = await TryUpdateModelAsync(model, prefix: "files", testContext: testContext); // Assert Assert.True(result); // Model var file = Assert.Single(model); Assert.Equal("form-data; name=files; filename=text.txt", file.ContentDisposition); using (var reader = new StreamReader(file.OpenReadStream())) { Assert.Equal(data, reader.ReadToEnd()); } // ModelState Assert.True(modelState.IsValid); var kvp = Assert.Single(modelState); Assert.Equal("files", kvp.Key); var modelStateEntry = kvp.Value; Assert.NotNull(modelStateEntry); Assert.Empty(modelStateEntry.Errors); Assert.Equal(ModelValidationState.Valid, modelStateEntry.ValidationState); Assert.Null(modelStateEntry.AttemptedValue); Assert.Null(modelStateEntry.RawValue); } private class AddressWithNoParameterlessConstructor { private readonly int _id; public AddressWithNoParameterlessConstructor(int id) { _id = id; } public string Street { get; set; } public string City { get; set; } } [Fact] public async Task TryUpdateModel_ExistingModelWithNoParameterlessConstructor_OverwritesBoundValues() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new AddressWithNoParameterlessConstructor(10) { Street = "DefaultStreet", City = "Toronto", }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.Equal("SomeStreet", model.Street); Assert.Equal("Toronto", model.City); // ModelState Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Equal("Street", entry.Key); var state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } private record AddressRecord(string Street, string City) { public string ZipCode { get; set; } } [Fact] public async Task TryUpdateModel_RecordTypeModel_Throws() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new AddressRecord("DefaultStreet", "Toronto") { ZipCode = "98001", }; var oldModel = model; // Act & Assert var ex = await Assert.ThrowsAsync<NotSupportedException>(() => TryUpdateModelAsync(model, string.Empty, testContext)); Assert.Equal($"TryUpdateModelAsync cannot update a record type model. If a '{model.GetType()}' must be updated, include it in an object type.", ex.Message); } private class ModelWithRecordTypeProperty { public AddressRecord Address { get; set; } } [Fact] public async Task TryUpdateModel_RecordTypeProperty() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address.ZipCode", "98007").Add("Address.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new ModelWithRecordTypeProperty(); var oldModel = model; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.NotNull(model.Address); var address = model.Address; Assert.Equal("SomeStreet", address.Street); Assert.Null(address.City); Assert.Equal("98007", address.ZipCode); // ModelState Assert.True(modelState.IsValid); Assert.Equal(2, modelState.Count); var entry = Assert.Single(modelState, k => k.Key == "Address.ZipCode"); var state = entry.Value; Assert.Equal("98007", state.AttemptedValue); Assert.Equal("98007", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); entry = Assert.Single(modelState, k => k.Key == "Address.Street"); state = entry.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); } [Fact] public async Task TryUpdateModel_RecordTypePropertyIsOverwritten() { // Arrange var testContext = ModelBindingTestHelper.GetTestContext(request => { request.QueryString = QueryString.Create("Address.ZipCode", "98007").Add("Address.Street", "SomeStreet"); }); var modelState = testContext.ModelState; var model = new ModelWithRecordTypeProperty { Address = new AddressRecord("DefaultStreet", "DefaultCity") { ZipCode = "98056", }, }; var oldModel = model; // Act var result = await TryUpdateModelAsync(model, string.Empty, testContext); // Assert Assert.True(result); // Model Assert.Same(oldModel, model); Assert.NotNull(model.Address); var address = model.Address; Assert.Equal("SomeStreet", address.Street); Assert.Null(address.City); Assert.Equal("98007", address.ZipCode); // ModelState Assert.True(modelState.IsValid); Assert.Collection( modelState.OrderBy(kvp => kvp.Key), kvp => { Assert.Equal("Address.Street", kvp.Key); var state = kvp.Value; Assert.Equal("SomeStreet", state.AttemptedValue); Assert.Equal("SomeStreet", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); }, kvp => { Assert.Equal("Address.ZipCode", kvp.Key); var state = kvp.Value; Assert.Equal("98007", state.AttemptedValue); Assert.Equal("98007", state.RawValue); Assert.Empty(state.Errors); Assert.Equal(ModelValidationState.Valid, state.ValidationState); }); } private void UpdateRequest(HttpRequest request, string data, string name) { const string fileName = "text.txt"; var fileCollection = new FormFileCollection(); var formCollection = new FormCollection(new Dictionary<string, StringValues>(), fileCollection); request.Form = formCollection; request.ContentType = "multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq"; request.Headers["Content-Disposition"] = $"form-data; name={name}; filename={fileName}"; var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(data)); fileCollection.Add(new FormFile(memoryStream, 0, data.Length, name, fileName) { Headers = request.Headers }); } private class CustomReadOnlyCollection<T> : ICollection<T> { private readonly ICollection<T> _original; public CustomReadOnlyCollection() : this(new List<T>()) { } public CustomReadOnlyCollection(ICollection<T> original) { _original = original; } public int Count { get { return _original.Count; } } public bool IsReadOnly { get { return true; } } public void Add(T item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(T item) { return _original.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { _original.CopyTo(array, arrayIndex); } public bool Remove(T item) { throw new NotSupportedException(); } public IEnumerator<T> GetEnumerator() { foreach (T t in _original) { yield return t; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } private async Task<bool> TryUpdateModelAsync( object model, string prefix, ModelBindingTestContext testContext) { var valueProvider = await CompositeValueProvider.CreateAsync(testContext); return await ModelBindingHelper.TryUpdateModelAsync( model, model.GetType(), prefix, testContext, testContext.MetadataProvider, TestModelBinderFactory.CreateDefault(), valueProvider, ModelBindingTestHelper.GetObjectValidator(testContext.MetadataProvider)); } }