1538 lines
58 KiB
C#
1538 lines
58 KiB
C#
// 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 Microsoft.AspNetCore.Mvc.Formatters;
|
|
using Microsoft.AspNetCore.Mvc.Internal;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
|
using Microsoft.Extensions.Options;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|
{
|
|
public class ModelStateDictionaryTest
|
|
{
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("foo")]
|
|
public void ContainsKey_ReturnsFalse_IfNodeHasNotBeenMutated(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError("foo.bar", "some error");
|
|
|
|
// Act
|
|
var result = dictionary.ContainsKey(key);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("foo")]
|
|
public void ContainsKey_ReturnsFalse_IfNodeHasBeenRemoved(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError(key, "some error");
|
|
|
|
// Act
|
|
var remove = dictionary.Remove(key);
|
|
var containsKey = dictionary.ContainsKey(key);
|
|
|
|
// Assert
|
|
Assert.True(remove);
|
|
Assert.False(containsKey);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("foo")]
|
|
[InlineData("foo.bar")]
|
|
[InlineData("foo[bar]")]
|
|
public void ContainsKey_ReturnsTrue_IfNodeHasBeenMutated(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldSkipped(key);
|
|
|
|
// Act
|
|
var result = dictionary.ContainsKey(key);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("foo")]
|
|
[InlineData("foo.bar")]
|
|
[InlineData("foo.bar[10]")]
|
|
public void IndexerDoesNotReturnIntermediaryNodes(string key)
|
|
{
|
|
// Arrange
|
|
var modelStateDictionary = new ModelStateDictionary();
|
|
modelStateDictionary.AddModelError("foo.bar[10].baz", "error-message");
|
|
|
|
// Act
|
|
var result = modelStateDictionary[key];
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("prop")]
|
|
[InlineData("first.second")]
|
|
[InlineData("[0].prop")]
|
|
[InlineData("[qux]")]
|
|
[InlineData("[test].prop")]
|
|
[InlineData("first[0].second")]
|
|
[InlineData("first.second[0].third")]
|
|
[InlineData("first[second][0]")]
|
|
[InlineData("first.second.third[0]")]
|
|
[InlineData("first.second.third[0].fourth")]
|
|
[InlineData("first[0][second]")]
|
|
public void Indexer_ReturnsValuesAddedUsingSetModelValue(string key)
|
|
{
|
|
// Arrange
|
|
var value = "Hello world";
|
|
var modelStateDictionary = new ModelStateDictionary();
|
|
modelStateDictionary.SetModelValue(key, value, value);
|
|
|
|
// Act
|
|
var result = modelStateDictionary[key];
|
|
|
|
// Assert
|
|
Assert.Equal(value, result.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_RemovesAllEntries()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError("a", "a-error");
|
|
dictionary.AddModelError("b", "b-error");
|
|
dictionary.AddModelError("c", "c-error");
|
|
|
|
// Act
|
|
dictionary.Clear();
|
|
|
|
// Assert
|
|
Assert.Empty(dictionary);
|
|
Assert.Equal(0, dictionary.ErrorCount);
|
|
Assert.Empty(dictionary);
|
|
Assert.Equal(ModelValidationState.Valid, dictionary.ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_RemovesAllEntries_IfDictionaryIsEmpty()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
dictionary.Clear();
|
|
|
|
// Assert
|
|
Assert.Empty(dictionary);
|
|
Assert.Equal(0, dictionary.ErrorCount);
|
|
Assert.Empty(dictionary);
|
|
Assert.Equal(ModelValidationState.Valid, dictionary.ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldSkipped_MarksFieldAsSkipped_IfStateIsUnvalidated()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.SetModelValue("key", "value", "value");
|
|
|
|
// Act
|
|
source.MarkFieldSkipped("key");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Skipped, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldSkipped_MarksFieldAsSkipped_IfStateIsValid()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.MarkFieldValid("key");
|
|
|
|
// Act
|
|
source.MarkFieldSkipped("key");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Skipped, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldSkipped_MarksFieldAsSkipped_IfKeyIsNotPresent()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
|
|
// Act
|
|
source.MarkFieldSkipped("key");
|
|
|
|
// Assert
|
|
Assert.Equal(0, source.ErrorCount);
|
|
Assert.Single(source);
|
|
Assert.Equal(ModelValidationState.Skipped, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldSkipped_Throws_IfStateIsInvalid()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.AddModelError("key", "some error");
|
|
|
|
// Act
|
|
var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldSkipped("key"));
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"A field previously marked invalid should not be marked skipped.",
|
|
exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldValid_MarksFieldAsValid_IfStateIsUnvalidated()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.SetModelValue("key", "value", "value");
|
|
|
|
// Act
|
|
source.MarkFieldValid("key");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldValid_MarksFieldAsValid_IfStateIsSkipped()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.MarkFieldSkipped("key");
|
|
|
|
// Act
|
|
source.MarkFieldValid("key");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldValid_MarksFieldAsValid_IfKeyIsNotPresent()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
|
|
// Act
|
|
source.MarkFieldValid("key");
|
|
|
|
// Assert
|
|
Assert.Equal(0, source.ErrorCount);
|
|
Assert.Single(source);
|
|
Assert.Equal(ModelValidationState.Valid, source["key"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFieldValid_Throws_IfStateIsInvalid()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.AddModelError("key", "some-error");
|
|
|
|
// Act
|
|
var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldValid("key"));
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"A field previously marked invalid should not be marked valid.",
|
|
exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyConstructor_CopiesModelStateData()
|
|
{
|
|
// Arrange
|
|
var source = new ModelStateDictionary();
|
|
source.SetModelValue("key", "attempted-value", "raw-value");
|
|
var entry = source["key"];
|
|
entry.AttemptedValue = "attempted-value";
|
|
entry.RawValue = "raw-value";
|
|
entry.Errors.Add(new ModelError(new InvalidOperationException()));
|
|
entry.Errors.Add(new ModelError("error-message"));
|
|
entry.ValidationState = ModelValidationState.Skipped;
|
|
// Act
|
|
var target = new ModelStateDictionary(source);
|
|
|
|
// Assert
|
|
Assert.Equal(2, target.ErrorCount);
|
|
Assert.Single(target);
|
|
var actual = target["key"];
|
|
Assert.Equal(entry.RawValue, actual.RawValue);
|
|
Assert.Equal(entry.AttemptedValue, actual.AttemptedValue);
|
|
Assert.Equal(entry.Errors, actual.Errors);
|
|
Assert.Equal(entry.ValidationState, actual.ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelException_Succeeds()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
var exception = new TestException();
|
|
|
|
// Act
|
|
dictionary.TryAddModelException("some key", exception);
|
|
|
|
// Assert
|
|
var kvp = Assert.Single(dictionary);
|
|
Assert.Equal("some key", kvp.Key);
|
|
var error = Assert.Single(kvp.Value.Errors);
|
|
Assert.Same(exception, error.Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddModelErrorCreatesModelStateIfNotPresent()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
dictionary.AddModelError("some key", "some error");
|
|
|
|
// Assert
|
|
Assert.Equal(1, dictionary.ErrorCount);
|
|
var kvp = Assert.Single(dictionary);
|
|
Assert.Equal("some key", kvp.Key);
|
|
var error = Assert.Single(kvp.Value.Errors);
|
|
Assert.Equal("some error", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddModelErrorUsesExistingModelStateIfPresent()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError("some key", "some error");
|
|
var exception = new Exception();
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.AddModelError("some key", exception, metadata);
|
|
|
|
// Assert
|
|
Assert.Equal(2, dictionary.ErrorCount);
|
|
var kvp = Assert.Single(dictionary);
|
|
Assert.Equal("some key", kvp.Key);
|
|
|
|
Assert.Equal(2, kvp.Value.Errors.Count);
|
|
Assert.Equal("some error", kvp.Value.Errors[0].ErrorMessage);
|
|
Assert.Same(exception, kvp.Value.Errors[1].Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConstructorWithDictionaryParameter()
|
|
{
|
|
// Arrange
|
|
var oldDictionary = new ModelStateDictionary();
|
|
oldDictionary.SetModelValue("foo", "bar", "bar");
|
|
|
|
// Act
|
|
var newDictionary = new ModelStateDictionary(oldDictionary);
|
|
|
|
// Assert
|
|
Assert.Single(newDictionary);
|
|
Assert.Equal("bar", newDictionary["foo"].RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetFieldValidationState_ReturnsUnvalidatedIfDictionaryDoesNotContainKey()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Unvalidated, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetValidationState_ReturnsValidationStateForKey_IgnoresChildren()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError("foo.bar", "error text");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetValidationState("foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Unvalidated, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("foo")]
|
|
[InlineData("foo.bar")]
|
|
[InlineData("foo[bar]")]
|
|
public void GetFieldValidationState_ReturnsInvalidIfKeyChildContainsErrors(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError(key, "error text");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Invalid, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("foo")]
|
|
[InlineData("foo.bar")]
|
|
[InlineData("foo[bar]")]
|
|
public void GetFieldValidationState_ReturnsValidIfModelStateDoesNotContainErrors(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid(key);
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("[0].foo.bar")]
|
|
[InlineData("[0].foo.bar[0]")]
|
|
public void GetFieldValidationState_IndexedPrefix_ReturnsInvalidIfKeyChildContainsErrors(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError(key, "error text");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("[0].foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Invalid, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("[0].foo.bar")]
|
|
[InlineData("[0].foo.bar[0]")]
|
|
public void GetFieldValidationState_IndexedPrefix_ReturnsValidIfModelStateDoesNotContainErrors(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid(key);
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("[0].foo");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValidPropertyReturnsFalseIfErrors()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("foo");
|
|
dictionary.AddModelError("bar", "some error");
|
|
|
|
// Act
|
|
var isValid = dictionary.IsValid;
|
|
var validationState = dictionary.ValidationState;
|
|
|
|
// Assert
|
|
Assert.False(isValid);
|
|
Assert.Equal(ModelValidationState.Invalid, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValidPropertyReturnsTrueIfNoErrors()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("foo");
|
|
dictionary.MarkFieldSkipped("bar");
|
|
|
|
// Act
|
|
var isValid = dictionary.IsValid;
|
|
var validationState = dictionary.ValidationState;
|
|
|
|
// Assert
|
|
Assert.True(isValid);
|
|
Assert.Equal(ModelValidationState.Valid, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValidPropertyReturnsFalse_IfSomeFieldsAreNotValidated()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("foo");
|
|
dictionary.SetModelValue("qux", "value", "value");
|
|
dictionary.AddModelError("baz", "some error");
|
|
|
|
// Act
|
|
var isValid = dictionary.IsValid;
|
|
var validationState = dictionary.ValidationState;
|
|
|
|
// Assert
|
|
Assert.False(isValid);
|
|
Assert.Equal(ModelValidationState.Unvalidated, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeCopiesDictionaryEntries()
|
|
{
|
|
// Arrange
|
|
var dictionary1 = new ModelStateDictionary();
|
|
dictionary1.SetModelValue("foo", "RawValue1", "AttemptedValue1");
|
|
dictionary1.AddModelError("foo", "value1-Error1");
|
|
dictionary1.AddModelError("foo", "value1-Error2");
|
|
|
|
var dictionary2 = new ModelStateDictionary();
|
|
dictionary2.SetModelValue("bar", "RawValue2", "AttemptedValue2");
|
|
dictionary2.AddModelError("bar", "value2-Error1");
|
|
|
|
// Act
|
|
dictionary1.Merge(dictionary2);
|
|
|
|
// Assert
|
|
Assert.Equal(2, dictionary1.Count);
|
|
var item = dictionary1["foo"];
|
|
Assert.Equal("AttemptedValue1", item.AttemptedValue);
|
|
Assert.Equal("RawValue1", item.RawValue);
|
|
|
|
item = dictionary1["bar"];
|
|
Assert.Equal("AttemptedValue2", item.AttemptedValue);
|
|
Assert.Equal("RawValue2", item.RawValue);
|
|
Assert.Collection(item.Errors,
|
|
error => Assert.Equal("value2-Error1", error.ErrorMessage));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("key1")]
|
|
public void MergeCopiesDictionaryOverwritesExistingValues(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary1 = new ModelStateDictionary();
|
|
dictionary1.SetModelValue(key, "RawValue1", "AttemptedValue1");
|
|
dictionary1.AddModelError(key, "value1-Error1");
|
|
dictionary1.AddModelError(key, "value1-Error2");
|
|
dictionary1.SetModelValue("other-key", null, null);
|
|
|
|
var dictionary2 = new ModelStateDictionary();
|
|
dictionary2.SetModelValue(key, "RawValue2", "AttemptedValue2");
|
|
dictionary2.AddModelError(key, "value2-Error1");
|
|
|
|
// Act
|
|
dictionary1.Merge(dictionary2);
|
|
|
|
// Assert
|
|
Assert.Equal(2, dictionary1.Count);
|
|
var item = dictionary1["other-key"];
|
|
Assert.Null(item.AttemptedValue);
|
|
Assert.Null(item.RawValue);
|
|
Assert.Empty(item.Errors);
|
|
|
|
item = dictionary1[key];
|
|
Assert.Equal("AttemptedValue2", item.AttemptedValue);
|
|
Assert.Equal("RawValue2", item.RawValue);
|
|
Assert.Collection(item.Errors,
|
|
error => Assert.Equal("value2-Error1", error.ErrorMessage));
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeDoesNothingIfParameterIsNull()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("foo", "value", "value");
|
|
|
|
// Act
|
|
dictionary.Merge(null);
|
|
|
|
// Assert
|
|
Assert.Single(dictionary);
|
|
Assert.True(dictionary.ContainsKey("foo"));
|
|
}
|
|
|
|
[Fact]
|
|
public void SetAttemptedValueCreatesModelStateIfNotPresent()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
dictionary.SetModelValue("some key", new string[] { "some value" }, "some value");
|
|
|
|
// Assert
|
|
Assert.Single(dictionary);
|
|
var modelState = dictionary["some key"];
|
|
|
|
Assert.Empty(modelState.Errors);
|
|
Assert.Equal(new string[] { "some value" }, modelState.RawValue);
|
|
Assert.Equal("some value", modelState.AttemptedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetAttemptedValueUsesExistingModelStateIfPresent()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.AddModelError("some key", "some error");
|
|
var ex = new Exception();
|
|
|
|
// Act
|
|
dictionary.SetModelValue("some key", new string[] { "some value" }, "some value");
|
|
|
|
// Assert
|
|
Assert.Single(dictionary);
|
|
var modelState = dictionary["some key"];
|
|
|
|
Assert.Single(modelState.Errors);
|
|
Assert.Equal("some error", modelState.Errors[0].ErrorMessage);
|
|
Assert.Equal(new string[] { "some value" }, modelState.RawValue);
|
|
Assert.Equal("some value", modelState.AttemptedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetFieldValidity_ReturnsUnvalidated_IfNoEntryExistsForKey()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("user.Name", new string[] { "some value" }, "some value");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("not-user");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Unvalidated, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetFieldValidity_ReturnsUnvalidated_IfAnyItemInSubtreeIsInvalid()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("user.Address");
|
|
dictionary.SetModelValue("user.Name", new string[] { "some value" }, "some value");
|
|
dictionary.AddModelError("user.Age", "Age is not a valid int");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("user");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Unvalidated, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("user")]
|
|
[InlineData("user.Age")]
|
|
public void GetFieldValidity_ReturnsInvalid_IfAllKeysAreValidatedAndAnyEntryIsInvalid(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("user.Address");
|
|
dictionary.MarkFieldValid("user.Name");
|
|
dictionary.AddModelError("user.Age", "Age is not a valid int");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState(key);
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Invalid, validationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("[0]")]
|
|
[InlineData("[0].product")]
|
|
public void GetFieldValidity_ReturnsInvalid_IfAllKeysAreValidatedAndAnyEntryIsInvalid_Collection(string key)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("[0].product.Name");
|
|
dictionary.MarkFieldValid("[0].product.Age[0]");
|
|
dictionary.AddModelError("[0].product.Name", "Name is invalid");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState(key);
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Invalid, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetFieldValidity_ReturnsValid_IfAllKeysAreValid()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("user.Address");
|
|
dictionary.MarkFieldValid("user.Name");
|
|
|
|
// Act
|
|
var validationState = dictionary.GetFieldValidationState("user");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, validationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddModelError_WithErrorString_AddsTooManyModelErrors_WhenMaxErrorsIsReached()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 5
|
|
};
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.AddModelError("key1", "error1");
|
|
dictionary.AddModelError("key2", new Exception(), metadata);
|
|
dictionary.AddModelError("key3", new Exception(), metadata);
|
|
dictionary.AddModelError("key4", "error4");
|
|
dictionary.AddModelError("key5", "error5");
|
|
|
|
// Assert
|
|
Assert.True(dictionary.HasReachedMaxErrors);
|
|
Assert.Equal(5, dictionary.ErrorCount);
|
|
var error = Assert.Single(dictionary[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
|
|
// TooManyModelErrorsException added instead of key5 error.
|
|
Assert.DoesNotContain("key5", dictionary.Keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelException_ReturnsFalse_AndAddsMaxModelErrorMessage()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 3
|
|
};
|
|
|
|
// Act and Assert
|
|
var result = dictionary.TryAddModelError("key1", "error1");
|
|
Assert.True(result);
|
|
|
|
result = dictionary.TryAddModelException("key2", new Exception());
|
|
Assert.True(result);
|
|
|
|
result = dictionary.TryAddModelException("key3", new Exception());
|
|
Assert.False(result);
|
|
|
|
Assert.Equal(3, dictionary.Count);
|
|
var error = Assert.Single(dictionary[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
|
|
// TooManyModelErrorsException added instead of key3 exception.
|
|
Assert.DoesNotContain("key3", dictionary.Keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelError_WithErrorString_ReturnsFalse_AndAddsMaxModelErrorMessage()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 3
|
|
};
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act and Assert
|
|
Assert.False(dictionary.HasReachedMaxErrors);
|
|
var result = dictionary.TryAddModelError("key1", "error1");
|
|
Assert.True(result);
|
|
|
|
Assert.False(dictionary.HasReachedMaxErrors);
|
|
result = dictionary.TryAddModelError("key2", new Exception(), metadata);
|
|
Assert.True(result);
|
|
|
|
Assert.False(dictionary.HasReachedMaxErrors); // Still room for TooManyModelErrorsException.
|
|
result = dictionary.TryAddModelError("key3", "error3");
|
|
Assert.False(result);
|
|
|
|
Assert.True(dictionary.HasReachedMaxErrors);
|
|
result = dictionary.TryAddModelError("key4", "error4"); // no-op
|
|
Assert.False(result);
|
|
|
|
Assert.True(dictionary.HasReachedMaxErrors);
|
|
Assert.Equal(3, dictionary.ErrorCount);
|
|
Assert.Equal(3, dictionary.Count);
|
|
|
|
var error = Assert.Single(dictionary[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
|
|
// TooManyModelErrorsException added instead of key3 error.
|
|
Assert.DoesNotContain("key3", dictionary.Keys);
|
|
|
|
// Last addition did nothing.
|
|
Assert.DoesNotContain("key4", dictionary.Keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddModelError_WithException_AddsTooManyModelError_WhenMaxErrorIsReached()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 4
|
|
};
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
dictionary.AddModelError("key1", new Exception(), metadata);
|
|
dictionary.AddModelError("key2", "error2");
|
|
dictionary.AddModelError("key3", "error3");
|
|
dictionary.AddModelError("key3", new Exception(), metadata);
|
|
|
|
// Act and Assert
|
|
Assert.True(dictionary.HasReachedMaxErrors);
|
|
Assert.Equal(4, dictionary.ErrorCount);
|
|
Assert.Equal(4, dictionary.Count);
|
|
var error = Assert.Single(dictionary[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
|
|
// Second key3 model error resulted in TooManyModelErrorsException instead.
|
|
error = Assert.Single(dictionary["key3"].Errors);
|
|
Assert.Null(error.Exception);
|
|
Assert.Equal("error3", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelError_WithException_ReturnsFalse_AndAddsMaxModelErrorMessage()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 3
|
|
};
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act and Assert
|
|
var result = dictionary.TryAddModelError("key1", "error1");
|
|
Assert.True(result);
|
|
|
|
result = dictionary.TryAddModelError("key2", new Exception(), metadata);
|
|
Assert.True(result);
|
|
|
|
result = dictionary.TryAddModelError("key3", new Exception(), metadata);
|
|
Assert.False(result);
|
|
|
|
Assert.Equal(3, dictionary.Count);
|
|
var error = Assert.Single(dictionary[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_TracksAddedErrorsOverCopyConstructor()
|
|
{
|
|
// Arrange
|
|
var expected = "The maximum number of allowed model errors has been reached.";
|
|
var dictionary = new ModelStateDictionary
|
|
{
|
|
MaxAllowedErrors = 3
|
|
};
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.AddModelError("key1", "error1");
|
|
dictionary.TryAddModelError("key3", new Exception(), metadata);
|
|
|
|
var copy = new ModelStateDictionary(dictionary);
|
|
copy.AddModelError("key2", "error2");
|
|
|
|
// Assert
|
|
Assert.Equal(3, copy.Count);
|
|
var error = Assert.Single(copy[string.Empty].Errors);
|
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
|
Assert.Equal(expected, error.Exception.Message);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(2, false)]
|
|
[InlineData(3, true)]
|
|
[InlineData(4, true)]
|
|
public void ModelStateDictionary_HasReachedMaxErrors(int errorCount, bool expected)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary()
|
|
{
|
|
MaxAllowedErrors = 3
|
|
};
|
|
|
|
for (var i = 0; i < errorCount; i++)
|
|
{
|
|
dictionary.AddModelError("key" + i, "error");
|
|
}
|
|
|
|
// Act
|
|
var canAdd = dictionary.HasReachedMaxErrors;
|
|
|
|
// Assert
|
|
Assert.Equal(expected, canAdd);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ReturnExceptionMessage_WhenModelStateNotSet()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
var exception = new FormatException("The supplied value is invalid for Length.");
|
|
|
|
// Act
|
|
dictionary.TryAddModelException("key", exception);
|
|
|
|
// Assert
|
|
var error = Assert.Single(dictionary["key"].Errors);
|
|
Assert.Same(exception, error.Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ReturnGenericErrorMessage_WhenModelStateNotSet()
|
|
{
|
|
// Arrange
|
|
var expected = "The supplied value is invalid for Length.";
|
|
var dictionary = new ModelStateDictionary();
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var error = Assert.Single(dictionary["key"].Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateNotSet()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the supplied value is not valid for Length.";
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(
|
|
name => $"Hmm, the supplied value is not valid for { name }.");
|
|
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateNotSet_WithParameter()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the supplied value is not valid.";
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(
|
|
() => "Hmm, the supplied value is not valid.");
|
|
|
|
var method = typeof(string).GetMethod(nameof(string.Copy));
|
|
var parameter = method.GetParameters()[0]; // Copy(string str)
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForParameter(parameter);
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateNotSet_WithType()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the supplied value is not valid.";
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(
|
|
() => "Hmm, the supplied value is not valid.");
|
|
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForType(typeof(int));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelException_ReturnExceptionMessage_WhenModelStateSet()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
var exception = new FormatException("The value 'some value' is not valid for Length.");
|
|
|
|
// Act
|
|
dictionary.TryAddModelException("key", exception);
|
|
|
|
// Assert
|
|
var error = Assert.Single(dictionary["key"].Errors);
|
|
Assert.Same(exception, error.Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ReturnSpecificErrorMessage_WhenModelStateSet()
|
|
{
|
|
// Arrange
|
|
var expected = "The value 'some value' is not valid for Length.";
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var error = Assert.Single(dictionary["key"].Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateSet()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the value 'some value' is not valid for Length.";
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor(
|
|
(value, name) => $"Hmm, the value '{ value }' is not valid for { name }.");
|
|
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateSet_WithParameter()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the value 'some value' is not valid.";
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
|
|
value => $"Hmm, the value '{ value }' is not valid.");
|
|
|
|
var method = typeof(string).GetMethod(nameof(string.Copy));
|
|
var parameter = method.GetParameters()[0]; // Copy(string str)
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForParameter(parameter);
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateSet_WithType()
|
|
{
|
|
// Arrange
|
|
var expected = "Hmm, the value 'some value' is not valid.";
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var optionsAccessor = new OptionsAccessor();
|
|
optionsAccessor.Value.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
|
|
(value) => $"Hmm, the value '{ value }' is not valid.");
|
|
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, optionsAccessor);
|
|
var metadata = provider.GetMetadataForType(typeof(int));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expected, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_NoErrorMessage_ForUnrecognizedException()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new InvalidOperationException(), metadata);
|
|
|
|
// Assert
|
|
var error = Assert.Single(dictionary["key"].Errors);
|
|
Assert.Empty(error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryAddModelException_AddsErrorMessage_ForInputFormatterException()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
var exception = new InputFormatterException("This is an InputFormatterException.");
|
|
|
|
// Act
|
|
dictionary.TryAddModelException("key", exception);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Same(exception, error.Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_AddsErrorMessage_ForInputFormatterException()
|
|
{
|
|
// Arrange
|
|
var expectedMessage = "This is an InputFormatterException";
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider();
|
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
|
var provider = new DefaultModelMetadataProvider(compositeProvider, new OptionsAccessor());
|
|
var metadata = provider.GetMetadataForType(typeof(int));
|
|
|
|
// Act
|
|
dictionary.TryAddModelError("key", new InputFormatterException(expectedMessage), metadata);
|
|
|
|
// Assert
|
|
var entry = Assert.Single(dictionary);
|
|
Assert.Equal("key", entry.Key);
|
|
var error = Assert.Single(entry.Value.Errors);
|
|
Assert.Equal(expectedMessage, error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ClearEntriesThatMatchWithKey_NonEmptyKey()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Property1");
|
|
dictionary.AddModelError("Property2", "Property2 invalid.");
|
|
dictionary.AddModelError("Property3", "Property invalid.");
|
|
dictionary.MarkFieldSkipped("Property4");
|
|
|
|
// Act
|
|
dictionary.ClearValidationState("Property1");
|
|
dictionary.ClearValidationState("Property2");
|
|
dictionary.ClearValidationState("Property4");
|
|
|
|
// Assert
|
|
Assert.Empty(dictionary["Property1"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property1"].ValidationState);
|
|
Assert.Empty(dictionary["Property2"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property2"].ValidationState);
|
|
Assert.Single(dictionary["Property3"].Errors);
|
|
Assert.Equal(ModelValidationState.Invalid, dictionary["Property3"].ValidationState);
|
|
Assert.Empty(dictionary["Property4"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property4"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ClearEntriesPrefixedWithKey_NonEmptyKey()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Product");
|
|
dictionary.AddModelError("Product.Detail1", "Product Detail1 invalid.");
|
|
dictionary.AddModelError("Product.Detail2[0]", "Product Detail2[0] invalid.");
|
|
dictionary.AddModelError("Product.Detail2[1]", "Product Detail2[1] invalid.");
|
|
dictionary.MarkFieldSkipped("Product.Detail2[2]");
|
|
dictionary.MarkFieldSkipped("Product.Detail3");
|
|
dictionary.AddModelError("ProductName", "ProductName invalid.");
|
|
|
|
// Act
|
|
dictionary.ClearValidationState("Product");
|
|
|
|
// Assert
|
|
Assert.Empty(dictionary["Product"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail1"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail1"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail2[0]"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail2[0]"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail2[1]"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail2[1]"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail2[2]"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail2[2]"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail3"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail3"].ValidationState);
|
|
Assert.Single(dictionary["ProductName"].Errors);
|
|
Assert.Equal(ModelValidationState.Invalid, dictionary["ProductName"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateDictionary_ClearEntries_KeyHasDot_NonEmptyKey()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Product");
|
|
dictionary.AddModelError("Product.Detail1", "Product Detail1 invalid.");
|
|
dictionary.AddModelError("Product.Detail1.Name", "Product Detail1 Name invalid.");
|
|
dictionary.MarkFieldSkipped("Product.Detail1Name");
|
|
|
|
// Act
|
|
dictionary.ClearValidationState("Product.Detail1");
|
|
|
|
// Assert
|
|
Assert.Equal(ModelValidationState.Valid, dictionary["Product"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail1"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail1"].ValidationState);
|
|
Assert.Empty(dictionary["Product.Detail1.Name"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Product.Detail1.Name"].ValidationState);
|
|
Assert.Equal(ModelValidationState.Skipped, dictionary["Product.Detail1Name"].ValidationState);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData(null)]
|
|
public void ModelStateDictionary_ClearsAllEntries_EmptyKey(string modelKey)
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Property1");
|
|
dictionary.AddModelError("Property2", "Property2 invalid.");
|
|
dictionary.AddModelError("Property3", "Property invalid.");
|
|
dictionary.MarkFieldSkipped("Property4");
|
|
|
|
// Act
|
|
dictionary.ClearValidationState(modelKey);
|
|
|
|
// Assert
|
|
Assert.Empty(dictionary["Property1"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property1"].ValidationState);
|
|
Assert.Empty(dictionary["Property2"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property2"].ValidationState);
|
|
Assert.Empty(dictionary["Property3"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property3"].ValidationState);
|
|
Assert.Empty(dictionary["Property4"].Errors);
|
|
Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property4"].ValidationState);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetEnumerable_ReturnsEmptySequenceWhenDictionaryIsEmpty()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act & Assert
|
|
Assert.Empty(dictionary);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetEnumerable_ReturnsAllNonContainerNodes()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Property1");
|
|
dictionary.SetModelValue("Property1.Property2", "value", "value");
|
|
dictionary.AddModelError("Property2", "Property invalid.");
|
|
dictionary.AddModelError("Property2[Property3]", "Property2[Property3] invalid.");
|
|
dictionary.MarkFieldSkipped("Property4");
|
|
dictionary.Remove("Property2");
|
|
|
|
// Act & Assert
|
|
Assert.Collection(
|
|
dictionary,
|
|
entry =>
|
|
{
|
|
Assert.Equal("Property1", entry.Key);
|
|
Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
|
|
Assert.Null(entry.Value.RawValue);
|
|
Assert.Null(entry.Value.AttemptedValue);
|
|
Assert.Empty(entry.Value.Errors);
|
|
},
|
|
entry =>
|
|
{
|
|
Assert.Equal("Property4", entry.Key);
|
|
Assert.Equal(ModelValidationState.Skipped, entry.Value.ValidationState);
|
|
Assert.Null(entry.Value.RawValue);
|
|
Assert.Null(entry.Value.AttemptedValue);
|
|
Assert.Empty(entry.Value.Errors);
|
|
},
|
|
entry =>
|
|
{
|
|
Assert.Equal("Property1.Property2", entry.Key);
|
|
Assert.Equal(ModelValidationState.Unvalidated, entry.Value.ValidationState);
|
|
Assert.Equal("value", entry.Value.RawValue);
|
|
Assert.Equal("value", entry.Value.AttemptedValue);
|
|
Assert.Empty(entry.Value.Errors);
|
|
},
|
|
entry =>
|
|
{
|
|
Assert.Equal("Property2[Property3]", entry.Key);
|
|
Assert.Equal(ModelValidationState.Invalid, entry.Value.ValidationState);
|
|
Assert.Null(entry.Value.RawValue);
|
|
Assert.Null(entry.Value.AttemptedValue);
|
|
Assert.Collection(entry.Value.Errors,
|
|
error => Assert.Equal("Property2[Property3] invalid.", error.ErrorMessage));
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void GetEnumerable_WorksCorrectlyWhenSiblingIsAPrefix()
|
|
{
|
|
// Arrange
|
|
var modelStateDictionary = new ModelStateDictionary();
|
|
modelStateDictionary.SetModelValue("prop", "value1", "value1");
|
|
modelStateDictionary.SetModelValue("property_name", "value3", "value3");
|
|
modelStateDictionary.SetModelValue("property", "value2", "value2");
|
|
|
|
// Act & Assert
|
|
Assert.Collection(modelStateDictionary,
|
|
entry =>
|
|
{
|
|
Assert.Equal("prop", entry.Key);
|
|
Assert.Equal("value1", entry.Value.RawValue);
|
|
},
|
|
entry =>
|
|
{
|
|
Assert.Equal("property", entry.Key);
|
|
Assert.Equal("value2", entry.Value.RawValue);
|
|
},
|
|
entry =>
|
|
{
|
|
Assert.Equal("property_name", entry.Key);
|
|
Assert.Equal("value3", entry.Value.RawValue);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void KeysEnumerable_ReturnsEmptySequenceWhenDictionaryIsEmpty()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
var keys = dictionary.Keys;
|
|
|
|
// Assert
|
|
Assert.Empty(keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void KeysEnumerable_ReturnsAllKeys()
|
|
{
|
|
// Arrange
|
|
var expected = new[] { "Property1", "Property4", "Property1.Property2", "Property2[Property3]" };
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Property1");
|
|
dictionary.AddModelError("Property1.Property2", "Property2 invalid.");
|
|
dictionary.AddModelError("Property2", "Property invalid.");
|
|
dictionary.AddModelError("Property2[Property3]", "Property2[Property3] invalid.");
|
|
dictionary.MarkFieldSkipped("Property4");
|
|
dictionary.Remove("Property2");
|
|
|
|
// Act
|
|
var keys = dictionary.Keys;
|
|
|
|
// Assert
|
|
Assert.Equal(expected, keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValuesEnumerable_ReturnsEmptySequenceWhenDictionaryIsEmpty()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
|
|
// Act
|
|
var values = dictionary.Values;
|
|
|
|
// Assert
|
|
Assert.Empty(values);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValuesEnumerable_ReturnsAllEntries()
|
|
{
|
|
// Arrange
|
|
var dictionary = new ModelStateDictionary();
|
|
dictionary.MarkFieldValid("Property1");
|
|
dictionary.SetModelValue("Property1.Property2", "value", "value");
|
|
dictionary.AddModelError("Property2", "Property invalid.");
|
|
dictionary.AddModelError("Property2[Property3]", "Property2[Property3] invalid.");
|
|
dictionary.MarkFieldSkipped("Property4");
|
|
dictionary.Remove("Property2");
|
|
|
|
// Act & Assert
|
|
Assert.Collection(dictionary.Values,
|
|
value =>
|
|
{
|
|
Assert.Equal(ModelValidationState.Valid, value.ValidationState);
|
|
Assert.Null(value.RawValue);
|
|
Assert.Null(value.AttemptedValue);
|
|
Assert.Empty(value.Errors);
|
|
},
|
|
value =>
|
|
{
|
|
Assert.Equal(ModelValidationState.Skipped, value.ValidationState);
|
|
Assert.Null(value.RawValue);
|
|
Assert.Null(value.AttemptedValue);
|
|
Assert.Empty(value.Errors);
|
|
},
|
|
value =>
|
|
{
|
|
Assert.Equal(ModelValidationState.Unvalidated, value.ValidationState);
|
|
Assert.Equal("value", value.RawValue);
|
|
Assert.Equal("value", value.AttemptedValue);
|
|
Assert.Empty(value.Errors);
|
|
},
|
|
value =>
|
|
{
|
|
Assert.Equal(ModelValidationState.Invalid, value.ValidationState);
|
|
Assert.Null(value.RawValue);
|
|
Assert.Null(value.AttemptedValue);
|
|
Assert.Collection(
|
|
value.Errors,
|
|
error => Assert.Equal("Property2[Property3] invalid.", error.ErrorMessage));
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelStateForProperty_ReturnsModelStateForImmediateChildren()
|
|
{
|
|
// Arrange
|
|
var modelStateDictionary = new ModelStateDictionary();
|
|
modelStateDictionary.SetModelValue("property1", "value1", "value1");
|
|
modelStateDictionary.SetModelValue("property1.property2", "value2", "value2");
|
|
|
|
// Act 1
|
|
var property1 = modelStateDictionary.Root.GetModelStateForProperty("property1");
|
|
var property2 = modelStateDictionary.Root.GetModelStateForProperty("property1.property2");
|
|
|
|
// Assert 1
|
|
Assert.Equal("value1", property1.RawValue);
|
|
Assert.Null(property2);
|
|
|
|
// Act 2
|
|
property2 = property1.GetModelStateForProperty("property2");
|
|
Assert.Equal("value2", property2.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelStateForProperty_ReturnsModelStateForIndexedChildren()
|
|
{
|
|
// Arrange
|
|
var modelStateDictionary = new ModelStateDictionary();
|
|
modelStateDictionary.SetModelValue("[property]", "value1", "value1");
|
|
|
|
// Act
|
|
var property = modelStateDictionary.Root.GetModelStateForProperty("[property]");
|
|
|
|
// Assert
|
|
Assert.Equal("value1", property.RawValue);
|
|
}
|
|
|
|
private class OptionsAccessor : IOptions<MvcOptions>
|
|
{
|
|
public MvcOptions Value { get; } = new MvcOptions();
|
|
}
|
|
}
|
|
|
|
internal class TestException : Exception
|
|
{
|
|
public TestException()
|
|
{
|
|
Message = "This is a test exception";
|
|
}
|
|
|
|
public override string Message { get; }
|
|
}
|
|
}
|