Skip to content

Commit 4bdb3e8

Browse files
author
Bart Koelman
committed
Refactored tests for custom exception handler
1 parent 90274c6 commit 4bdb3e8

File tree

8 files changed

+225
-80
lines changed

8 files changed

+225
-80
lines changed

test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomErrorHandlingTests.cs

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Middleware;
4+
using JsonApiDotNetCore.Serialization.Objects;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
8+
{
9+
public sealed class AlternateExceptionHandler : ExceptionHandler
10+
{
11+
public AlternateExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options)
12+
: base(loggerFactory, options)
13+
{
14+
}
15+
16+
protected override LogLevel GetLogLevel(Exception exception)
17+
{
18+
if (exception is ConsumerArticleIsNoLongerAvailableException)
19+
{
20+
return LogLevel.Warning;
21+
}
22+
23+
return base.GetLogLevel(exception);
24+
}
25+
26+
protected override ErrorDocument CreateErrorDocument(Exception exception)
27+
{
28+
if (exception is ConsumerArticleIsNoLongerAvailableException articleException)
29+
{
30+
articleException.Errors[0].Meta.Data.Add("support",
31+
$"Please contact us for info about similar articles at {articleException.SupportEmailAddress}.");
32+
}
33+
34+
return base.CreateErrorDocument(exception);
35+
}
36+
}
37+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using JsonApiDotNetCore.Resources;
2+
using JsonApiDotNetCore.Resources.Annotations;
3+
4+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
5+
{
6+
public sealed class ConsumerArticle : Identifiable
7+
{
8+
[Attr]
9+
public string Code { get; set; }
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Net;
2+
using JsonApiDotNetCore.Errors;
3+
using JsonApiDotNetCore.Serialization.Objects;
4+
5+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
6+
{
7+
public sealed class ConsumerArticleIsNoLongerAvailableException : JsonApiException
8+
{
9+
public string ArticleCode { get; }
10+
public string SupportEmailAddress { get; }
11+
12+
public ConsumerArticleIsNoLongerAvailableException(string articleCode, string supportEmailAddress)
13+
: base(new Error(HttpStatusCode.Gone)
14+
{
15+
Title = "The requested article is no longer available.",
16+
Detail = $"Article with code '{articleCode}' is no longer available."
17+
})
18+
{
19+
ArticleCode = articleCode;
20+
SupportEmailAddress = supportEmailAddress;
21+
}
22+
}
23+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.Configuration;
4+
using JsonApiDotNetCore.Hooks;
5+
using JsonApiDotNetCore.Middleware;
6+
using JsonApiDotNetCore.Queries;
7+
using JsonApiDotNetCore.Repositories;
8+
using JsonApiDotNetCore.Resources;
9+
using JsonApiDotNetCore.Services;
10+
using Microsoft.Extensions.Logging;
11+
12+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
13+
{
14+
public sealed class ConsumerArticleService : JsonApiResourceService<ConsumerArticle>
15+
{
16+
private const string _supportEmailAddress = "company@email.com";
17+
18+
public ConsumerArticleService(IResourceRepositoryAccessor repositoryAccessor, IQueryLayerComposer queryLayerComposer,
19+
IPaginationContext paginationContext, IJsonApiOptions options, ILoggerFactory loggerFactory,
20+
IJsonApiRequest request, IResourceChangeTracker<ConsumerArticle> resourceChangeTracker,
21+
IResourceHookExecutorFacade hookExecutor)
22+
: base(repositoryAccessor, queryLayerComposer, paginationContext, options, loggerFactory, request,
23+
resourceChangeTracker, hookExecutor)
24+
{
25+
}
26+
27+
public override async Task<ConsumerArticle> GetAsync(int id, CancellationToken cancellationToken)
28+
{
29+
var consumerArticle = await base.GetAsync(id, cancellationToken);
30+
31+
if (consumerArticle.Code.StartsWith("X"))
32+
{
33+
throw new ConsumerArticleIsNoLongerAvailableException(consumerArticle.Code, _supportEmailAddress);
34+
}
35+
36+
return consumerArticle;
37+
}
38+
}
39+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
7+
{
8+
public sealed class ConsumerArticlesController : JsonApiController<ConsumerArticle>
9+
{
10+
public ConsumerArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
11+
IResourceService<ConsumerArticle> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
4+
{
5+
public sealed class ErrorDbContext : DbContext
6+
{
7+
public DbSet<ConsumerArticle> ConsumerArticles { get; set; }
8+
9+
public ErrorDbContext(DbContextOptions<ErrorDbContext> options)
10+
: base(options)
11+
{
12+
}
13+
}
14+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System.Linq;
2+
using System.Net;
3+
using System.Threading.Tasks;
4+
using FluentAssertions;
5+
using JsonApiDotNetCore.Configuration;
6+
using JsonApiDotNetCore.Middleware;
7+
using JsonApiDotNetCore.Serialization.Objects;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Logging;
10+
using Xunit;
11+
12+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling
13+
{
14+
public sealed class ExceptionHandlerTests
15+
: IClassFixture<IntegrationTestContext<TestableStartup<ErrorDbContext>, ErrorDbContext>>
16+
{
17+
private readonly IntegrationTestContext<TestableStartup<ErrorDbContext>, ErrorDbContext> _testContext;
18+
19+
public ExceptionHandlerTests(IntegrationTestContext<TestableStartup<ErrorDbContext>, ErrorDbContext> testContext)
20+
{
21+
_testContext = testContext;
22+
23+
FakeLoggerFactory loggerFactory = null;
24+
25+
testContext.ConfigureLogging(options =>
26+
{
27+
loggerFactory = new FakeLoggerFactory();
28+
29+
options.ClearProviders();
30+
options.AddProvider(loggerFactory);
31+
options.SetMinimumLevel(LogLevel.Warning);
32+
});
33+
34+
testContext.ConfigureServicesBeforeStartup(services =>
35+
{
36+
if (loggerFactory != null)
37+
{
38+
services.AddSingleton(_ => loggerFactory);
39+
}
40+
});
41+
42+
testContext.ConfigureServicesAfterStartup(services =>
43+
{
44+
services.AddResourceService<ConsumerArticleService>();
45+
services.AddScoped<IExceptionHandler, AlternateExceptionHandler>();
46+
});
47+
}
48+
49+
[Fact]
50+
public async Task Logs_and_produces_error_response_for_custom_exception()
51+
{
52+
// Arrange
53+
var loggerFactory = _testContext.Factory.Services.GetRequiredService<FakeLoggerFactory>();
54+
loggerFactory.Logger.Clear();
55+
56+
var consumerArticle = new ConsumerArticle
57+
{
58+
Code = "X123"
59+
};
60+
61+
await _testContext.RunOnDatabaseAsync(async dbContext =>
62+
{
63+
dbContext.ConsumerArticles.Add(consumerArticle);
64+
await dbContext.SaveChangesAsync();
65+
});
66+
67+
var route = "/consumerArticles/" + consumerArticle.StringId;
68+
69+
// Act
70+
var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync<ErrorDocument>(route);
71+
72+
// Assert
73+
httpResponse.Should().HaveStatusCode(HttpStatusCode.Gone);
74+
75+
responseDocument.Errors.Should().HaveCount(1);
76+
responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Gone);
77+
responseDocument.Errors[0].Title.Should().Be("The requested article is no longer available.");
78+
responseDocument.Errors[0].Detail.Should().Be("Article with code 'X123' is no longer available.");
79+
responseDocument.Errors[0].Meta.Data["support"].Should().Be("Please contact us for info about similar articles at company@email.com.");
80+
81+
loggerFactory.Logger.Messages.Should().HaveCount(1);
82+
loggerFactory.Logger.Messages.Single().Text.Should().Contain("Article with code 'X123' is no longer available.");
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)