Skip to content

Commit

Permalink
Add ValidationApiException support for Refit (#875)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonCropp authored Oct 20, 2024
1 parent f7a1530 commit 949aef6
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ApiExceptionDestructurer(bool destructureCommonExceptionProperties = true
}

/// <inheritdoc cref="IExceptionDestructurer.TargetTypes"/>
public override Type[] TargetTypes => [typeof(ApiException)];
public override Type[] TargetTypes => [typeof(ApiException), typeof(ValidationApiException)];

/// <inheritdoc />
public override void Destructure(Exception exception, IExceptionPropertiesBag propertiesBag, Func<Exception, IReadOnlyDictionary<string, object?>?> destructureException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace Serilog.Exceptions.Test.Destructurers;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using global::Refit;
using Serilog.Exceptions.Core;
Expand All @@ -12,87 +13,174 @@ namespace Serilog.Exceptions.Test.Destructurers;

public class ApiExceptionDestructurerTest
{
private static Uri requestUri = new("https://foobar.com");

[Fact]
public async Task ApiException_HttpStatusCodeIsLoggedAsPropertyAsync()
{
using var message = new HttpRequestMessage(HttpMethod.Get, new Uri("https://foobar.com"));
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer()]);
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildApiException();

Test_LoggedExceptionContainsProperty(exception, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options);
}

[Fact]
public async Task ValidationApiException_HttpStatusCodeIsLoggedAsPropertyAsync()
{
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();

Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options);
Test_LoggedExceptionContainsProperty(exception, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options);
}

[Fact]
public async Task ApiException_UriIsLoggedAsPropertyAsync()
{
var requestUri = new Uri("https://foobar.com");
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer()]);
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildApiException();
Test_LoggedExceptionContainsProperty(exception, nameof(ApiException.Uri), requestUri.ToString(), options);
}

Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Uri), requestUri.ToString(), options);
[Fact]
public async Task ValidationApi_UriIsLoggedAsPropertyAsync()
{
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();
Test_LoggedExceptionContainsProperty(exception, nameof(ApiException.Uri), requestUri.ToString(), options);
}

[Fact]
public async Task ApiException_ByDefaultContentIsNotLoggedAsPropertyAsync()
{
var requestUri = new Uri("https://foobar.com");
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer()]);
response.Content = JsonContent.Create("hello");

var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildApiException("hello");
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(ApiException.Content), options);
}

Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(ApiException.Content), options);
[Fact]
public async Task ValidationApiException_ByDefaultContentIsNotLoggedAsPropertyAsync()
{
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(ApiException.Content), options);
}

[Fact]
public async Task ApiException_WhenSpecifiedContentIsLoggedAsPropertyAsync()
{
var requestUri = new Uri("https://foobar.com");
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer(destructureHttpContent: true)]);
response.Content = JsonContent.Create("hello");
var destructurer = new ApiExceptionDestructurer(destructureHttpContent: true);
var options = BuildOptions(destructurer);
var exception = await BuildApiException("hello");

var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
Test_LoggedExceptionContainsProperty(exception, nameof(ApiException.Content), "\"hello\"", options);
}

[Fact]
public async Task ValidationApiException_WhenSpecifiedContentIsLoggedAsPropertyAsync()
{
var destructurer = new ApiExceptionDestructurer(destructureHttpContent: true);
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();

Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Content), "\"hello\"", options);
Test_LoggedExceptionContainsProperty(
exception,
nameof(ApiException.Content),
"{\"Errors\":{},\"Type\":\"about:blank\",\"Title\":\"title\",\"Status\":0,\"Detail\":null,\"Instance\":null}",
options);
}

[Fact]
public async Task ApiException_ByDefaultCommonPropertiesLoggedAsPropertiesAsync()
{
var requestUri = new Uri("https://foobar.com");
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer()]);
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildApiException();

// No need to test all properties, just a handful is sufficient
Test_LoggedExceptionContainsProperty(exception, nameof(Exception.StackTrace), exception.StackTrace, options);
Test_LoggedExceptionContainsProperty(exception, nameof(Exception.Message), exception.Message, options);
Test_LoggedExceptionContainsProperty(exception, nameof(Type), exception.GetType().ToString(), options);
}

[Fact]
public async Task ValidationApiException_ByDefaultCommonPropertiesLoggedAsPropertiesAsync()
{
var destructurer = new ApiExceptionDestructurer();
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();

// No need to test all properties, just a handful is sufficient
Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.StackTrace), apiException.StackTrace, options);
Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.Message), apiException.Message, options);
Test_LoggedExceptionContainsProperty(apiException, nameof(Type), apiException.GetType().ToString(), options);
Test_LoggedExceptionContainsProperty(exception, nameof(Exception.StackTrace), exception.StackTrace, options);
Test_LoggedExceptionContainsProperty(exception, nameof(Exception.Message), exception.Message, options);
Test_LoggedExceptionContainsProperty(exception, nameof(Type), exception.GetType().ToString(), options);
}

[Fact]
public async Task ApiException_WhenSpecifiedCommonPropertiesNotLoggedAsPropertiesAsync()
{
var requestUri = new Uri("https://foobar.com");
var destructurer = new ApiExceptionDestructurer(destructureCommonExceptionProperties: false);
var options = BuildOptions(destructurer);
var exception = await BuildApiException();

Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.StackTrace), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.Message), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.InnerException), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.HelpLink), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.Data), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.HResult), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Type), options);
}

[Fact]
public async Task ValidationApiException_WhenSpecifiedCommonPropertiesNotLoggedAsPropertiesAsync()
{
var destructurer = new ApiExceptionDestructurer(destructureCommonExceptionProperties: false);
var options = BuildOptions(destructurer);
var exception = await BuildValidationApiException();

Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.StackTrace), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.Message), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.InnerException), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.HelpLink), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.Data), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Exception.HResult), options);
Test_LoggedExceptionDoesNotContainProperty(exception, nameof(Type), options);
}

private static DestructuringOptionsBuilder BuildOptions(ApiExceptionDestructurer destructurer) => new DestructuringOptionsBuilder()
.WithDestructurers([destructurer]);

private static async Task<ApiException> BuildApiException(string? content = null)
{
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var options = new DestructuringOptionsBuilder().WithDestructurers([new ApiExceptionDestructurer(destructureCommonExceptionProperties: false)]);
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
if (content != null)
{
response.Content = JsonContent.Create(content);
}

return await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
}

private static async Task<ValidationApiException> BuildValidationApiException()
{
var content = JsonSerializer.Serialize(
new ProblemDetails
{
Title = "title",
});

Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.StackTrace), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Message), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.InnerException), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HelpLink), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Data), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HResult), options);
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Type), options);
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri);
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(content);

var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings());
return ValidationApiException.Create(apiException);
}
}

0 comments on commit 949aef6

Please sign in to comment.