305 lines
14 KiB
C#
305 lines
14 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.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net.Http.Formatting;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Testing.xunit;
|
|
using Newtonsoft.Json.Linq;
|
|
using Xunit;
|
|
|
|
namespace System.Web.Http.Dispatcher
|
|
{
|
|
public class HttpErrorTest
|
|
{
|
|
public static IEnumerable<object[]> ErrorKeyValue
|
|
{
|
|
get
|
|
{
|
|
var httpError = new HttpError();
|
|
yield return new object[] { httpError, (Func<string>)(() => httpError.Message), "Message", "Message_Value" };
|
|
yield return new object[] { httpError, (Func<string>)(() => httpError.MessageDetail), "MessageDetail", "MessageDetail_Value" };
|
|
yield return new object[] { httpError, (Func<string>)(() => httpError.ExceptionMessage), "ExceptionMessage", "ExceptionMessage_Value" };
|
|
yield return new object[] { httpError, (Func<string>)(() => httpError.ExceptionType), "ExceptionType", "ExceptionType_Value" };
|
|
yield return new object[] { httpError, (Func<string>)(() => httpError.StackTrace), "StackTrace", "StackTrace_Value" };
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<object[]> HttpErrors
|
|
{
|
|
get
|
|
{
|
|
yield return new[] { new HttpError() };
|
|
yield return new[] { new HttpError("error") };
|
|
yield return new[] { new HttpError(new NotImplementedException(), true) };
|
|
|
|
var modelState = new ModelStateDictionary();
|
|
modelState.AddModelError("key", "error");
|
|
yield return new[] { new HttpError(modelState, true) };
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void StringConstructor_AddsCorrectDictionaryItems()
|
|
{
|
|
HttpError error = new HttpError("something bad happened");
|
|
|
|
Assert.Contains(new KeyValuePair<string, object>("Message", "something bad happened"), error);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExceptionConstructorWithDetail_AddsCorrectDictionaryItems()
|
|
{
|
|
HttpError error = new HttpError(new ArgumentException("error", new Exception()), true);
|
|
|
|
Assert.Contains(new KeyValuePair<string, object>("Message", "An error has occurred."), error);
|
|
Assert.Contains(new KeyValuePair<string, object>("ExceptionMessage", "error"), error);
|
|
Assert.Contains(new KeyValuePair<string, object>("ExceptionType", "System.ArgumentException"), error);
|
|
Assert.True(error.ContainsKey("StackTrace"));
|
|
Assert.True(error.ContainsKey("InnerException"));
|
|
Assert.IsType<HttpError>(error["InnerException"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateConstructorWithDetail_AddsCorrectDictionaryItems()
|
|
{
|
|
// Arrange
|
|
ModelStateDictionary modelState = new ModelStateDictionary();
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
modelState.AddModelError("[0].Name", "error1");
|
|
modelState.AddModelError("[0].Name", "error2");
|
|
modelState.AddModelError("[0].Address", "error");
|
|
modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata);
|
|
|
|
// Act
|
|
HttpError error = new HttpError(modelState, true);
|
|
|
|
// Assert
|
|
HttpError modelStateError = error["ModelState"] as HttpError;
|
|
|
|
Assert.Contains(new KeyValuePair<string, object>("Message", "The request is invalid."), error);
|
|
Assert.Contains("error1", modelStateError["[0].Name"] as IEnumerable<string>);
|
|
Assert.Contains("error2", modelStateError["[0].Name"] as IEnumerable<string>);
|
|
Assert.Contains("error", modelStateError["[0].Address"] as IEnumerable<string>);
|
|
Assert.True(modelStateError.ContainsKey("[2].Name"));
|
|
Assert.Contains("OH NO", modelStateError["[2].Name"] as IEnumerable<string>);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExceptionConstructorWithoutDetail_AddsCorrectDictionaryItems()
|
|
{
|
|
HttpError error = new HttpError(new ArgumentException("error", new Exception()), false);
|
|
|
|
Assert.Contains(new KeyValuePair<string, object>("Message", "An error has occurred."), error);
|
|
Assert.False(error.ContainsKey("ExceptionMessage"));
|
|
Assert.False(error.ContainsKey("ExceptionType"));
|
|
Assert.False(error.ContainsKey("StackTrace"));
|
|
Assert.False(error.ContainsKey("InnerException"));
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelStateConstructorWithoutDetail_AddsCorrectDictionaryItems()
|
|
{
|
|
// Arrange
|
|
ModelStateDictionary modelState = new ModelStateDictionary();
|
|
var provider = new EmptyModelMetadataProvider();
|
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
|
modelState.AddModelError("[0].Name", "error1");
|
|
modelState.AddModelError("[0].Name", "error2");
|
|
modelState.AddModelError("[0].Address", "error");
|
|
modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata);
|
|
|
|
// Act
|
|
HttpError error = new HttpError(modelState, false);
|
|
|
|
// Assert
|
|
HttpError modelStateError = error["ModelState"] as HttpError;
|
|
|
|
Assert.Contains(new KeyValuePair<string, object>("Message", "The request is invalid."), error);
|
|
Assert.Contains("error1", modelStateError["[0].Name"] as IEnumerable<string>);
|
|
Assert.Contains("error2", modelStateError["[0].Name"] as IEnumerable<string>);
|
|
Assert.Contains("error", modelStateError["[0].Address"] as IEnumerable<string>);
|
|
Assert.True(modelStateError.ContainsKey("[2].Name"));
|
|
Assert.DoesNotContain("OH NO", modelStateError["[2].Name"] as IEnumerable<string>);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpError_Roundtrips_WithJsonFormatter()
|
|
{
|
|
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
|
|
MediaTypeFormatter formatter = new JsonMediaTypeFormatter();
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
|
|
stream.Position = 0;
|
|
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
|
|
|
|
Assert.NotNull(roundtrippedError);
|
|
Assert.Equal("error", roundtrippedError.Message);
|
|
Assert.Equal(42L, roundtrippedError["ErrorCode"]);
|
|
JArray data = roundtrippedError["Data"] as JArray;
|
|
Assert.Equal(3, data.Count);
|
|
Assert.Contains("a", data);
|
|
Assert.Contains("b", data);
|
|
Assert.Contains("c", data);
|
|
}
|
|
|
|
[ConditionalFact]
|
|
// Mono issue - https://github.com/aspnet/External/issues/25
|
|
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
|
public void HttpError_Roundtrips_WithXmlFormatter()
|
|
{
|
|
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
|
|
MediaTypeFormatter formatter = new XmlMediaTypeFormatter();
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
|
|
stream.Position = 0;
|
|
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
|
|
|
|
Assert.NotNull(roundtrippedError);
|
|
Assert.Equal("error", roundtrippedError.Message);
|
|
Assert.Equal("42", roundtrippedError["ErrorCode"]);
|
|
Assert.Equal("a b c", roundtrippedError["Data"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpErrorWithWhitespace_Roundtrips_WithXmlFormatter()
|
|
{
|
|
string message = " foo\n bar \n ";
|
|
HttpError error = new HttpError(message);
|
|
MediaTypeFormatter formatter = new XmlMediaTypeFormatter();
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
|
|
stream.Position = 0;
|
|
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
|
|
|
|
Assert.NotNull(roundtrippedError);
|
|
Assert.Equal(message, roundtrippedError.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpError_Roundtrips_WithXmlSerializer()
|
|
{
|
|
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
|
|
MediaTypeFormatter formatter = new XmlMediaTypeFormatter() { UseXmlSerializer = true };
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
|
|
stream.Position = 0;
|
|
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
|
|
|
|
Assert.NotNull(roundtrippedError);
|
|
Assert.Equal("error", roundtrippedError.Message);
|
|
Assert.Equal("42", roundtrippedError["ErrorCode"]);
|
|
Assert.Equal("a b c", roundtrippedError["Data"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpErrorForInnerException_Serializes_WithXmlSerializer()
|
|
{
|
|
HttpError error = new HttpError(new ArgumentException("error", new Exception("innerError")), includeErrorDetail: true);
|
|
MediaTypeFormatter formatter = new XmlMediaTypeFormatter() { UseXmlSerializer = true };
|
|
MemoryStream stream = new MemoryStream();
|
|
|
|
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
|
|
stream.Position = 0;
|
|
string serializedError = new StreamReader(stream).ReadToEnd();
|
|
|
|
Assert.NotNull(serializedError);
|
|
Assert.Equal(
|
|
"<Error><Message>An error has occurred.</Message><ExceptionMessage>error</ExceptionMessage><ExceptionType>System.ArgumentException</ExceptionType><StackTrace /><InnerException><Message>An error has occurred.</Message><ExceptionMessage>innerError</ExceptionMessage><ExceptionType>System.Exception</ExceptionType><StackTrace /></InnerException></Error>",
|
|
serializedError);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValue_GetsValue_IfTypeMatches()
|
|
{
|
|
HttpError error = new HttpError();
|
|
error["key"] = "x";
|
|
|
|
Assert.Equal("x", error.GetPropertyValue<string>("key"));
|
|
Assert.Equal("x", error.GetPropertyValue<object>("key"));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValue_GetsDefault_IfTypeDoesNotMatch()
|
|
{
|
|
HttpError error = new HttpError();
|
|
error["key"] = "x";
|
|
|
|
Assert.Null(error.GetPropertyValue<Uri>("key"));
|
|
Assert.Equal(0, error.GetPropertyValue<int>("key"));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValue_GetsDefault_IfPropertyMissing()
|
|
{
|
|
HttpError error = new HttpError();
|
|
|
|
Assert.Null(error.GetPropertyValue<string>("key"));
|
|
Assert.Equal(0, error.GetPropertyValue<int>("key"));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(ErrorKeyValue))]
|
|
public void HttpErrorStringProperties_UseCorrectHttpErrorKey(HttpError httpError, Func<string> productUnderTest, string key, string actualValue)
|
|
{
|
|
// Arrange
|
|
httpError[key] = actualValue;
|
|
|
|
// Act
|
|
string expectedValue = productUnderTest.Invoke();
|
|
|
|
// Assert
|
|
Assert.Equal(expectedValue, actualValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpErrorProperty_InnerException_UsesCorrectHttpErrorKey()
|
|
{
|
|
// Arrange
|
|
HttpError error = new HttpError(new ArgumentException("error", new Exception()), true);
|
|
|
|
// Act
|
|
HttpError innerException = error.InnerException;
|
|
|
|
// Assert
|
|
Assert.Same(error["InnerException"], innerException);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpErrorProperty_ModelState_UsesCorrectHttpErrorKey()
|
|
{
|
|
// Arrange
|
|
ModelStateDictionary modelState = new ModelStateDictionary();
|
|
modelState.AddModelError("[0].Name", "error1");
|
|
HttpError error = new HttpError(modelState, true);
|
|
|
|
// Act
|
|
HttpError actualModelStateError = error.ModelState;
|
|
|
|
// Assert
|
|
Assert.Same(error["ModelState"], actualModelStateError);
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(HttpErrors))]
|
|
public void HttpErrors_UseCaseInsensitiveComparer(HttpError httpError)
|
|
{
|
|
// Arrange
|
|
var lowercaseKey = "abcd";
|
|
var uppercaseKey = "ABCD";
|
|
|
|
httpError[lowercaseKey] = "error";
|
|
|
|
// Act & Assert
|
|
Assert.True(httpError.ContainsKey(lowercaseKey));
|
|
Assert.True(httpError.ContainsKey(uppercaseKey));
|
|
}
|
|
}
|
|
}
|