From 355083cef819a07aa1df23cdcac3da3f0805f792 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 26 Jan 2021 11:21:39 +0100 Subject: [PATCH 01/47] Refactored tests for HttpReadOnly/NoHttpPost/Patch/Delete controller attributes --- docs/usage/extensibility/controllers.md | 2 +- .../Restricted/ReadOnlyController.cs | 106 --------------- .../HttpReadOnlyTests.cs | 106 --------------- .../NoHttpDeleteTests.cs | 92 ------------- .../NoHttpPatchTests.cs | 92 ------------- .../HttpMethodRestrictions/NoHttpPostTests.cs | 92 ------------- .../RestrictedControllers/Bed.cs | 8 ++ .../BlockingHttpDeleteController.cs | 18 +++ .../BlockingHttpPatchController.cs | 18 +++ .../BlockingHttpPostController.cs | 18 +++ .../BlockingWritesController.cs | 18 +++ .../RestrictedControllers/Chair.cs | 9 ++ .../HttpReadOnlyTests.cs | 126 ++++++++++++++++++ .../NoHttpDeleteTests.cs | 116 ++++++++++++++++ .../RestrictedControllers/NoHttpPatchTests.cs | 116 ++++++++++++++++ .../RestrictedControllers/NoHttpPostTests.cs | 116 ++++++++++++++++ .../RestrictionDbContext.cs | 17 +++ .../RestrictionFakers.cs | 29 ++++ .../RestrictedControllers/Sofa.cs | 8 ++ .../RestrictedControllers/Table.cs | 9 ++ 20 files changed, 627 insertions(+), 489 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/Restricted/ReadOnlyController.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/HttpReadOnlyTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpDeleteTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPatchTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPostTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs diff --git a/docs/usage/extensibility/controllers.md b/docs/usage/extensibility/controllers.md index 125c6a23a1..7ff26077e5 100644 --- a/docs/usage/extensibility/controllers.md +++ b/docs/usage/extensibility/controllers.md @@ -74,7 +74,7 @@ The next option is to use the ActionFilter attributes that ship with the library - `HttpReadOnly`: all of the above Not only does this reduce boilerplate, but it also provides a more meaningful HTTP response code. -An attempt to use one blacklisted methods will result in a HTTP 405 Method Not Allowed response. +An attempt to use one of the blacklisted methods will result in a HTTP 405 Method Not Allowed response. ```c# [HttpReadOnly] diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/Restricted/ReadOnlyController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/Restricted/ReadOnlyController.cs deleted file mode 100644 index cbdbdbc2a8..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/Restricted/ReadOnlyController.cs +++ /dev/null @@ -1,106 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreExample.Controllers.Restricted -{ - [DisableRoutingConvention, Route("[controller]")] - [HttpReadOnly] - public class ReadOnlyController : BaseJsonApiController
- { - public ReadOnlyController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService
resourceService) - : base(options, loggerFactory, resourceService) - { } - - [HttpGet] - public IActionResult Get() => Ok(); - - [HttpPost] - public IActionResult Post() => Ok(); - - [HttpPatch] - public IActionResult Patch() => Ok(); - - [HttpDelete] - public IActionResult Delete() => Ok(); - } - - [DisableRoutingConvention, Route("[controller]")] - [NoHttpPost] - public class NoHttpPostController : BaseJsonApiController
- { - public NoHttpPostController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService
resourceService) - : base(options, loggerFactory, resourceService) - { } - - [HttpGet] - public IActionResult Get() => Ok(); - - [HttpPost] - public IActionResult Post() => Ok(); - - [HttpPatch] - public IActionResult Patch() => Ok(); - - [HttpDelete] - public IActionResult Delete() => Ok(); - } - - [DisableRoutingConvention, Route("[controller]")] - [NoHttpPatch] - public class NoHttpPatchController : BaseJsonApiController
- { - public NoHttpPatchController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService
resourceService) - : base(options, loggerFactory, resourceService) - { } - - [HttpGet] - public IActionResult Get() => Ok(); - - [HttpPost] - public IActionResult Post() => Ok(); - - [HttpPatch] - public IActionResult Patch() => Ok(); - - [HttpDelete] - public IActionResult Delete() => Ok(); - } - - [DisableRoutingConvention, Route("[controller]")] - [NoHttpDelete] - public class NoHttpDeleteController : BaseJsonApiController
- { - public NoHttpDeleteController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService
resourceService) - : base(options, loggerFactory, resourceService) - { } - - [HttpGet] - public IActionResult Get() => Ok(); - - [HttpPost] - public IActionResult Post() => Ok(); - - [HttpPatch] - public IActionResult Patch() => Ok(); - - [HttpDelete] - public IActionResult Delete() => Ok(); - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/HttpReadOnlyTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/HttpReadOnlyTests.cs deleted file mode 100644 index 16f660e3f6..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/HttpReadOnlyTests.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public sealed class HttpReadOnlyTests - { - [Fact] - public async Task Allows_GET_Requests() - { - // Arrange - const string route = "readonly"; - const string method = "GET"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Rejects_POST_Requests() - { - // Arrange - const string route = "readonly"; - const string method = "POST"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support POST requests.", errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Rejects_PATCH_Requests() - { - // Arrange - const string route = "readonly"; - const string method = "PATCH"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support PATCH requests.", errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Rejects_DELETE_Requests() - { - // Arrange - const string route = "readonly"; - const string method = "DELETE"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support DELETE requests.", errorDocument.Errors[0].Detail); - } - - private async Task MakeRequestAsync(string route, string method) - { - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod(method); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - var response = await client.SendAsync(request); - return response; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpDeleteTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpDeleteTests.cs deleted file mode 100644 index 7103dc3a2c..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpDeleteTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public sealed class NoHttpDeleteTests - { - [Fact] - public async Task Allows_GET_Requests() - { - // Arrange - const string route = "nohttpdelete"; - const string method = "GET"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Allows_POST_Requests() - { - // Arrange - const string route = "nohttpdelete"; - const string method = "POST"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Allows_PATCH_Requests() - { - // Arrange - const string route = "nohttpdelete"; - const string method = "PATCH"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Rejects_DELETE_Requests() - { - // Arrange - const string route = "nohttpdelete"; - const string method = "DELETE"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support DELETE requests.", errorDocument.Errors[0].Detail); - } - - private async Task MakeRequestAsync(string route, string method) - { - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod(method); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - var response = await client.SendAsync(request); - return response; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPatchTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPatchTests.cs deleted file mode 100644 index 7d26157c46..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPatchTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public sealed class NoHttpPatchTests - { - [Fact] - public async Task Allows_GET_Requests() - { - // Arrange - const string route = "nohttppatch"; - const string method = "GET"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Allows_POST_Requests() - { - // Arrange - const string route = "nohttppatch"; - const string method = "POST"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Rejects_PATCH_Requests() - { - // Arrange - const string route = "nohttppatch"; - const string method = "PATCH"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support PATCH requests.", errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Allows_DELETE_Requests() - { - // Arrange - const string route = "nohttppatch"; - const string method = "DELETE"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - private async Task MakeRequestAsync(string route, string method) - { - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod(method); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - var response = await client.SendAsync(request); - return response; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPostTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPostTests.cs deleted file mode 100644 index 23d63eca51..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/HttpMethodRestrictions/NoHttpPostTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public sealed class NoHttpPostTests - { - [Fact] - public async Task Allows_GET_Requests() - { - // Arrange - const string route = "nohttppost"; - const string method = "GET"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Rejects_POST_Requests() - { - // Arrange - const string route = "nohttppost"; - const string method = "POST"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.MethodNotAllowed, errorDocument.Errors[0].StatusCode); - Assert.Equal("The request method is not allowed.", errorDocument.Errors[0].Title); - Assert.Equal("Resource does not support POST requests.", errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Allows_PATCH_Requests() - { - // Arrange - const string route = "nohttppost"; - const string method = "PATCH"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task Allows_DELETE_Requests() - { - // Arrange - const string route = "nohttppost"; - const string method = "DELETE"; - - // Act - var response = await MakeRequestAsync(route, method); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - private async Task MakeRequestAsync(string route, string method) - { - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod(method); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - var response = await client.SendAsync(request); - return response; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs new file mode 100644 index 0000000000..3aa4747c9c --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs @@ -0,0 +1,8 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class Bed : Identifiable + { + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs new file mode 100644 index 0000000000..9651950dd3 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + [NoHttpDelete] + public sealed class BlockingHttpDeleteController : JsonApiController + { + public BlockingHttpDeleteController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs new file mode 100644 index 0000000000..9edfdb07c5 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + [NoHttpPatch] + public sealed class BlockingHttpPatchController : JsonApiController + { + public BlockingHttpPatchController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs new file mode 100644 index 0000000000..2dbaaadf80 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + [NoHttpPost] + public sealed class BlockingHttpPostController : JsonApiController + { + public BlockingHttpPostController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs new file mode 100644 index 0000000000..5704bb5600 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + [HttpReadOnly] + public sealed class BlockingWritesController : JsonApiController + { + public BlockingWritesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs new file mode 100644 index 0000000000..e80794f0e7 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs @@ -0,0 +1,9 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class Chair : Identifiable + { + + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs new file mode 100644 index 0000000000..2cefccbf8d --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs @@ -0,0 +1,126 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class HttpReadOnlyTests + : IClassFixture, RestrictionDbContext>> + { + private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly RestrictionFakers _fakers = new RestrictionFakers(); + + public HttpReadOnlyTests(IntegrationTestContext, RestrictionDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resources() + { + // Arrange + var route = "/beds"; + + // Act + var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + } + + [Fact] + public async Task Cannot_create_resource() + { + // Arrange + var requestBody = new + { + data = new + { + type = "beds", + attributes = new + { + } + } + }; + + var route = "/beds"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support POST requests."); + } + + [Fact] + public async Task Cannot_update_resource() + { + // Arrange + var existingBed = _fakers.Bed.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Beds.Add(existingBed); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "beds", + id = existingBed.StringId, + attributes = new + { + } + } + }; + + var route = "/beds/" + existingBed.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support PATCH requests."); + } + + [Fact] + public async Task Cannot_delete_resource() + { + // Arrange + var existingBed = _fakers.Bed.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Beds.Add(existingBed); + await dbContext.SaveChangesAsync(); + }); + + var route = "/beds/" + existingBed.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support DELETE requests."); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs new file mode 100644 index 0000000000..d8160f1b60 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs @@ -0,0 +1,116 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class NoHttpDeleteTests + : IClassFixture, RestrictionDbContext>> + { + private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly RestrictionFakers _fakers = new RestrictionFakers(); + + public NoHttpDeleteTests(IntegrationTestContext, RestrictionDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resources() + { + // Arrange + var route = "/sofas"; + + // Act + var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + } + + [Fact] + public async Task Can_create_resource() + { + // Arrange + var requestBody = new + { + data = new + { + type = "sofas", + attributes = new + { + } + } + }; + + var route = "/sofas"; + + // Act + var (httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + } + + [Fact] + public async Task Can_update_resource() + { + // Arrange + var existingSofa = _fakers.Sofa.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Sofas.Add(existingSofa); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "sofas", + id = existingSofa.StringId, + attributes = new + { + } + } + }; + + var route = "/sofas/" + existingSofa.StringId; + + // Act + var (httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + } + + [Fact] + public async Task Cannot_delete_resource() + { + // Arrange + var existingSofa = _fakers.Sofa.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Sofas.Add(existingSofa); + await dbContext.SaveChangesAsync(); + }); + + var route = "/sofas/" + existingSofa.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support DELETE requests."); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs new file mode 100644 index 0000000000..62484888d0 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs @@ -0,0 +1,116 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class NoHttpPatchTests + : IClassFixture, RestrictionDbContext>> + { + private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly RestrictionFakers _fakers = new RestrictionFakers(); + + public NoHttpPatchTests(IntegrationTestContext, RestrictionDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resources() + { + // Arrange + var route = "/chairs"; + + // Act + var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + } + + [Fact] + public async Task Can_create_resource() + { + // Arrange + var requestBody = new + { + data = new + { + type = "chairs", + attributes = new + { + } + } + }; + + var route = "/chairs"; + + // Act + var (httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + } + + [Fact] + public async Task Cannot_update_resource() + { + // Arrange + var existingChair = _fakers.Chair.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Chairs.Add(existingChair); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "chairs", + id = existingChair.StringId, + attributes = new + { + } + } + }; + + var route = "/chairs/" + existingChair.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support PATCH requests."); + } + + [Fact] + public async Task Can_delete_resource() + { + // Arrange + var existingChair = _fakers.Chair.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Chairs.Add(existingChair); + await dbContext.SaveChangesAsync(); + }); + + var route = "/chairs/" + existingChair.StringId; + + // Act + var (httpResponse, _) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs new file mode 100644 index 0000000000..d8f5fda941 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs @@ -0,0 +1,116 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class NoHttpPostTests + : IClassFixture, RestrictionDbContext>> + { + private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly RestrictionFakers _fakers = new RestrictionFakers(); + + public NoHttpPostTests(IntegrationTestContext, RestrictionDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resources() + { + // Arrange + var route = "/tables"; + + // Act + var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + } + + [Fact] + public async Task Cannot_create_resource() + { + // Arrange + var requestBody = new + { + data = new + { + type = "tables", + attributes = new + { + } + } + }; + + var route = "/tables"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.MethodNotAllowed); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); + responseDocument.Errors[0].Title.Should().Be("The request method is not allowed."); + responseDocument.Errors[0].Detail.Should().Be("Resource does not support POST requests."); + } + + [Fact] + public async Task Can_update_resource() + { + // Arrange + var existingTable = _fakers.Table.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Tables.Add(existingTable); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "tables", + id = existingTable.StringId, + attributes = new + { + } + } + }; + + var route = "/tables/" + existingTable.StringId; + + // Act + var (httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + } + + [Fact] + public async Task Can_delete_resource() + { + // Arrange + var existingTable = _fakers.Table.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Tables.Add(existingTable); + await dbContext.SaveChangesAsync(); + }); + + var route = "/tables/" + existingTable.StringId; + + // Act + var (httpResponse, _) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs new file mode 100644 index 0000000000..190df5f4d1 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class RestrictionDbContext : DbContext + { + public DbSet
Tables { get; set; } + public DbSet Chairs { get; set; } + public DbSet Sofas { get; set; } + public DbSet Beds { get; set; } + + public RestrictionDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs new file mode 100644 index 0000000000..70d2dab8a3 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs @@ -0,0 +1,29 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + internal sealed class RestrictionFakers : FakerContainer + { + private readonly Lazy> _lazyTableFaker = new Lazy>(() => + new Faker
() + .UseSeed(GetFakerSeed())); + + private readonly Lazy> _lazyChairFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed())); + + private readonly Lazy> _lazySofaFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed())); + + private readonly Lazy> _lazyBedFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed())); + + public Faker
Table => _lazyTableFaker.Value; + public Faker Chair => _lazyChairFaker.Value; + public Faker Sofa => _lazySofaFaker.Value; + public Faker Bed => _lazyBedFaker.Value; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs new file mode 100644 index 0000000000..4c5c84ce20 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs @@ -0,0 +1,8 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class Sofa : Identifiable + { + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs new file mode 100644 index 0000000000..af649cc581 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs @@ -0,0 +1,9 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class Table : Identifiable + { + + } +} From 90274c604e0208f6678c040c888d6021e88f7d40 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 26 Jan 2021 13:51:44 +0100 Subject: [PATCH 02/47] Refactored tests for casing convention --- .../Data/AppDbContext.cs | 1 - .../Models/KebabCasedModel.cs | 11 - .../Acceptance/KebabCaseFormatterTests.cs | 201 ------------------ .../Acceptance/TestFixture.cs | 56 +---- .../NamingConventions/DivingBoard.cs | 14 ++ .../DivingBoardsController.cs | 16 ++ .../KebabCasingConventionStartup.cs | 29 +++ .../NamingConventions/KebabCasingTests.cs | 196 +++++++++++++++++ .../NamingConventions/SwimmingDbContext.cs | 16 ++ .../NamingConventions/SwimmingFakers.cs | 27 +++ .../NamingConventions/SwimmingPool.cs | 18 ++ .../SwimmingPoolsController.cs | 16 ++ .../NamingConventions/WaterSlide.cs | 11 + .../ObjectAssertionsExtensions.cs | 12 ++ 14 files changed, 356 insertions(+), 268 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/KebabCasedModel.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/KebabCaseFormatterTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoard.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoardsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPool.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPoolsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/WaterSlide.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 7221b18492..198169edcb 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -13,7 +13,6 @@ public sealed class AppDbContext : DbContext public DbSet Passports { get; set; } public DbSet People { get; set; } public DbSet TodoItemCollections { get; set; } - public DbSet KebabCasedModels { get; set; } public DbSet
Articles { get; set; } public DbSet AuthorDifferentDbContextName { get; set; } public DbSet Users { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/KebabCasedModel.cs b/src/Examples/JsonApiDotNetCoreExample/Models/KebabCasedModel.cs deleted file mode 100644 index d9e4c4bbf9..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Models/KebabCasedModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace JsonApiDotNetCoreExample.Models -{ - public class KebabCasedModel : Identifiable - { - [Attr] - public string CompoundAttr { get; set; } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/KebabCaseFormatterTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/KebabCaseFormatterTests.cs deleted file mode 100644 index 1d995bb721..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/KebabCaseFormatterTests.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Net; -using System.Threading.Tasks; -using Bogus; -using FluentAssertions; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Serialization.Client.Internal; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - public sealed class KebabCaseFormatterTests : IClassFixture> - { - private readonly IntegrationTestContext _testContext; - private readonly Faker _faker; - - public KebabCaseFormatterTests(IntegrationTestContext testContext) - { - _testContext = testContext; - - _faker = new Faker() - .RuleFor(m => m.CompoundAttr, f => f.Lorem.Sentence()); - } - - [Fact] - public async Task KebabCaseFormatter_GetAll_IsReturned() - { - // Arrange - var model = _faker.Generate(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - await dbContext.ClearTableAsync(); - dbContext.KebabCasedModels.Add(model); - - await dbContext.SaveChangesAsync(); - }); - - var route = "api/v1/kebab-cased-models"; - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - - responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Id.Should().Be(model.StringId); - responseDocument.ManyData[0].Attributes["compound-attr"].Should().Be(model.CompoundAttr); - } - - [Fact] - public async Task KebabCaseFormatter_GetSingle_IsReturned() - { - // Arrange - var model = _faker.Generate(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.KebabCasedModels.Add(model); - - await dbContext.SaveChangesAsync(); - }); - - var route = "api/v1/kebab-cased-models/" + model.StringId; - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - - responseDocument.SingleData.Should().NotBeNull(); - responseDocument.SingleData.Id.Should().Be(model.StringId); - responseDocument.SingleData.Attributes["compound-attr"].Should().Be(model.CompoundAttr); - } - - [Fact] - public async Task KebabCaseFormatter_Create_IsCreated() - { - // Arrange - var model = _faker.Generate(); - var serializer = GetSerializer(kcm => new { kcm.CompoundAttr }); - - var route = "api/v1/kebab-cased-models"; - - var requestBody = serializer.Serialize(model); - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); - - responseDocument.SingleData.Should().NotBeNull(); - responseDocument.SingleData.Attributes["compound-attr"].Should().Be(model.CompoundAttr); - } - - [Fact] - public async Task KebabCaseFormatter_Update_IsUpdated() - { - // Arrange - var model = _faker.Generate(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.KebabCasedModels.Add(model); - - await dbContext.SaveChangesAsync(); - }); - - model.CompoundAttr = _faker.Generate().CompoundAttr; - var serializer = GetSerializer(kcm => new { kcm.CompoundAttr }); - - var route = "api/v1/kebab-cased-models/" + model.StringId; - - var requestBody = serializer.Serialize(model); - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); - - responseDocument.Should().BeEmpty(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - var stored = await dbContext.KebabCasedModels.SingleAsync(x => x.Id == model.Id); - Assert.Equal(model.CompoundAttr, stored.CompoundAttr); - }); - } - - [Fact] - public async Task KebabCaseFormatter_ErrorWithStackTrace_CasingConventionIsApplied() - { - // Arrange - var route = "api/v1/kebab-cased-models/1"; - - const string requestBody = "{ \"data\": {"; - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); - - httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); - - var meta = responseDocument["errors"][0]["meta"]; - Assert.NotNull(meta["stack-trace"]); - } - - private IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable - { - using var scope = _testContext.Factory.Services.CreateScope(); - var serializer = scope.ServiceProvider.GetRequiredService(); - var graph = scope.ServiceProvider.GetRequiredService(); - - serializer.AttributesToSerialize = attributes != null ? graph.GetAttributes(attributes) : null; - serializer.RelationshipsToSerialize = relationships != null ? graph.GetRelationships(relationships) : null; - - return serializer; - } - } - - public sealed class KebabCaseStartup : TestStartup - { - public KebabCaseStartup(IConfiguration configuration) : base(configuration) - { - } - - protected override void ConfigureJsonApiOptions(JsonApiOptions options) - { - base.ConfigureJsonApiOptions(options); - - ((DefaultContractResolver)options.SerializerSettings.ContractResolver).NamingStrategy = new KebabCaseNamingStrategy(); - } - } - - public sealed class KebabCasedModelsController : JsonApiController - { - public KebabCasedModelsController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, loggerFactory, resourceService) - { } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs index 8054663ef3..9136abc00c 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs @@ -1,21 +1,12 @@ using System; -using System.Linq.Expressions; using System.Net; using System.Net.Http; -using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Repositories; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Serialization.Client.Internal; using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.Helpers.Models; using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace JsonApiDotNetCoreExampleTests.Acceptance @@ -35,55 +26,10 @@ public TestFixture() } public HttpClient Client { get; set; } - public AppDbContext Context { get; private set; } - - public static IRequestSerializer GetSerializer(IServiceProvider serviceProvider, Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable - { - var serializer = (IRequestSerializer)serviceProvider.GetRequiredService(typeof(IRequestSerializer)); - var graph = (IResourceGraph)serviceProvider.GetRequiredService(typeof(IResourceGraph)); - serializer.AttributesToSerialize = attributes != null ? graph.GetAttributes(attributes) : null; - serializer.RelationshipsToSerialize = relationships != null ? graph.GetRelationships(relationships) : null; - return serializer; - } - - public IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable - { - var serializer = GetRequiredService(); - var graph = GetRequiredService(); - serializer.AttributesToSerialize = attributes != null ? graph.GetAttributes(attributes) : null; - serializer.RelationshipsToSerialize = relationships != null ? graph.GetRelationships(relationships) : null; - return serializer; - } - - public IResponseDeserializer GetDeserializer() - { - var options = GetRequiredService(); - - var resourceGraph = new ResourceGraphBuilder(options, NullLoggerFactory.Instance) - .Add() - .Add
() - .Add() - .Add() - .Add() - .Add() - .Add() - .Add() - .Add() - .Add("todoItems") - .Add().Build(); - return new ResponseDeserializer(resourceGraph, new ResourceFactory(ServiceProvider)); - } + public AppDbContext Context { get; } public T GetRequiredService() => (T)ServiceProvider.GetRequiredService(typeof(T)); - public void ReloadDbContext() - { - ISystemClock systemClock = ServiceProvider.GetRequiredService(); - DbContextOptions options = GetRequiredService>(); - - Context = new AppDbContext(options, systemClock); - } - public void AssertEqualStatusCode(HttpStatusCode expected, HttpResponseMessage response) { var responseBody = response.Content.ReadAsStringAsync().Result; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoard.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoard.cs new file mode 100644 index 0000000000..c46c4f410f --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoard.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class DivingBoard : Identifiable + { + [Attr] + [Required] + [Range(1, 20)] + public decimal HeightInMeters { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoardsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoardsController.cs new file mode 100644 index 0000000000..826249bb22 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/DivingBoardsController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class DivingBoardsController : JsonApiController + { + public DivingBoardsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs new file mode 100644 index 0000000000..44fd3af114 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs @@ -0,0 +1,29 @@ +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json.Serialization; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class KebabCasingConventionStartup : TestableStartup + where TDbContext : DbContext + { + public KebabCasingConventionStartup(IConfiguration configuration) : base(configuration) + { + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.IncludeExceptionStackTraceInErrors = true; + options.Namespace = "public-api"; + options.UseRelativeLinks = true; + options.IncludeTotalResourceCount = true; + options.ValidateModelState = true; + + var resolver = (DefaultContractResolver) options.SerializerSettings.ContractResolver; + resolver!.NamingStrategy = new KebabCaseNamingStrategy(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs new file mode 100644 index 0000000000..6b5929328b --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -0,0 +1,196 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.EntityFrameworkCore; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class KebabCasingTests + : IClassFixture, SwimmingDbContext>> + { + private readonly IntegrationTestContext, SwimmingDbContext> _testContext; + private readonly SwimmingFakers _fakers = new SwimmingFakers(); + + public KebabCasingTests(IntegrationTestContext, SwimmingDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resources_with_include() + { + // Arrange + var pools = _fakers.SwimmingPool.Generate(2); + pools[1].DivingBoards = _fakers.DivingBoard.Generate(1); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.SwimmingPools.AddRange(pools); + await dbContext.SaveChangesAsync(); + }); + + var route = "/public-api/swimming-pools?include=diving-boards"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.ManyData.Should().HaveCount(2); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Type == "swimming-pools"); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Attributes.ContainsKey("is-indoor")); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Relationships.ContainsKey("water-slides")); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Relationships.ContainsKey("diving-boards")); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Type.Should().Be("diving-boards"); + responseDocument.Included[0].Id.Should().Be(pools[1].DivingBoards[0].StringId); + responseDocument.Included[0].Attributes["height-in-meters"].Should().BeApproximately(pools[1].DivingBoards[0].HeightInMeters); + responseDocument.Included[0].Relationships.Should().BeNull(); + responseDocument.Included[0].Links.Self.Should().Be($"/public-api/diving-boards/{pools[1].DivingBoards[0].StringId}"); + + responseDocument.Meta["total-resources"].Should().Be(2); + } + + [Fact] + public async Task Can_filter_secondary_resources_with_sparse_fieldset() + { + // Arrange + var pool = _fakers.SwimmingPool.Generate(); + pool.WaterSlides = _fakers.WaterSlide.Generate(2); + pool.WaterSlides[0].LengthInMeters = 1; + pool.WaterSlides[1].LengthInMeters = 5; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.SwimmingPools.Add(pool); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/public-api/swimming-pools/{pool.StringId}/water-slides?filter=greaterThan(length-in-meters,'1')&fields[water-slides]=length-in-meters"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Type.Should().Be("water-slides"); + responseDocument.ManyData[0].Id.Should().Be(pool.WaterSlides[1].StringId); + responseDocument.ManyData[0].Attributes.Should().HaveCount(1); + } + + [Fact] + public async Task Can_create_resource() + { + // Arrange + var newPool = _fakers.SwimmingPool.Generate(); + + var requestBody = new + { + data = new + { + type = "swimming-pools", + attributes = new Dictionary + { + ["is-indoor"] = newPool.IsIndoor + } + } + }; + + var route = "/public-api/swimming-pools"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Type.Should().Be("swimming-pools"); + responseDocument.SingleData.Attributes["is-indoor"].Should().Be(newPool.IsIndoor); + + var newPoolId = int.Parse(responseDocument.SingleData.Id); + + responseDocument.SingleData.Relationships.Should().NotBeEmpty(); + responseDocument.SingleData.Relationships["water-slides"].Links.Self.Should().Be($"/public-api/swimming-pools/{newPoolId}/relationships/water-slides"); + responseDocument.SingleData.Relationships["water-slides"].Links.Related.Should().Be($"/public-api/swimming-pools/{newPoolId}/water-slides"); + responseDocument.SingleData.Relationships["diving-boards"].Links.Self.Should().Be($"/public-api/swimming-pools/{newPoolId}/relationships/diving-boards"); + responseDocument.SingleData.Relationships["diving-boards"].Links.Related.Should().Be($"/public-api/swimming-pools/{newPoolId}/diving-boards"); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var poolInDatabase = await dbContext.SwimmingPools + .FirstAsync(pool => pool.Id == newPoolId); + + poolInDatabase.IsIndoor.Should().Be(newPool.IsIndoor); + }); + } + + [Fact] + public async Task Cannot_create_resource_for_invalid_request_body() + { + // Arrange + var requestBody = "{ \"data\": {"; + + var route = "/public-api/swimming-pools"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + responseDocument.Errors[0].Title.Should().Be("Failed to deserialize request body."); + responseDocument.Errors[0].Meta.Data.Should().ContainKey("stack-trace"); + } + + [Fact] + public async Task Cannot_update_resource_for_invalid_attribute() + { + // Arrange + var existingBoard = _fakers.DivingBoard.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.DivingBoards.Add(existingBoard); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "diving-boards", + id = existingBoard.StringId, + attributes = new Dictionary + { + ["height-in-meters"] = -1 + } + } + }; + + var route = "/public-api/diving-boards/" + existingBoard.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + responseDocument.Errors[0].Title.Should().Be("Input validation failed."); + responseDocument.Errors[0].Detail.Should().Be("The field HeightInMeters must be between 1 and 20."); + responseDocument.Errors[0].Source.Pointer.Should().Be("/data/attributes/height-in-meters"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingDbContext.cs new file mode 100644 index 0000000000..9d8de4aa26 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingDbContext.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class SwimmingDbContext : DbContext + { + public DbSet SwimmingPools { get; set; } + public DbSet WaterSlides { get; set; } + public DbSet DivingBoards { get; set; } + + public SwimmingDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs new file mode 100644 index 0000000000..71615bc567 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs @@ -0,0 +1,27 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + internal sealed class SwimmingFakers : FakerContainer + { + private readonly Lazy> _lazySwimmingPoolFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(swimmingPool => swimmingPool.IsIndoor, f => f.Random.Bool())); + + private readonly Lazy> _lazyWaterSlideFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(waterSlide => waterSlide.LengthInMeters, f => f.Random.Decimal(3, 100))); + + private readonly Lazy> _lazyDivingBoardFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(divingBoard => divingBoard.HeightInMeters, f => f.Random.Decimal(1, 15))); + + public Faker SwimmingPool => _lazySwimmingPoolFaker.Value; + public Faker WaterSlide => _lazyWaterSlideFaker.Value; + public Faker DivingBoard => _lazyDivingBoardFaker.Value; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPool.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPool.cs new file mode 100644 index 0000000000..6c1274816e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPool.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class SwimmingPool : Identifiable + { + [Attr] + public bool IsIndoor { get; set; } + + [HasMany] + public IList WaterSlides { get; set; } + + [HasMany] + public IList DivingBoards { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPoolsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPoolsController.cs new file mode 100644 index 0000000000..6af99a7ea4 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingPoolsController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class SwimmingPoolsController : JsonApiController + { + public SwimmingPoolsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/WaterSlide.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/WaterSlide.cs new file mode 100644 index 0000000000..b7dea1d5ba --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/WaterSlide.cs @@ -0,0 +1,11 @@ +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions +{ + public sealed class WaterSlide : Identifiable + { + [Attr] + public decimal LengthInMeters { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs index c4ab87ea6d..159a7691b4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs @@ -24,5 +24,17 @@ public static void BeCloseTo(this ObjectAssertions source, DateTimeOffset? expec value.Should().BeCloseTo(expected.Value, because: because, becauseArgs: becauseArgs); } } + + /// + /// Used to assert on a column, whose value is returned as in json:api response body. + /// + public static void BeApproximately(this ObjectAssertions source, decimal? expected, decimal precision = 0.00000000001M, string because = "", + params object[] becauseArgs) + { + // We lose a little bit of precision on roundtrip through PostgreSQL database. + + var value = (decimal?) (double) source.Subject; + value.Should().BeApproximately(expected, precision, because, becauseArgs); + } } } From 4bdb3e89bee2279b7ca72ec5ab708f07a12eed2c Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 26 Jan 2021 15:15:48 +0100 Subject: [PATCH 03/47] Refactored tests for custom exception handler --- .../Extensibility/CustomErrorHandlingTests.cs | 80 ----------------- .../AlternateExceptionHandler.cs | 37 ++++++++ .../ExceptionHandling/ConsumerArticle.cs | 11 +++ ...umerArticleIsNoLongerAvailableException.cs | 23 +++++ .../ConsumerArticleService.cs | 39 +++++++++ .../ConsumerArticlesController.cs | 16 ++++ .../ExceptionHandling/ErrorDbContext.cs | 14 +++ .../ExceptionHandlerTests.cs | 85 +++++++++++++++++++ 8 files changed, 225 insertions(+), 80 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomErrorHandlingTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/AlternateExceptionHandler.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleIsNoLongerAvailableException.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomErrorHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomErrorHandlingTests.cs deleted file mode 100644 index ff49489904..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomErrorHandlingTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Errors; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Extensibility -{ - public sealed class CustomErrorHandlingTests - { - [Fact] - public void When_using_custom_exception_handler_it_must_create_error_document_and_log() - { - // Arrange - var loggerFactory = new FakeLoggerFactory(); - var options = new JsonApiOptions {IncludeExceptionStackTraceInErrors = true}; - var handler = new CustomExceptionHandler(loggerFactory, options); - - // Act - var errorDocument = handler.HandleException(new NoPermissionException("YouTube")); - - // Assert - Assert.Single(errorDocument.Errors); - Assert.Equal("For support, email to: support@company.com?subject=YouTube", - errorDocument.Errors[0].Meta.Data["support"]); - Assert.NotEmpty((string[]) errorDocument.Errors[0].Meta.Data["StackTrace"]); - - Assert.Single(loggerFactory.Logger.Messages); - Assert.Equal(LogLevel.Warning, loggerFactory.Logger.Messages.Single().LogLevel); - Assert.Contains("Access is denied.", loggerFactory.Logger.Messages.Single().Text); - } - - public class CustomExceptionHandler : ExceptionHandler - { - public CustomExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options) - : base(loggerFactory, options) - { - } - - protected override LogLevel GetLogLevel(Exception exception) - { - if (exception is NoPermissionException) - { - return LogLevel.Warning; - } - - return base.GetLogLevel(exception); - } - - protected override ErrorDocument CreateErrorDocument(Exception exception) - { - if (exception is NoPermissionException noPermissionException) - { - noPermissionException.Errors[0].Meta.Data.Add("support", - "For support, email to: support@company.com?subject=" + noPermissionException.CustomerCode); - } - - return base.CreateErrorDocument(exception); - } - } - - public class NoPermissionException : JsonApiException - { - public string CustomerCode { get; } - - public NoPermissionException(string customerCode) : base(new Error(HttpStatusCode.Forbidden) - { - Title = "Access is denied.", - Detail = $"Customer '{customerCode}' does not have permission to access this location." - }) - { - CustomerCode = customerCode; - } - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/AlternateExceptionHandler.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/AlternateExceptionHandler.cs new file mode 100644 index 0000000000..385d2d6493 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/AlternateExceptionHandler.cs @@ -0,0 +1,37 @@ +using System; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class AlternateExceptionHandler : ExceptionHandler + { + public AlternateExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options) + : base(loggerFactory, options) + { + } + + protected override LogLevel GetLogLevel(Exception exception) + { + if (exception is ConsumerArticleIsNoLongerAvailableException) + { + return LogLevel.Warning; + } + + return base.GetLogLevel(exception); + } + + protected override ErrorDocument CreateErrorDocument(Exception exception) + { + if (exception is ConsumerArticleIsNoLongerAvailableException articleException) + { + articleException.Errors[0].Meta.Data.Add("support", + $"Please contact us for info about similar articles at {articleException.SupportEmailAddress}."); + } + + return base.CreateErrorDocument(exception); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs new file mode 100644 index 0000000000..320355edfa --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs @@ -0,0 +1,11 @@ +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ConsumerArticle : Identifiable + { + [Attr] + public string Code { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleIsNoLongerAvailableException.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleIsNoLongerAvailableException.cs new file mode 100644 index 0000000000..f4420dea90 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleIsNoLongerAvailableException.cs @@ -0,0 +1,23 @@ +using System.Net; +using JsonApiDotNetCore.Errors; +using JsonApiDotNetCore.Serialization.Objects; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ConsumerArticleIsNoLongerAvailableException : JsonApiException + { + public string ArticleCode { get; } + public string SupportEmailAddress { get; } + + public ConsumerArticleIsNoLongerAvailableException(string articleCode, string supportEmailAddress) + : base(new Error(HttpStatusCode.Gone) + { + Title = "The requested article is no longer available.", + Detail = $"Article with code '{articleCode}' is no longer available." + }) + { + ArticleCode = articleCode; + SupportEmailAddress = supportEmailAddress; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs new file mode 100644 index 0000000000..17daa6233a --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs @@ -0,0 +1,39 @@ +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Hooks; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Queries; +using JsonApiDotNetCore.Repositories; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ConsumerArticleService : JsonApiResourceService + { + private const string _supportEmailAddress = "company@email.com"; + + public ConsumerArticleService(IResourceRepositoryAccessor repositoryAccessor, IQueryLayerComposer queryLayerComposer, + IPaginationContext paginationContext, IJsonApiOptions options, ILoggerFactory loggerFactory, + IJsonApiRequest request, IResourceChangeTracker resourceChangeTracker, + IResourceHookExecutorFacade hookExecutor) + : base(repositoryAccessor, queryLayerComposer, paginationContext, options, loggerFactory, request, + resourceChangeTracker, hookExecutor) + { + } + + public override async Task GetAsync(int id, CancellationToken cancellationToken) + { + var consumerArticle = await base.GetAsync(id, cancellationToken); + + if (consumerArticle.Code.StartsWith("X")) + { + throw new ConsumerArticleIsNoLongerAvailableException(consumerArticle.Code, _supportEmailAddress); + } + + return consumerArticle; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs new file mode 100644 index 0000000000..dcc0ad7e8e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ConsumerArticlesController : JsonApiController + { + public ConsumerArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs new file mode 100644 index 0000000000..2e9f0cc3e8 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ErrorDbContext : DbContext + { + public DbSet ConsumerArticles { get; set; } + + public ErrorDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs new file mode 100644 index 0000000000..fca27f6857 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -0,0 +1,85 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ExceptionHandlerTests + : IClassFixture, ErrorDbContext>> + { + private readonly IntegrationTestContext, ErrorDbContext> _testContext; + + public ExceptionHandlerTests(IntegrationTestContext, ErrorDbContext> testContext) + { + _testContext = testContext; + + FakeLoggerFactory loggerFactory = null; + + testContext.ConfigureLogging(options => + { + loggerFactory = new FakeLoggerFactory(); + + options.ClearProviders(); + options.AddProvider(loggerFactory); + options.SetMinimumLevel(LogLevel.Warning); + }); + + testContext.ConfigureServicesBeforeStartup(services => + { + if (loggerFactory != null) + { + services.AddSingleton(_ => loggerFactory); + } + }); + + testContext.ConfigureServicesAfterStartup(services => + { + services.AddResourceService(); + services.AddScoped(); + }); + } + + [Fact] + public async Task Logs_and_produces_error_response_for_custom_exception() + { + // Arrange + var loggerFactory = _testContext.Factory.Services.GetRequiredService(); + loggerFactory.Logger.Clear(); + + var consumerArticle = new ConsumerArticle + { + Code = "X123" + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ConsumerArticles.Add(consumerArticle); + await dbContext.SaveChangesAsync(); + }); + + var route = "/consumerArticles/" + consumerArticle.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Gone); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Gone); + responseDocument.Errors[0].Title.Should().Be("The requested article is no longer available."); + responseDocument.Errors[0].Detail.Should().Be("Article with code 'X123' is no longer available."); + responseDocument.Errors[0].Meta.Data["support"].Should().Be("Please contact us for info about similar articles at company@email.com."); + + loggerFactory.Logger.Messages.Should().HaveCount(1); + loggerFactory.Logger.Messages.Single().Text.Should().Contain("Article with code 'X123' is no longer available."); + } + } +} From cbe01c229c02e4b96dd45f27f6e85a60b3c8cd8c Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 26 Jan 2021 17:41:54 +0100 Subject: [PATCH 04/47] Refactored tests for nulls/default query string parameters; removed some duplication and fixed an equality bug. --- .../Building/ResourceObjectBuilder.cs | 2 +- .../Extensibility/IgnoreDefaultValuesTests.cs | 172 ----------------- .../Extensibility/IgnoreNullValuesTests.cs | 175 ------------------ .../QueryStrings/Appointment.cs | 18 ++ .../IntegrationTests/QueryStrings/Calendar.cs | 18 ++ .../QueryStrings/CalendarsController.cs | 16 ++ .../QueryStrings/QueryStringDbContext.cs | 14 ++ .../QueryStrings/QueryStringFakers.cs | 24 +++ .../QueryStrings/QueryStringTests.cs | 16 +- .../SerializerDefaultValueHandlingTests.cs | 97 ++++++++++ .../SerializerNullValueHandlingTests.cs | 97 ++++++++++ 11 files changed, 293 insertions(+), 356 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/IgnoreDefaultValuesTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/IgnoreNullValuesTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Appointment.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Calendar.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/CalendarsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs diff --git a/src/JsonApiDotNetCore/Serialization/Building/ResourceObjectBuilder.cs b/src/JsonApiDotNetCore/Serialization/Building/ResourceObjectBuilder.cs index 63df921f96..28808bb6ba 100644 --- a/src/JsonApiDotNetCore/Serialization/Building/ResourceObjectBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Building/ResourceObjectBuilder.cs @@ -146,7 +146,7 @@ private void ProcessAttributes(IIdentifiable resource, IEnumerable fixture) - { - _dbContext = fixture.GetRequiredService(); - _todoItem = new TodoItem - { - CreatedDate = default, - Owner = new Person { Age = default } - }; - _dbContext.TodoItems.Add(_todoItem); - } - - public async Task InitializeAsync() - { - await _dbContext.SaveChangesAsync(); - } - - public Task DisposeAsync() - { - return Task.CompletedTask; - } - - [Theory] - [InlineData(null, null, null, DefaultValueHandling.Include)] - [InlineData(null, null, "false", DefaultValueHandling.Include)] - [InlineData(null, null, "true", DefaultValueHandling.Include)] - [InlineData(null, null, "unknown", null)] - [InlineData(null, null, "", null)] - [InlineData(null, false, null, DefaultValueHandling.Include)] - [InlineData(null, false, "false", DefaultValueHandling.Include)] - [InlineData(null, false, "true", DefaultValueHandling.Include)] - [InlineData(null, false, "unknown", null)] - [InlineData(null, false, "", null)] - [InlineData(null, true, null, DefaultValueHandling.Include)] - [InlineData(null, true, "false", DefaultValueHandling.Ignore)] - [InlineData(null, true, "true", DefaultValueHandling.Include)] - [InlineData(null, true, "unknown", null)] - [InlineData(null, true, "", null)] - [InlineData(DefaultValueHandling.Ignore, null, null, DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, null, "false", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, null, "true", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, null, "unknown", null)] - [InlineData(DefaultValueHandling.Ignore, null, "", null)] - [InlineData(DefaultValueHandling.Ignore, false, null, DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, false, "false", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, false, "true", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, false, "unknown", null)] - [InlineData(DefaultValueHandling.Ignore, false, "", null)] - [InlineData(DefaultValueHandling.Ignore, true, null, DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, true, "false", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Ignore, true, "true", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Ignore, true, "unknown", null)] - [InlineData(DefaultValueHandling.Ignore, true, "", null)] - [InlineData(DefaultValueHandling.Include, null, null, DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, null, "false", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, null, "true", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, null, "unknown", null)] - [InlineData(DefaultValueHandling.Include, null, "", null)] - [InlineData(DefaultValueHandling.Include, false, null, DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, false, "false", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, false, "true", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, false, "unknown", null)] - [InlineData(DefaultValueHandling.Include, false, "", null)] - [InlineData(DefaultValueHandling.Include, true, null, DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, true, "false", DefaultValueHandling.Ignore)] - [InlineData(DefaultValueHandling.Include, true, "true", DefaultValueHandling.Include)] - [InlineData(DefaultValueHandling.Include, true, "unknown", null)] - [InlineData(DefaultValueHandling.Include, true, "", null)] - public async Task CheckBehaviorCombination(DefaultValueHandling? defaultValue, bool? allowQueryStringOverride, string queryStringValue, DefaultValueHandling? expected) - { - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var services = server.Host.Services; - var client = server.CreateClient(); - - var options = (JsonApiOptions)services.GetRequiredService(typeof(IJsonApiOptions)); - - if (defaultValue != null) - { - options.SerializerSettings.DefaultValueHandling = defaultValue.Value; - } - if (allowQueryStringOverride != null) - { - options.AllowQueryStringOverrideForSerializerDefaultValueHandling = allowQueryStringOverride.Value; - } - - var queryString = queryStringValue != null - ? $"&defaults={queryStringValue}" - : ""; - var route = $"/api/v1/todoItems/{_todoItem.Id}?include=owner{queryString}"; - var request = new HttpRequestMessage(HttpMethod.Get, route); - - // Act - var response = await client.SendAsync(request); - var body = await response.Content.ReadAsStringAsync(); - - var isQueryStringValueEmpty = queryStringValue == string.Empty; - var isDisallowedOverride = !options.AllowQueryStringOverrideForSerializerDefaultValueHandling && queryStringValue != null; - var isQueryStringInvalid = queryStringValue != null && !bool.TryParse(queryStringValue, out _); - - if (isQueryStringValueEmpty) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Missing query string parameter value.", errorDocument.Errors[0].Title); - Assert.Equal("Missing value for 'defaults' query string parameter.", errorDocument.Errors[0].Detail); - Assert.Equal("defaults", errorDocument.Errors[0].Source.Parameter); - } - else if (isDisallowedOverride) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Usage of one or more query string parameters is not allowed at the requested endpoint.", errorDocument.Errors[0].Title); - Assert.Equal("The parameter 'defaults' cannot be used at this endpoint.", errorDocument.Errors[0].Detail); - Assert.Equal("defaults", errorDocument.Errors[0].Source.Parameter); - } - else if (isQueryStringInvalid) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("The specified defaults is invalid.", errorDocument.Errors[0].Title); - Assert.Equal("The value 'unknown' must be 'true' or 'false'.", errorDocument.Errors[0].Detail); - Assert.Equal("defaults", errorDocument.Errors[0].Source.Parameter); - } - else - { - if (expected == null) - { - throw new Exception("Invalid test combination. Should never get here."); - } - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var deserializeBody = JsonConvert.DeserializeObject(body); - Assert.Equal(expected == DefaultValueHandling.Include, deserializeBody.SingleData.Attributes.ContainsKey("createdDate")); - Assert.Equal(expected == DefaultValueHandling.Include, deserializeBody.Included[0].Attributes.ContainsKey("the-Age")); - } - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/IgnoreNullValuesTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/IgnoreNullValuesTests.cs deleted file mode 100644 index e4ac1e61cc..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/IgnoreNullValuesTests.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Extensibility -{ - [Collection("WebHostCollection")] - public sealed class IgnoreNullValuesTests : IAsyncLifetime - { - private readonly AppDbContext _dbContext; - private readonly TodoItem _todoItem; - - public IgnoreNullValuesTests(TestFixture fixture) - { - _dbContext = fixture.GetRequiredService(); - _todoItem = new TodoItem - { - Description = null, - Ordinal = 1, - CreatedDate = new DateTime(2002, 2,2), - AchievedDate = new DateTime(2002, 2,4), - Owner = new Person { FirstName = "Bob", LastName = null } - }; - _dbContext.TodoItems.Add(_todoItem); - } - - public async Task InitializeAsync() - { - await _dbContext.SaveChangesAsync(); - } - - public Task DisposeAsync() - { - return Task.CompletedTask; - } - - [Theory] - [InlineData(null, null, null, NullValueHandling.Include)] - [InlineData(null, null, "false", NullValueHandling.Include)] - [InlineData(null, null, "true", NullValueHandling.Include)] - [InlineData(null, null, "unknown", null)] - [InlineData(null, null, "", null)] - [InlineData(null, false, null, NullValueHandling.Include)] - [InlineData(null, false, "false", NullValueHandling.Include)] - [InlineData(null, false, "true", NullValueHandling.Include)] - [InlineData(null, false, "unknown", null)] - [InlineData(null, false, "", null)] - [InlineData(null, true, null, NullValueHandling.Include)] - [InlineData(null, true, "false", NullValueHandling.Ignore)] - [InlineData(null, true, "true", NullValueHandling.Include)] - [InlineData(null, true, "unknown", null)] - [InlineData(null, true, "", null)] - [InlineData(NullValueHandling.Ignore, null, null, NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, null, "false", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, null, "true", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, null, "unknown", null)] - [InlineData(NullValueHandling.Ignore, null, "", null)] - [InlineData(NullValueHandling.Ignore, false, null, NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, false, "false", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, false, "true", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, false, "unknown", null)] - [InlineData(NullValueHandling.Ignore, false, "", null)] - [InlineData(NullValueHandling.Ignore, true, null, NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, true, "false", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Ignore, true, "true", NullValueHandling.Include)] - [InlineData(NullValueHandling.Ignore, true, "unknown", null)] - [InlineData(NullValueHandling.Ignore, true, "", null)] - [InlineData(NullValueHandling.Include, null, null, NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, null, "false", NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, null, "true", NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, null, "unknown", null)] - [InlineData(NullValueHandling.Include, null, "", null)] - [InlineData(NullValueHandling.Include, false, null, NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, false, "false", NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, false, "true", NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, false, "unknown", null)] - [InlineData(NullValueHandling.Include, false, "", null)] - [InlineData(NullValueHandling.Include, true, null, NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, true, "false", NullValueHandling.Ignore)] - [InlineData(NullValueHandling.Include, true, "true", NullValueHandling.Include)] - [InlineData(NullValueHandling.Include, true, "unknown", null)] - [InlineData(NullValueHandling.Include, true, "", null)] - public async Task CheckBehaviorCombination(NullValueHandling? defaultValue, bool? allowQueryStringOverride, string queryStringValue, NullValueHandling? expected) - { - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var services = server.Host.Services; - var client = server.CreateClient(); - - var options = (JsonApiOptions)services.GetRequiredService(typeof(IJsonApiOptions)); - - if (defaultValue != null) - { - options.SerializerSettings.NullValueHandling = defaultValue.Value; - } - if (allowQueryStringOverride != null) - { - options.AllowQueryStringOverrideForSerializerNullValueHandling = allowQueryStringOverride.Value; - } - - var queryString = queryStringValue != null - ? $"&nulls={queryStringValue}" - : ""; - var route = $"/api/v1/todoItems/{_todoItem.Id}?include=owner{queryString}"; - var request = new HttpRequestMessage(HttpMethod.Get, route); - - // Act - var response = await client.SendAsync(request); - var body = await response.Content.ReadAsStringAsync(); - - var isQueryStringValueEmpty = queryStringValue == string.Empty; - var isDisallowedOverride = !options.AllowQueryStringOverrideForSerializerNullValueHandling && queryStringValue != null; - var isQueryStringInvalid = queryStringValue != null && !bool.TryParse(queryStringValue, out _); - - if (isQueryStringValueEmpty) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Missing query string parameter value.", errorDocument.Errors[0].Title); - Assert.Equal("Missing value for 'nulls' query string parameter.", errorDocument.Errors[0].Detail); - Assert.Equal("nulls", errorDocument.Errors[0].Source.Parameter); - } - else if (isDisallowedOverride) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Usage of one or more query string parameters is not allowed at the requested endpoint.", errorDocument.Errors[0].Title); - Assert.Equal("The parameter 'nulls' cannot be used at this endpoint.", errorDocument.Errors[0].Detail); - Assert.Equal("nulls", errorDocument.Errors[0].Source.Parameter); - } - else if (isQueryStringInvalid) - { - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("The specified nulls is invalid.", errorDocument.Errors[0].Title); - Assert.Equal("The value 'unknown' must be 'true' or 'false'.", errorDocument.Errors[0].Detail); - Assert.Equal("nulls", errorDocument.Errors[0].Source.Parameter); - } - else - { - if (expected == null) - { - throw new Exception("Invalid test combination. Should never get here."); - } - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var deserializeBody = JsonConvert.DeserializeObject(body); - Assert.Equal(expected == NullValueHandling.Include, deserializeBody.SingleData.Attributes.ContainsKey("description")); - Assert.Equal(expected == NullValueHandling.Include, deserializeBody.Included[0].Attributes.ContainsKey("lastName")); - } - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Appointment.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Appointment.cs new file mode 100644 index 0000000000..62838a6df8 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Appointment.cs @@ -0,0 +1,18 @@ +using System; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class Appointment : Identifiable + { + [Attr] + public string Title { get; set; } + + [Attr] + public DateTimeOffset StartTime { get; set; } + + [Attr] + public DateTimeOffset EndTime { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Calendar.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Calendar.cs new file mode 100644 index 0000000000..5447aba40b --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Calendar.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class Calendar : Identifiable + { + [Attr] + public string TimeZone { get; set; } + + [Attr] + public int DefaultAppointmentDurationInMinutes { get; set; } + + [HasMany] + public ISet Appointments { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/CalendarsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/CalendarsController.cs new file mode 100644 index 0000000000..7aeed74bd0 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/CalendarsController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class CalendarsController : JsonApiController + { + public CalendarsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs new file mode 100644 index 0000000000..8c25b6efba --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class QueryStringDbContext : DbContext + { + public DbSet Calendars { get; set; } + public DbSet Appointments { get; set; } + + public QueryStringDbContext(DbContextOptions options) : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs new file mode 100644 index 0000000000..3a14f8f64e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs @@ -0,0 +1,24 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + internal sealed class QueryStringFakers : FakerContainer + { + private readonly Lazy> _lazyCalendarFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(calendar => calendar.TimeZone, f => f.Date.TimeZoneString()) + .RuleFor(calendar => calendar.DefaultAppointmentDurationInMinutes, f => f.PickRandom(15, 30, 45, 60))); + + private readonly Lazy> _lazyAppointmentFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(appointment => appointment.Title, f => f.Random.Word()) + .RuleFor(appointment => appointment.StartTime, f => f.Date.FutureOffset()) + .RuleFor(appointment => appointment.EndTime, (f, appointment) => appointment.StartTime.AddHours(f.Random.Double(1, 4)))); + + public Faker Calendar => _lazyCalendarFaker.Value; + public Faker Appointment => _lazyAppointmentFaker.Value; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs index 6241847a31..2084a8dadb 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs @@ -3,18 +3,18 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings { - public sealed class QueryStringTests : IClassFixture> + public sealed class QueryStringTests + : IClassFixture, QueryStringDbContext>> { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly QueryStringFakers _fakers = new QueryStringFakers(); - public QueryStringTests(IntegrationTestContext testContext) + public QueryStringTests(IntegrationTestContext, QueryStringDbContext> testContext) { _testContext = testContext; } @@ -26,7 +26,7 @@ public async Task Cannot_use_unknown_query_string_parameter() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.AllowUnknownQueryStringParameters = false; - var route = "/api/v1/articles?foo=bar"; + var route = "/calendars?foo=bar"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -48,7 +48,7 @@ public async Task Can_use_unknown_query_string_parameter() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.AllowUnknownQueryStringParameters = true; - var route = "/api/v1/articles?foo=bar"; + var route = "/calendars?foo=bar"; // Act var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); @@ -71,7 +71,7 @@ public async Task Cannot_use_empty_query_string_parameter_value(string parameter var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.AllowUnknownQueryStringParameters = false; - var route = "/api/v1/articles?" + parameterName + "="; + var route = "calendars?" + parameterName + "="; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs new file mode 100644 index 0000000000..ec9c5275eb --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs @@ -0,0 +1,97 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class SerializerDefaultValueHandlingTests + : IClassFixture, QueryStringDbContext>> + { + private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly QueryStringFakers _fakers = new QueryStringFakers(); + + public SerializerDefaultValueHandlingTests(IntegrationTestContext, QueryStringDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Cannot_override_from_query_string() + { + // Arrange + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.AllowQueryStringOverrideForSerializerDefaultValueHandling = false; + + var route = "/calendars?defaults=true"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest); + responseDocument.Errors[0].Title.Should().Be("Usage of one or more query string parameters is not allowed at the requested endpoint."); + responseDocument.Errors[0].Detail.Should().Be("The parameter 'defaults' cannot be used at this endpoint."); + responseDocument.Errors[0].Source.Parameter.Should().Be("defaults"); + } + + [Theory] + [InlineData(null, null, true)] + [InlineData(null, "false", false)] + [InlineData(null, "true", true)] + [InlineData(DefaultValueHandling.Ignore, null, false)] + [InlineData(DefaultValueHandling.Ignore, "false", false)] + [InlineData(DefaultValueHandling.Ignore, "true", true)] + [InlineData(DefaultValueHandling.Include, null, true)] + [InlineData(DefaultValueHandling.Include, "false", false)] + [InlineData(DefaultValueHandling.Include, "true", true)] + public async Task Can_override_from_query_string(DefaultValueHandling? configurationValue, string queryStringValue, bool expectInDocument) + { + // Arrange + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.AllowQueryStringOverrideForSerializerDefaultValueHandling = true; + options.SerializerSettings.DefaultValueHandling = configurationValue ?? DefaultValueHandling.Include; + + var calendar = _fakers.Calendar.Generate(); + calendar.DefaultAppointmentDurationInMinutes = default; + calendar.Appointments = _fakers.Appointment.Generate(1).ToHashSet(); + calendar.Appointments.Single().EndTime = default; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Calendars.Add(calendar); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/calendars/{calendar.StringId}?include=appointments" + (queryStringValue != null ? "&defaults=" + queryStringValue : ""); + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.Included.Should().HaveCount(1); + + if (expectInDocument) + { + responseDocument.SingleData.Attributes.Should().ContainKey("defaultAppointmentDurationInMinutes"); + responseDocument.Included[0].Attributes.Should().ContainKey("endTime"); + } + else + { + responseDocument.SingleData.Attributes.Should().NotContainKey("defaultAppointmentDurationInMinutes"); + responseDocument.Included[0].Attributes.Should().NotContainKey("endTime"); + } + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs new file mode 100644 index 0000000000..9487ae3b9e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs @@ -0,0 +1,97 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class SerializerNullValueHandlingTests + : IClassFixture, QueryStringDbContext>> + { + private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly QueryStringFakers _fakers = new QueryStringFakers(); + + public SerializerNullValueHandlingTests(IntegrationTestContext, QueryStringDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Cannot_override_from_query_string() + { + // Arrange + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.AllowQueryStringOverrideForSerializerNullValueHandling = false; + + var route = "/calendars?nulls=true"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest); + responseDocument.Errors[0].Title.Should().Be("Usage of one or more query string parameters is not allowed at the requested endpoint."); + responseDocument.Errors[0].Detail.Should().Be("The parameter 'nulls' cannot be used at this endpoint."); + responseDocument.Errors[0].Source.Parameter.Should().Be("nulls"); + } + + [Theory] + [InlineData(null, null, true)] + [InlineData(null, "false", false)] + [InlineData(null, "true", true)] + [InlineData(NullValueHandling.Ignore, null, false)] + [InlineData(NullValueHandling.Ignore, "false", false)] + [InlineData(NullValueHandling.Ignore, "true", true)] + [InlineData(NullValueHandling.Include, null, true)] + [InlineData(NullValueHandling.Include, "false", false)] + [InlineData(NullValueHandling.Include, "true", true)] + public async Task Can_override_from_query_string(NullValueHandling? configurationValue, string queryStringValue, bool expectInDocument) + { + // Arrange + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.AllowQueryStringOverrideForSerializerNullValueHandling = true; + options.SerializerSettings.NullValueHandling = configurationValue ?? NullValueHandling.Include; + + var calendar = _fakers.Calendar.Generate(); + calendar.TimeZone = null; + calendar.Appointments = _fakers.Appointment.Generate(1).ToHashSet(); + calendar.Appointments.Single().Title = null; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Calendars.Add(calendar); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/calendars/{calendar.StringId}?include=appointments" + (queryStringValue != null ? "&nulls=" + queryStringValue : ""); + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.Included.Should().HaveCount(1); + + if (expectInDocument) + { + responseDocument.SingleData.Attributes.Should().ContainKey("timeZone"); + responseDocument.Included[0].Attributes.Should().ContainKey("title"); + } + else + { + responseDocument.SingleData.Attributes.Should().NotContainKey("timeZone"); + responseDocument.Included[0].Attributes.Should().NotContainKey("title"); + } + } + } +} From 0c5d5e86d1dae19e816df464d8164cfc332b6505 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Wed, 27 Jan 2021 11:43:04 +0100 Subject: [PATCH 05/47] Refactored tests for DisableQueryStringAttribute --- .../Controllers/CountriesController.cs | 3 - .../Controllers/TagsController.cs | 2 - .../Startups/Startup.cs | 5 -- .../Spec/DisableQueryAttributeTests.cs | 67 --------------- .../BlockingHttpDeleteController.cs | 2 + .../BlockingWritesController.cs | 1 + .../DisableQueryStringTests.cs | 85 +++++++++++++++++++ .../SkipCacheQueryStringParameterReader.cs | 6 +- 8 files changed, 91 insertions(+), 80 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DisableQueryAttributeTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs rename {src/Examples/JsonApiDotNetCoreExample/Services => test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers}/SkipCacheQueryStringParameterReader.cs (79%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/CountriesController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/CountriesController.cs index 2f37dcb387..25c875cdc9 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/CountriesController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/CountriesController.cs @@ -1,14 +1,11 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.QueryStrings; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCoreExample.Controllers { - [DisableQueryString(StandardQueryStringParameters.Sort | StandardQueryStringParameters.Page)] public sealed class CountriesController : JsonApiController { public CountriesController( diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs index bccf2192d6..c06452cef7 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs @@ -1,13 +1,11 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCoreExample.Controllers { - [DisableQueryString("skipCache")] public sealed class TagsController : JsonApiController { public TagsController( diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs index 628e566a58..983f6a9dad 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs @@ -1,8 +1,6 @@ using System; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.QueryStrings; using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -28,9 +26,6 @@ public override void ConfigureServices(IServiceCollection services) { ConfigureClock(services); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddDbContext(options => { options.EnableSensitiveDataLogging(); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DisableQueryAttributeTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DisableQueryAttributeTests.cs deleted file mode 100644 index 42492f3abd..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DisableQueryAttributeTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec -{ - [Collection("WebHostCollection")] - public sealed class DisableQueryAttributeTests - { - private readonly TestFixture _fixture; - - public DisableQueryAttributeTests(TestFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task Cannot_Sort_If_Query_String_Parameter_Is_Blocked_By_Controller() - { - // Arrange - var httpMethod = new HttpMethod("GET"); - var route = "/api/v1/countries?sort=name"; - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Usage of one or more query string parameters is not allowed at the requested endpoint.", errorDocument.Errors[0].Title); - Assert.Equal("The parameter 'sort' cannot be used at this endpoint.", errorDocument.Errors[0].Detail); - Assert.Equal("sort", errorDocument.Errors[0].Source.Parameter); - } - - [Fact] - public async Task Cannot_Use_Custom_Query_String_Parameter_If_Blocked_By_Controller() - { - // Arrange - var httpMethod = new HttpMethod("GET"); - var route = "/api/v1/tags?skipCache=true"; - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.BadRequest, errorDocument.Errors[0].StatusCode); - Assert.Equal("Usage of one or more query string parameters is not allowed at the requested endpoint.", errorDocument.Errors[0].Title); - Assert.Equal("The parameter 'skipCache' cannot be used at this endpoint.", errorDocument.Errors[0].Detail); - Assert.Equal("skipCache", errorDocument.Errors[0].Source.Parameter); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs index 9651950dd3..39a3eea31b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpDeleteController.cs @@ -1,12 +1,14 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.QueryStrings; using JsonApiDotNetCore.Services; using Microsoft.Extensions.Logging; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { [NoHttpDelete] + [DisableQueryString(StandardQueryStringParameters.Sort | StandardQueryStringParameters.Page)] public sealed class BlockingHttpDeleteController : JsonApiController { public BlockingHttpDeleteController(IJsonApiOptions options, ILoggerFactory loggerFactory, diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs index 5704bb5600..21602e5487 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs @@ -7,6 +7,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { [HttpReadOnly] + [DisableQueryString("skipCache")] public sealed class BlockingWritesController : JsonApiController { public BlockingWritesController(IJsonApiOptions options, ILoggerFactory loggerFactory, diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs new file mode 100644 index 0000000000..15e3987762 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs @@ -0,0 +1,85 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.QueryStrings; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers +{ + public sealed class DisableQueryStringTests + : IClassFixture, RestrictionDbContext>> + { + private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly RestrictionFakers _fakers = new RestrictionFakers(); + + public DisableQueryStringTests(IntegrationTestContext, RestrictionDbContext> testContext) + { + _testContext = testContext; + + testContext.ConfigureServicesAfterStartup(services => + { + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + }); + } + + [Fact] + public async Task Cannot_sort_if_query_string_parameter_is_blocked_by_controller() + { + // Arrange + var route = "/sofas?sort=id"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest); + responseDocument.Errors[0].Title.Should().Be("Usage of one or more query string parameters is not allowed at the requested endpoint."); + responseDocument.Errors[0].Detail.Should().Be("The parameter 'sort' cannot be used at this endpoint."); + responseDocument.Errors[0].Source.Parameter.Should().Be("sort"); + } + + [Fact] + public async Task Cannot_paginate_if_query_string_parameter_is_blocked_by_controller() + { + // Arrange + var route = "/sofas?page[number]=2"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest); + responseDocument.Errors[0].Title.Should().Be("Usage of one or more query string parameters is not allowed at the requested endpoint."); + responseDocument.Errors[0].Detail.Should().Be("The parameter 'page[number]' cannot be used at this endpoint."); + responseDocument.Errors[0].Source.Parameter.Should().Be("page[number]"); + } + + [Fact] + public async Task Cannot_use_custom_query_string_parameter_if_blocked_by_controller() + { + // Arrange + var route = "/beds?skipCache=true"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest); + responseDocument.Errors[0].Title.Should().Be("Usage of one or more query string parameters is not allowed at the requested endpoint."); + responseDocument.Errors[0].Detail.Should().Be("The parameter 'skipCache' cannot be used at this endpoint."); + responseDocument.Errors[0].Source.Parameter.Should().Be("skipCache"); + } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/SkipCacheQueryStringParameterReader.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/SkipCacheQueryStringParameterReader.cs similarity index 79% rename from src/Examples/JsonApiDotNetCoreExample/Services/SkipCacheQueryStringParameterReader.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/SkipCacheQueryStringParameterReader.cs index 14db454c6b..a165357740 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Services/SkipCacheQueryStringParameterReader.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/SkipCacheQueryStringParameterReader.cs @@ -4,9 +4,9 @@ using JsonApiDotNetCore.QueryStrings; using Microsoft.Extensions.Primitives; -namespace JsonApiDotNetCoreExample.Services +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { - public class SkipCacheQueryStringParameterReader : IQueryStringParameterReader + public sealed class SkipCacheQueryStringParameterReader : IQueryStringParameterReader { private const string _skipCacheParameterName = "skipCache"; @@ -27,7 +27,7 @@ public void Read(string parameterName, StringValues parameterValue) if (!bool.TryParse(parameterValue, out bool skipCache)) { throw new InvalidQueryStringParameterException(parameterName, "Boolean value required.", - $"The value {parameterValue} is not a valid boolean."); + $"The value '{parameterValue}' is not a valid boolean."); } SkipCache = skipCache; From 7057c89aa2f97d59adf2bdffc6a731df3b2ed422 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 28 Jan 2021 11:54:14 +0100 Subject: [PATCH 06/47] Refactored tests for resource injection --- .../Acceptance/InjectableResourceTests.cs | 209 ---------- .../FrozenSystemClock.cs | 13 + .../GiftCertificate.cs | 28 ++ .../GiftCertificatesController.cs | 16 + .../InjectionDbContext.cs | 20 + .../InjectionFakers.cs | 40 ++ .../PostOffice.cs | 35 ++ .../PostOfficesController.cs | 16 + .../ResourceInjectionTests.cs | 360 ++++++++++++++++++ 9 files changed, 528 insertions(+), 209 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/InjectableResourceTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/InjectableResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/InjectableResourceTests.cs deleted file mode 100644 index 6388be509f..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/InjectableResourceTests.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Bogus; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Newtonsoft.Json; -using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public class InjectableResourceTests - { - private readonly TestFixture _fixture; - private readonly AppDbContext _context; - private readonly Faker _personFaker; - private readonly Faker _passportFaker; - private readonly Faker _countryFaker; - - public InjectableResourceTests(TestFixture fixture) - { - _fixture = fixture; - _context = fixture.GetRequiredService(); - - _personFaker = new Faker() - .RuleFor(t => t.FirstName, f => f.Name.FirstName()) - .RuleFor(t => t.LastName, f => f.Name.LastName()); - _passportFaker = new Faker() - .CustomInstantiator(f => new Passport(_context)) - .RuleFor(t => t.SocialSecurityNumber, f => f.Random.Number(100, 10_000)); - _countryFaker = new Faker() - .RuleFor(c => c.Name, f => f.Address.Country()); - } - - [Fact] - public async Task Can_Get_Single_Passport() - { - // Arrange - var passport = _passportFaker.Generate(); - passport.BirthCountry = _countryFaker.Generate(); - - _context.Passports.Add(passport); - await _context.SaveChangesAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/passports/" + passport.StringId); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - _fixture.AssertEqualStatusCode(HttpStatusCode.OK, response); - - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); - - Assert.NotNull(document.SingleData); - Assert.Equal(passport.IsLocked, document.SingleData.Attributes["isLocked"]); - } - - [Fact] - public async Task Can_Get_Passports() - { - // Arrange - await _context.ClearTableAsync(); - - var passports = _passportFaker.Generate(3); - foreach (var passport in passports) - { - passport.BirthCountry = _countryFaker.Generate(); - } - - _context.Passports.AddRange(passports); - await _context.SaveChangesAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/passports"); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - _fixture.AssertEqualStatusCode(HttpStatusCode.OK, response); - - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); - - Assert.Equal(3, document.ManyData.Count); - foreach (var passport in passports) - { - Assert.Contains(document.ManyData, - resource => (long)resource.Attributes["socialSecurityNumber"] == passport.SocialSecurityNumber); - Assert.Contains(document.ManyData, - resource => (string)resource.Attributes["birthCountryName"] == passport.BirthCountryName); - } - } - - [Fact] - public async Task Can_Get_Passports_With_Filter() - { - // Arrange - await _context.ClearTableAsync(); - - var passports = _passportFaker.Generate(3); - foreach (var passport in passports) - { - passport.SocialSecurityNumber = 11111; - passport.BirthCountry = _countryFaker.Generate(); - passport.Person = _personFaker.Generate(); - passport.Person.FirstName = "Jack"; - } - - passports[2].SocialSecurityNumber = 12345; - passports[2].Person.FirstName= "Joe"; - - _context.Passports.AddRange(passports); - await _context.SaveChangesAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/passports?include=person&filter=and(equals(socialSecurityNumber,'12345'),equals(person.firstName,'Joe'))"); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - _fixture.AssertEqualStatusCode(HttpStatusCode.OK, response); - - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); - - Assert.Single(document.ManyData); - Assert.Equal(12345L, document.ManyData[0].Attributes["socialSecurityNumber"]); - - Assert.Single(document.Included); - Assert.Equal("Joe", document.Included[0].Attributes["firstName"]); - } - - [Fact] - public async Task Can_Get_Passports_With_Sparse_Fieldset() - { - // Arrange - await _context.ClearTableAsync(); - - var passports = _passportFaker.Generate(2); - foreach (var passport in passports) - { - passport.BirthCountry = _countryFaker.Generate(); - passport.Person = _personFaker.Generate(); - } - - _context.Passports.AddRange(passports); - await _context.SaveChangesAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/passports?include=person&fields[passports]=socialSecurityNumber&fields[people]=firstName"); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - _fixture.AssertEqualStatusCode(HttpStatusCode.OK, response); - - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); - - Assert.Equal(2, document.ManyData.Count); - foreach (var passport in passports) - { - Assert.Contains(document.ManyData, - resource => (long)resource.Attributes["socialSecurityNumber"] == passport.SocialSecurityNumber); - } - - Assert.DoesNotContain(document.ManyData, - resource => resource.Attributes.ContainsKey("isLocked")); - - Assert.Equal(2, document.Included.Count); - foreach (var person in passports.Select(p => p.Person)) - { - Assert.Contains(document.Included, - resource => (string) resource.Attributes["firstName"] == person.FirstName); - } - - Assert.DoesNotContain(document.Included, - resource => resource.Attributes.ContainsKey("lastName")); - } - - [Fact] - public async Task Fail_When_Deleting_Missing_Passport() - { - // Arrange - - var request = new HttpRequestMessage(HttpMethod.Delete, "/api/v1/passports/1234567890"); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - _fixture.AssertEqualStatusCode(HttpStatusCode.NotFound, response); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.NotFound, errorDocument.Errors[0].StatusCode); - Assert.Equal("The requested resource does not exist.", errorDocument.Errors[0].Title); - Assert.Equal("Resource of type 'passports' with ID '1234567890' does not exist.", errorDocument.Errors[0].Detail); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs b/test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs new file mode 100644 index 0000000000..f08b81b905 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.AspNetCore.Authentication; + +namespace JsonApiDotNetCoreExampleTests +{ + internal sealed class FrozenSystemClock : ISystemClock + { + private static readonly DateTimeOffset _defaultTime = + new DateTimeOffset(new DateTime(2000, 1, 1, 1, 1, 1), TimeSpan.FromHours(1)); + + public DateTimeOffset UtcNow { get; set; } = _defaultTime; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs new file mode 100644 index 0000000000..44436e03ee --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; +using Microsoft.AspNetCore.Authentication; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class GiftCertificate : Identifiable + { + private readonly ISystemClock _systemClock; + + [Attr] + public DateTimeOffset IssueDate { get; set; } + + [Attr(Capabilities = AttrCapabilities.AllowView)] + [NotMapped] + public bool HasExpired => IssueDate.AddYears(1) < _systemClock.UtcNow; + + [HasOne] + public PostOffice Issuer { get; set; } + + public GiftCertificate(InjectionDbContext injectionDbContext) + { + _systemClock = injectionDbContext.SystemClock; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs new file mode 100644 index 0000000000..8d4b5c1e39 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class GiftCertificatesController : JsonApiController + { + public GiftCertificatesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs new file mode 100644 index 0000000000..2b695dfd45 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class InjectionDbContext : DbContext + { + public ISystemClock SystemClock { get; } + + public DbSet PostOffice { get; set; } + public DbSet GiftCertificates { get; set; } + + public InjectionDbContext(DbContextOptions options, ISystemClock systemClock) + : base(options) + { + SystemClock = systemClock ?? throw new ArgumentNullException(nameof(systemClock)); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs new file mode 100644 index 0000000000..6dbf85a9c3 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs @@ -0,0 +1,40 @@ +using System; +using Bogus; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + internal sealed class InjectionFakers : FakerContainer + { + private readonly IServiceProvider _serviceProvider; + + private readonly Lazy> _lazyPostOfficeFaker; + private readonly Lazy> _lazyGiftCertificateFaker; + + public Faker PostOffice => _lazyPostOfficeFaker.Value; + public Faker GiftCertificate => _lazyGiftCertificateFaker.Value; + + public InjectionFakers(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + _lazyPostOfficeFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .CustomInstantiator(f => new PostOffice(ResolveDbContext())) + .RuleFor(postOffice => postOffice.Address, f => f.Address.FullAddress())); + + _lazyGiftCertificateFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .CustomInstantiator(f => new GiftCertificate(ResolveDbContext())) + .RuleFor(giftCertificate => giftCertificate.IssueDate, f => f.Date.PastOffset())); + } + + private InjectionDbContext ResolveDbContext() + { + using var scope = _serviceProvider.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs new file mode 100644 index 0000000000..e183760098 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; +using Microsoft.AspNetCore.Authentication; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class PostOffice : Identifiable + { + private readonly ISystemClock _systemClock; + + [Attr] + public string Address { get; set; } + + [Attr(Capabilities = AttrCapabilities.AllowView)] + [NotMapped] + public bool IsOpen => IsWithinOperatingHours(); + + [HasMany] + public IList GiftCertificates { get; set; } + + public PostOffice(InjectionDbContext injectionDbContext) + { + _systemClock = injectionDbContext.SystemClock; + } + + private bool IsWithinOperatingHours() + { + var currentTime = _systemClock.UtcNow; + return currentTime.DayOfWeek >= DayOfWeek.Monday && currentTime.DayOfWeek <= DayOfWeek.Friday && currentTime.Hour >= 9 && currentTime.Hour <= 17; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs new file mode 100644 index 0000000000..aff0d117c3 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class PostOfficesController : JsonApiController + { + public PostOfficesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs new file mode 100644 index 0000000000..449c9f2ddb --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -0,0 +1,360 @@ +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using FluentAssertions.Extensions; +using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.AspNetCore.Authentication; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection +{ + public sealed class ResourceInjectionTests + : IClassFixture, InjectionDbContext>> + { + private readonly IntegrationTestContext, InjectionDbContext> _testContext; + private readonly InjectionFakers _fakers; + + public ResourceInjectionTests(IntegrationTestContext, InjectionDbContext> testContext) + { + _testContext = testContext; + + testContext.ConfigureServicesBeforeStartup(services => + { + services.AddSingleton(); + }); + + _fakers = new InjectionFakers(testContext.Factory.Services); + } + + [Fact] + public async Task Can_get_resource_by_ID() + { + // Arrange + var clock = (FrozenSystemClock) _testContext.Factory.Services.GetRequiredService(); + clock.UtcNow = 27.January(2021); + + var certificate = _fakers.GiftCertificate.Generate(); + certificate.IssueDate = 28.January(2020); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.GiftCertificates.Add(certificate); + await dbContext.SaveChangesAsync(); + }); + + var route = "/giftCertificates/" + certificate.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Id.Should().Be(certificate.StringId); + responseDocument.SingleData.Attributes["issueDate"].Should().Be(certificate.IssueDate.DateTime); + responseDocument.SingleData.Attributes["hasExpired"].Should().Be(false); + } + + [Fact] + public async Task Can_filter_resources_by_ID() + { + // Arrange + var clock = (FrozenSystemClock) _testContext.Factory.Services.GetRequiredService(); + clock.UtcNow = 27.January(2021).At(13, 53); + + var postOffices = _fakers.PostOffice.Generate(2); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.PostOffice.AddRange(postOffices); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/postOffices?filter=equals(id,'{postOffices[1].StringId}')"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Id.Should().Be(postOffices[1].StringId); + responseDocument.ManyData[0].Attributes["address"].Should().Be(postOffices[1].Address); + responseDocument.ManyData[0].Attributes["isOpen"].Should().Be(true); + } + + [Fact] + public async Task Can_get_secondary_resource_with_fieldset() + { + // Arrange + var clock = (FrozenSystemClock) _testContext.Factory.Services.GetRequiredService(); + clock.UtcNow = 27.January(2021).At(13, 53); + + var certificate = _fakers.GiftCertificate.Generate(); + certificate.Issuer = _fakers.PostOffice.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.GiftCertificates.Add(certificate); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/giftCertificates/{certificate.StringId}/issuer?fields[postOffices]=id,isOpen"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Id.Should().Be(certificate.Issuer.StringId); + responseDocument.SingleData.Attributes.Should().HaveCount(1); + responseDocument.SingleData.Attributes["isOpen"].Should().Be(true); + } + + [Fact] + public async Task Can_create_resource_with_ToOne_relationship_and_include() + { + // Arrange + var clock = (FrozenSystemClock) _testContext.Factory.Services.GetRequiredService(); + clock.UtcNow = 19.March(1998).At(6, 34); + + var existingOffice = _fakers.PostOffice.Generate(); + + var newIssueDate = 18.March(1997); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PostOffice.Add(existingOffice); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "giftCertificates", + attributes = new + { + issueDate = newIssueDate + }, + relationships = new + { + issuer = new + { + data = new + { + type = "postOffices", + id = existingOffice.StringId + } + } + } + } + }; + + var route = "/giftCertificates?include=issuer"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Attributes["issueDate"].Should().Be(newIssueDate); + responseDocument.SingleData.Attributes["hasExpired"].Should().Be(true); + responseDocument.SingleData.Relationships["issuer"].SingleData.Id.Should().Be(existingOffice.StringId); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Id.Should().Be(existingOffice.StringId); + responseDocument.Included[0].Attributes["address"].Should().Be(existingOffice.Address); + responseDocument.Included[0].Attributes["isOpen"].Should().Be(false); + + var newCertificateId = int.Parse(responseDocument.SingleData.Id); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var certificateInDatabase = await dbContext.GiftCertificates + .Include(giftCertificate => giftCertificate.Issuer) + .FirstAsync(giftCertificate => giftCertificate.Id == newCertificateId); + + certificateInDatabase.IssueDate.Should().Be(newIssueDate); + + certificateInDatabase.Issuer.Should().NotBeNull(); + certificateInDatabase.Issuer.Id.Should().Be(existingOffice.Id); + }); + } + + [Fact] + public async Task Can_update_resource_with_ToMany_relationship() + { + // Arrange + var clock = (FrozenSystemClock) _testContext.Factory.Services.GetRequiredService(); + clock.UtcNow = 19.March(1998).At(6, 34); + + var existingOffice = _fakers.PostOffice.Generate(); + existingOffice.GiftCertificates = _fakers.GiftCertificate.Generate(1); + + var newAddress = _fakers.PostOffice.Generate().Address; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PostOffice.Add(existingOffice); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "postOffices", + id = existingOffice.StringId, + attributes = new + { + address = newAddress + }, + relationships = new + { + giftCertificates = new + { + data = new[] + { + new + { + type = "giftCertificates", + id = existingOffice.GiftCertificates[0].StringId + } + } + } + } + } + }; + + var route = "/postOffices/" + existingOffice.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + + responseDocument.Should().BeEmpty(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var officeInDatabase = await dbContext.PostOffice + .Include(postOffice => postOffice.GiftCertificates) + .FirstAsync(postOffice => postOffice.Id == existingOffice.Id); + + officeInDatabase.Address.Should().Be(newAddress); + + officeInDatabase.GiftCertificates.Should().HaveCount(1); + officeInDatabase.GiftCertificates[0].Id.Should().Be(existingOffice.GiftCertificates[0].Id); + }); + } + + [Fact] + public async Task Can_delete_resource() + { + // Arrange + var existingOffice = _fakers.PostOffice.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PostOffice.Add(existingOffice); + await dbContext.SaveChangesAsync(); + }); + + var route = "/postOffices/" + existingOffice.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + + responseDocument.Should().BeEmpty(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var officeInDatabase = await dbContext.PostOffice + .FirstOrDefaultAsync(postOffice => postOffice.Id == existingOffice.Id); + + officeInDatabase.Should().BeNull(); + }); + } + + [Fact] + public async Task Cannot_delete_unknown_resource() + { + // Arrange + var route = "/postOffices/99999999"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.NotFound); + responseDocument.Errors[0].Title.Should().Be("The requested resource does not exist."); + responseDocument.Errors[0].Detail.Should().Be("Resource of type 'postOffices' with ID '99999999' does not exist."); + } + + [Fact] + public async Task Can_add_to_ToMany_relationship() + { + // Arrange + var existingOffice = _fakers.PostOffice.Generate(); + existingOffice.GiftCertificates = _fakers.GiftCertificate.Generate(1); + + var existingCertificate = _fakers.GiftCertificate.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.AddRange(existingOffice, existingCertificate); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "giftCertificates", + id = existingCertificate.StringId + } + } + }; + + var route = $"/postOffices/{existingOffice.StringId}/relationships/giftCertificates"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); + + responseDocument.Should().BeEmpty(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var officeInDatabase = await dbContext.PostOffice + .Include(postOffice => postOffice.GiftCertificates) + .FirstAsync(postOffice => postOffice.Id == existingOffice.Id); + + officeInDatabase.GiftCertificates.Should().HaveCount(2); + }); + } + } +} From 8756f2555031c11e130c49b9a78b39022780dd1f Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 28 Jan 2021 13:29:33 +0100 Subject: [PATCH 07/47] Fixed assertions on DateTime/DateTimeOffset in tests --- .../IntegrationTestConfiguration.cs | 19 +++++++++++++++ .../IntegrationTestContext.cs | 3 ++- .../Filtering/FilterDataTypeTests.cs | 11 +++++---- .../Filtering/FilterOperatorTests.cs | 2 +- .../IntegrationTests/Includes/IncludeTests.cs | 4 ++-- .../NamingConventions/KebabCasingTests.cs | 2 +- .../ObjectAssertionsExtensions.cs | 23 +++++++------------ .../ResourceInjectionTests.cs | 9 ++++---- .../SparseFieldSets/SparseFieldSetTests.cs | 2 +- 9 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs new file mode 100644 index 0000000000..7f92b3ee67 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace JsonApiDotNetCoreExampleTests +{ + internal static class IntegrationTestConfiguration + { + // Because our tests often deserialize incoming responses into weakly-typed string-to-object dictionaries (as part of ResourceObject), + // Newtonsoft.JSON is unable to infer the target type in such cases. So we steer a bit using explicit configuration. + public static readonly JsonSerializerSettings DeserializationSettings = new JsonSerializerSettings + { + // Choosing between DateTime and DateTimeOffset is impossible: it depends on how the resource properties are declared. + // So instead we leave them as strings and let the test itself deal with the conversion. + DateParseHandling = DateParseHandling.None, + + // Here we must choose between double (default) and decimal. Favored decimal because it has higher precision (but lower range). + FloatParseHandling = FloatParseHandling.Decimal + }; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs index cb12fc2c09..ea0f41f279 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs @@ -189,7 +189,8 @@ private TResponseDocument DeserializeResponse(string response try { - return JsonConvert.DeserializeObject(responseText); + return JsonConvert.DeserializeObject(responseText, + IntegrationTestConfiguration.DeserializationSettings); } catch (JsonException exception) { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs index 90aa2dd639..d3f062b99a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs @@ -2,6 +2,7 @@ using System.Net; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Common; using FluentAssertions.Extensions; using Humanizer; using JsonApiDotNetCore.Configuration; @@ -138,7 +139,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Attributes["someDateTime"].Should().Be(resource.SomeDateTime); + responseDocument.ManyData[0].Attributes["someDateTime"].Should().BeCloseTo(resource.SomeDateTime); } [Fact] @@ -147,7 +148,7 @@ public async Task Can_filter_equality_on_type_DateTimeOffset() // Arrange var resource = new FilterableResource { - SomeDateTimeOffset = new DateTimeOffset(27.January(2003).At(11, 22, 33, 44), TimeSpan.FromHours(3)) + SomeDateTimeOffset = 27.January(2003).At(11, 22, 33, 44).ToDateTimeOffset(TimeSpan.FromHours(3)) }; await _testContext.RunOnDatabaseAsync(async dbContext => @@ -167,7 +168,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Attributes["someDateTimeOffset"].Should().Be(resource.SomeDateTimeOffset.LocalDateTime); + responseDocument.ManyData[0].Attributes["someDateTimeOffset"].Should().BeCloseTo(resource.SomeDateTimeOffset); } [Fact] @@ -254,7 +255,7 @@ public async Task Can_filter_is_null_on_type(string propertyName) SomeNullableDouble = 1, SomeNullableGuid = Guid.NewGuid(), SomeNullableDateTime = 1.January(2001), - SomeNullableDateTimeOffset = 1.January(2001), + SomeNullableDateTimeOffset = 1.January(2001).ToDateTimeOffset(TimeSpan.FromHours(-1)), SomeNullableTimeSpan = TimeSpan.FromHours(1), SomeNullableEnum = DayOfWeek.Friday }; @@ -305,7 +306,7 @@ public async Task Can_filter_is_not_null_on_type(string propertyName) SomeNullableDouble = 1, SomeNullableGuid = Guid.NewGuid(), SomeNullableDateTime = 1.January(2001), - SomeNullableDateTimeOffset = 1.January(2001), + SomeNullableDateTimeOffset = 1.January(2001).ToDateTimeOffset(TimeSpan.FromHours(-1)), SomeNullableTimeSpan = TimeSpan.FromHours(1), SomeNullableEnum = DayOfWeek.Friday }; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs index 69254c0632..fb38aeee8d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs @@ -381,7 +381,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Attributes["someDateTime"].Should().Be(resource.SomeDateTime); + responseDocument.ManyData[0].Attributes["someDateTime"].Should().BeCloseTo(resource.SomeDateTime); } [Theory] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs index 00a281ff6f..f97e5e6a1c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs @@ -282,7 +282,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included.Should().HaveCount(1); responseDocument.Included[0].Type.Should().Be("revisions"); responseDocument.Included[0].Id.Should().Be(article.Revisions.Single().StringId); - responseDocument.Included[0].Attributes["publishTime"].Should().Be(article.Revisions.Single().PublishTime); + responseDocument.Included[0].Attributes["publishTime"].Should().BeCloseTo(article.Revisions.Single().PublishTime); } [Fact] @@ -479,7 +479,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[1].Type.Should().Be("revisions"); responseDocument.Included[1].Id.Should().Be(blog.Articles[0].Revisions.Single().StringId); - responseDocument.Included[1].Attributes["publishTime"].Should().Be(blog.Articles[0].Revisions.Single().PublishTime); + responseDocument.Included[1].Attributes["publishTime"].Should().BeCloseTo(blog.Articles[0].Revisions.Single().PublishTime); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs index 6b5929328b..20bda54a9f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -50,7 +50,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included.Should().HaveCount(1); responseDocument.Included[0].Type.Should().Be("diving-boards"); responseDocument.Included[0].Id.Should().Be(pools[1].DivingBoards[0].StringId); - responseDocument.Included[0].Attributes["height-in-meters"].Should().BeApproximately(pools[1].DivingBoards[0].HeightInMeters); + responseDocument.Included[0].Attributes["height-in-meters"].As().Should().BeApproximately(pools[1].DivingBoards[0].HeightInMeters, 0.00000000001M); responseDocument.Included[0].Relationships.Should().BeNull(); responseDocument.Included[0].Links.Self.Should().Be($"/public-api/diving-boards/{pools[1].DivingBoards[0].StringId}"); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs index 159a7691b4..d342daf992 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs @@ -7,7 +7,9 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests public static class ObjectAssertionsExtensions { /// - /// Used to assert on a nullable column, whose value is returned as in JSON:API response body. + /// Used to assert on a (nullable) or property, + /// whose value is returned as in JSON:API response body + /// because of . /// public static void BeCloseTo(this ObjectAssertions source, DateTimeOffset? expected, string because = "", params object[] becauseArgs) @@ -18,23 +20,14 @@ public static void BeCloseTo(this ObjectAssertions source, DateTimeOffset? expec } else { - // We lose a little bit of precision (milliseconds) on roundtrip through PostgreSQL database. + if (!DateTimeOffset.TryParse((string) source.Subject, out var value)) + { + source.Subject.Should().Be(expected, because, becauseArgs); + } - var value = new DateTimeOffset((DateTime) source.Subject); + // We lose a little bit of precision (milliseconds) on roundtrip through PostgreSQL database. value.Should().BeCloseTo(expected.Value, because: because, becauseArgs: becauseArgs); } } - - /// - /// Used to assert on a column, whose value is returned as in json:api response body. - /// - public static void BeApproximately(this ObjectAssertions source, decimal? expected, decimal precision = 0.00000000001M, string because = "", - params object[] becauseArgs) - { - // We lose a little bit of precision on roundtrip through PostgreSQL database. - - var value = (decimal?) (double) source.Subject; - value.Should().BeApproximately(expected, precision, because, becauseArgs); - } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs index 449c9f2ddb..17b3262114 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -1,8 +1,7 @@ -using System; -using System.Linq; using System.Net; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Common; using FluentAssertions.Extensions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.AspNetCore.Authentication; @@ -56,7 +55,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Id.Should().Be(certificate.StringId); - responseDocument.SingleData.Attributes["issueDate"].Should().Be(certificate.IssueDate.DateTime); + responseDocument.SingleData.Attributes["issueDate"].Should().BeCloseTo(certificate.IssueDate); responseDocument.SingleData.Attributes["hasExpired"].Should().Be(false); } @@ -129,7 +128,7 @@ public async Task Can_create_resource_with_ToOne_relationship_and_include() var existingOffice = _fakers.PostOffice.Generate(); - var newIssueDate = 18.March(1997); + var newIssueDate = 18.March(1997).ToDateTimeOffset(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -169,7 +168,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); - responseDocument.SingleData.Attributes["issueDate"].Should().Be(newIssueDate); + responseDocument.SingleData.Attributes["issueDate"].Should().BeCloseTo(newIssueDate); responseDocument.SingleData.Attributes["hasExpired"].Should().Be(true); responseDocument.SingleData.Relationships["issuer"].SingleData.Id.Should().Be(existingOffice.StringId); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs index 54f43380e0..029ffe87ac 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs @@ -641,7 +641,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[0].Id.Should().Be(blog.Owner.StringId); responseDocument.Included[0].Attributes["firstName"].Should().Be(blog.Owner.FirstName); responseDocument.Included[0].Attributes["lastName"].Should().Be(blog.Owner.LastName); - responseDocument.Included[0].Attributes["dateOfBirth"].Should().Be(blog.Owner.DateOfBirth); + responseDocument.Included[0].Attributes["dateOfBirth"].Should().BeCloseTo(blog.Owner.DateOfBirth); responseDocument.Included[0].Relationships["articles"].ManyData.Should().HaveCount(1); responseDocument.Included[0].Relationships["articles"].ManyData[0].Id.Should().Be(blog.Owner.Articles[0].StringId); responseDocument.Included[0].Relationships["articles"].Links.Self.Should().NotBeNull(); From 44653f8f6ee428ea9e88613e0ee74355910cabf0 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 28 Jan 2021 17:11:36 +0100 Subject: [PATCH 08/47] Refactored tests for non-json:api controllers --- .../Controllers/NonJsonApiController.cs | 51 ++++++ .../Controllers/TestValuesController.cs | 35 ---- .../Extensibility/CustomControllerTests.cs | 20 --- .../Acceptance/NonJsonApiControllerTests.cs | 104 ------------ .../NonJsonApiControllerTests.cs | 153 ++++++++++++++++++ 5 files changed, 204 insertions(+), 159 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/TestValuesController.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/NonJsonApiControllerTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs new file mode 100644 index 0000000000..90a864f07c --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs @@ -0,0 +1,51 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace JsonApiDotNetCoreExample.Controllers +{ + [Route("[controller]")] + public sealed class NonJsonApiController : ControllerBase + { + [HttpGet] + public IActionResult Get() + { + var result = new[] {"Welcome!"}; + return Ok(result); + } + + [HttpPost] + public async Task PostAsync() + { + string name = await new StreamReader(Request.Body).ReadToEndAsync(); + + if (string.IsNullOrEmpty(name)) + { + return BadRequest("Please send your name."); + } + + var result = "Hello, " + name; + return Ok(result); + } + + [HttpPut] + public IActionResult Put([FromBody] string name) + { + var result = "Hi, " + name; + return Ok(result); + } + + [HttpPatch] + public IActionResult Patch(string name) + { + var result = "Good day, " + name; + return Ok(result); + } + + [HttpDelete] + public IActionResult Delete() + { + return Ok("Bye."); + } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TestValuesController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TestValuesController.cs deleted file mode 100644 index 9487df1c3b..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TestValuesController.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace JsonApiDotNetCoreExample.Controllers -{ - [Route("[controller]")] - public class TestValuesController : ControllerBase - { - [HttpGet] - public IActionResult Get() - { - var result = new[] { "value" }; - return Ok(result); - } - - [HttpPost] - public IActionResult Post(string name) - { - var result = "Hello, " + name; - return Ok(result); - } - - [HttpPatch] - public IActionResult Patch(string name) - { - var result = "Hello, " + name; - return Ok(result); - } - - [HttpDelete] - public IActionResult Delete() - { - return Ok("Deleted"); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs index 86c021e86d..61c467cbdc 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs @@ -37,26 +37,6 @@ public CustomControllerTests(TestFixture fixture) .RuleFor(p => p.LastName, f => f.Name.LastName()); } - [Fact] - public async Task NonJsonApiControllers_DoNotUse_Dasherized_Routes() - { - // Arrange - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod("GET"); - var route = "testValues"; - - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - [Fact] public async Task CustomRouteControllers_Uses_Dasherized_Collection_Route() { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/NonJsonApiControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/NonJsonApiControllerTests.cs deleted file mode 100644 index 397914ba7b..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/NonJsonApiControllerTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - public sealed class NonJsonApiControllerTests - { - [Fact] - public async Task NonJsonApiController_Skips_Middleware_And_Formatters_On_Get() - { - // Arrange - const string route = "testValues"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Get, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("[\"value\"]", body); - } - - [Fact] - public async Task NonJsonApiController_Skips_Middleware_And_Formatters_On_Post() - { - // Arrange - const string route = "testValues?name=Jack"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Post, route) {Content = new StringContent("XXX")}; - request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("text/plain; charset=utf-8", response.Content.Headers.ContentType.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Hello, Jack", body); - } - - [Fact] - public async Task NonJsonApiController_Skips_Middleware_And_Formatters_On_Patch() - { - // Arrange - const string route = "testValues?name=Jack"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Patch, route) {Content = new StringContent("XXX")}; - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("text/plain; charset=utf-8", response.Content.Headers.ContentType.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Hello, Jack", body); - } - - [Fact] - public async Task NonJsonApiController_Skips_Middleware_And_Formatters_On_Delete() - { - // Arrange - const string route = "testValues"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Delete, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("text/plain; charset=utf-8", response.Content.Headers.ContentType.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Deleted", body); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs new file mode 100644 index 0000000000..e1e41af7be --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs @@ -0,0 +1,153 @@ +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCoreExample; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NonJsonApiControllers +{ + public sealed class NonJsonApiControllerTests : IClassFixture> + { + private readonly WebApplicationFactory _factory; + + public NonJsonApiControllerTests(WebApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task Get_skips_middleware_and_formatters() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Get, "/NonJsonApi"); + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("application/json; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("[\"Welcome!\"]"); + } + + [Fact] + public async Task Post_skips_middleware_and_formatters() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Post, "/NonJsonApi") + { + Content = new StringContent("Jack") + { + Headers = + { + ContentType = new MediaTypeHeaderValue("text/plain") + } + } + }; + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("text/plain; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("Hello, Jack"); + } + + [Fact] + public async Task Post_skips_error_handler() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Post, "/NonJsonApi"); + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("text/plain; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("Please send your name."); + } + + [Fact] + public async Task Put_skips_middleware_and_formatters() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Put, "/NonJsonApi") + { + Content = new StringContent("\"Jane\"") + { + Headers = + { + ContentType = new MediaTypeHeaderValue("application/json") + } + } + }; + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("text/plain; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("Hi, Jane"); + } + + [Fact] + public async Task Patch_skips_middleware_and_formatters() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Patch, "/NonJsonApi?name=Janice"); + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("text/plain; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("Good day, Janice"); + } + + [Fact] + public async Task Delete_skips_middleware_and_formatters() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Delete, "/NonJsonApi"); + + var client = _factory.CreateClient(); + + // Act + var httpResponse = await client.SendAsync(request); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Content.Headers.ContentType.ToString().Should().Be("text/plain; charset=utf-8"); + + string responseText = await httpResponse.Content.ReadAsStringAsync(); + responseText.Should().Be("Bye."); + } + } +} From 22738b64d1f0913750133fb3c1f8f31993b2911c Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 28 Jan 2021 18:45:03 +0100 Subject: [PATCH 09/47] Refactored tests for ActionResult usage --- .../Controllers/TodoItemsTestController.cs | 110 -------------- .../Acceptance/ActionResultTests.cs | 117 --------------- .../ActionResultDbContext.cs | 14 ++ .../ActionResultTests.cs | 142 ++++++++++++++++++ .../ControllerActionResults/Toothbrush.cs | 8 + .../ToothbrushesController.cs | 78 ++++++++++ 6 files changed, 242 insertions(+), 227 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/Toothbrush.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs deleted file mode 100644 index b4d0f9fe2c..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreExample.Controllers -{ - public abstract class AbstractTodoItemsController - : BaseJsonApiController where T : class, IIdentifiable - { - protected AbstractTodoItemsController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService service) - : base(options, loggerFactory, service) - { } - } - - [DisableRoutingConvention] - [Route("/abstract")] - public class TodoItemsTestController : AbstractTodoItemsController - { - public TodoItemsTestController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService service) - : base(options, loggerFactory, service) - { } - - [HttpGet] - public override async Task GetAsync(CancellationToken cancellationToken) - { - return await base.GetAsync(cancellationToken); - } - - [HttpGet("{id}")] - public override async Task GetAsync(int id, CancellationToken cancellationToken) - { - return await base.GetAsync(id, cancellationToken); - } - - [HttpGet("{id}/{relationshipName}")] - public override async Task GetSecondaryAsync(int id, string relationshipName, CancellationToken cancellationToken) - { - return await base.GetSecondaryAsync(id, relationshipName, cancellationToken); - } - - [HttpGet("{id}/relationships/{relationshipName}")] - public override async Task GetRelationshipAsync(int id, string relationshipName, CancellationToken cancellationToken) - { - return await base.GetRelationshipAsync(id, relationshipName, cancellationToken); - } - - [HttpPost] - public override async Task PostAsync([FromBody] TodoItem resource, CancellationToken cancellationToken) - { - await Task.Yield(); - - return NotFound(new Error(HttpStatusCode.NotFound) - { - Title = "NotFound ActionResult with explicit error object." - }); - } - - [HttpPost("{id}/relationships/{relationshipName}")] - public override async Task PostRelationshipAsync( - int id, string relationshipName, [FromBody] ISet secondaryResourceIds, CancellationToken cancellationToken) - { - return await base.PostRelationshipAsync(id, relationshipName, secondaryResourceIds, cancellationToken); - } - - [HttpPatch("{id}")] - public override async Task PatchAsync(int id, [FromBody] TodoItem resource, CancellationToken cancellationToken) - { - await Task.Yield(); - - return Conflict("Something went wrong"); - } - - [HttpPatch("{id}/relationships/{relationshipName}")] - public override async Task PatchRelationshipAsync( - int id, string relationshipName, [FromBody] object secondaryResourceIds, CancellationToken cancellationToken) - { - return await base.PatchRelationshipAsync(id, relationshipName, secondaryResourceIds, cancellationToken); - } - - [HttpDelete("{id}")] - public override async Task DeleteAsync(int id, CancellationToken cancellationToken) - { - await Task.Yield(); - - return NotFound(); - } - - [HttpDelete("{id}/relationships/{relationshipName}")] - public override async Task DeleteRelationshipAsync(int id, string relationshipName, [FromBody] ISet secondaryResourceIds, CancellationToken cancellationToken) - { - return await base.DeleteRelationshipAsync(id, relationshipName, secondaryResourceIds, cancellationToken); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs deleted file mode 100644 index 5fd33a87f5..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - [Collection("WebHostCollection")] - public sealed class ActionResultTests - { - private readonly TestFixture _fixture; - - public ActionResultTests(TestFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task ActionResult_With_Error_Object_Is_Converted_To_Error_Collection() - { - // Arrange - var route = "/abstract"; - var request = new HttpRequestMessage(HttpMethod.Post, route); - var content = new - { - data = new - { - type = "todoItems", - id = 1, - attributes = new Dictionary - { - {"ordinal", 1} - } - } - }; - - request.Content = new StringContent(JsonConvert.SerializeObject(content)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.NotFound, errorDocument.Errors[0].StatusCode); - Assert.Equal("NotFound ActionResult with explicit error object.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Empty_ActionResult_Is_Converted_To_Error_Collection() - { - // Arrange - var route = "/abstract/123"; - var request = new HttpRequestMessage(HttpMethod.Delete, route); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.NotFound, errorDocument.Errors[0].StatusCode); - Assert.Equal("NotFound", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task ActionResult_With_String_Object_Is_Converted_To_Error_Collection() - { - // Arrange - var route = "/abstract/123"; - var request = new HttpRequestMessage(HttpMethod.Patch, route); - var content = new - { - data = new - { - type = "todoItems", - id = 123, - attributes = new Dictionary - { - {"ordinal", 1} - } - } - }; - - request.Content = new StringContent(JsonConvert.SerializeObject(content)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _fixture.Client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.InternalServerError, errorDocument.Errors[0].StatusCode); - Assert.Equal("An unhandled error occurred while processing this request.", errorDocument.Errors[0].Title); - Assert.Equal("Data being returned must be errors or resources.", errorDocument.Errors[0].Detail); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs new file mode 100644 index 0000000000..4db7d81157 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults +{ + public sealed class ActionResultDbContext : DbContext + { + public DbSet Toothbrushes { get; set; } + + public ActionResultDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs new file mode 100644 index 0000000000..2b0adc0580 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs @@ -0,0 +1,142 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults +{ + public sealed class ActionResultTests + : IClassFixture, ActionResultDbContext>> + { + private readonly IntegrationTestContext, ActionResultDbContext> _testContext; + + public ActionResultTests(IntegrationTestContext, ActionResultDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resource_by_ID() + { + // Arrange + var toothbrush = new Toothbrush(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Toothbrushes.Add(toothbrush); + await dbContext.SaveChangesAsync(); + }); + + var route = "/toothbrushes/" + toothbrush.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Id.Should().Be(toothbrush.StringId); + } + + [Fact] + public async Task Converts_empty_ActionResult_to_error_collection() + { + // Arrange + var route = "/toothbrushes/11111111"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.NotFound); + responseDocument.Errors[0].Title.Should().Be("NotFound"); + responseDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Converts_ActionResult_with_error_object_to_error_collection() + { + // Arrange + var route = "/toothbrushes/22222222"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.NotFound); + responseDocument.Errors[0].Title.Should().Be("No toothbrush with that ID exists."); + responseDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cannot_convert_ActionResult_with_string_parameter_to_error_collection() + { + // Arrange + var route = "/toothbrushes/33333333"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.InternalServerError); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.InternalServerError); + responseDocument.Errors[0].Title.Should().Be("An unhandled error occurred while processing this request."); + responseDocument.Errors[0].Detail.Should().Be("Data being returned must be errors or resources."); + } + + [Fact] + public async Task Converts_ObjectResult_with_error_object_to_error_collection() + { + // Arrange + var route = "/toothbrushes/44444444"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadGateway); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadGateway); + responseDocument.Errors[0].Title.Should().BeNull(); + responseDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Converts_ObjectResult_with_error_objects_to_error_collection() + { + // Arrange + var route = "/toothbrushes/55555555"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.Should().HaveCount(3); + + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.PreconditionFailed); + responseDocument.Errors[0].Title.Should().BeNull(); + responseDocument.Errors[0].Detail.Should().BeNull(); + + responseDocument.Errors[1].StatusCode.Should().Be(HttpStatusCode.Unauthorized); + responseDocument.Errors[1].Title.Should().BeNull(); + responseDocument.Errors[1].Detail.Should().BeNull(); + + responseDocument.Errors[2].StatusCode.Should().Be(HttpStatusCode.ExpectationFailed); + responseDocument.Errors[2].Title.Should().Be("This is not a very great request."); + responseDocument.Errors[2].Detail.Should().BeNull(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/Toothbrush.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/Toothbrush.cs new file mode 100644 index 0000000000..60a2e5a869 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/Toothbrush.cs @@ -0,0 +1,8 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults +{ + public sealed class Toothbrush : Identifiable + { + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs new file mode 100644 index 0000000000..8155d1ba3b --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs @@ -0,0 +1,78 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults +{ + public sealed class ToothbrushesController : BaseToothbrushesController + { + public ToothbrushesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + + [HttpGet("{id}")] + public override Task GetAsync(int id, CancellationToken cancellationToken) + { + return base.GetAsync(id, cancellationToken); + } + } + + public abstract class BaseToothbrushesController : BaseJsonApiController + { + protected BaseToothbrushesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + + public override async Task GetAsync(int id, CancellationToken cancellationToken) + { + if (id == 11111111) + { + return NotFound(); + } + + if (id == 22222222) + { + return NotFound(new Error(HttpStatusCode.NotFound) + { + Title = "No toothbrush with that ID exists." + }); + } + + if (id == 33333333) + { + return Conflict("Something went wrong."); + } + + if (id == 44444444) + { + return Error(new Error(HttpStatusCode.BadGateway)); + } + + if (id == 55555555) + { + var errors = new[] + { + new Error(HttpStatusCode.PreconditionFailed), + new Error(HttpStatusCode.Unauthorized), + new Error(HttpStatusCode.ExpectationFailed) + { + Title = "This is not a very great request." + } + }; + return Error(errors); + } + + return await base.GetAsync(id, cancellationToken); + } + } +} From e12d27f121300b74e64c3db46b278f771f3ca61d Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 2 Feb 2021 12:15:24 +0100 Subject: [PATCH 10/47] Refactored tests for custom routes --- .../Controllers/TodoItemsCustomController.cs | 149 ---------------- .../Extensibility/CustomControllerTests.cs | 159 ------------------ .../ApiControllerAttributeTests.cs | 35 ++++ .../IntegrationTests/CustomRoutes/Civilian.cs | 11 ++ .../CustomRoutes/CiviliansController.cs | 28 +++ .../CustomRoutes/CustomRouteDbContext.cs | 15 ++ .../CustomRoutes/CustomRouteFakers.cs | 23 +++ .../CustomRoutes/CustomRouteTests.cs | 80 +++++++++ .../IntegrationTests/CustomRoutes/Town.cs | 21 +++ .../CustomRoutes/TownsController.cs | 37 ++++ 10 files changed, 250 insertions(+), 308 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Civilian.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CiviliansController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Town.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/TownsController.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs deleted file mode 100644 index 721f126648..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.Errors; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Mvc; - -namespace JsonApiDotNetCoreExample.Controllers -{ - [ApiController] - [DisableRoutingConvention, Route("custom/route/todoItems")] - public class TodoItemsCustomController : CustomJsonApiController - { - public TodoItemsCustomController( - IJsonApiOptions options, - IResourceService resourceService) - : base(options, resourceService) - { } - } - - public class CustomJsonApiController - : CustomJsonApiController where T : class, IIdentifiable - { - public CustomJsonApiController( - IJsonApiOptions options, - IResourceService resourceService) - : base(options, resourceService) - { - } - } - - public class CustomJsonApiController - : ControllerBase where T : class, IIdentifiable - { - private readonly IJsonApiOptions _options; - private readonly IResourceService _resourceService; - - private IActionResult Forbidden() - { - return new StatusCodeResult((int)HttpStatusCode.Forbidden); - } - - public CustomJsonApiController( - IJsonApiOptions options, - IResourceService resourceService) - { - _options = options; - _resourceService = resourceService; - } - - public CustomJsonApiController( - IResourceService resourceService) - { - _resourceService = resourceService; - } - - [HttpGet] - public async Task GetAsync(CancellationToken cancellationToken) - { - var resources = await _resourceService.GetAsync(cancellationToken); - return Ok(resources); - } - - [HttpGet("{id}")] - public async Task GetAsync(TId id, CancellationToken cancellationToken) - { - try - { - var resource = await _resourceService.GetAsync(id, cancellationToken); - return Ok(resource); - } - catch (ResourceNotFoundException) - { - return NotFound(); - } - } - - [HttpGet("{id}/relationships/{relationshipName}")] - public async Task GetRelationshipsAsync(TId id, string relationshipName, CancellationToken cancellationToken) - { - try - { - var relationship = await _resourceService.GetRelationshipAsync(id, relationshipName, cancellationToken); - return Ok(relationship); - } - catch (ResourceNotFoundException) - { - return NotFound(); - } - } - - [HttpGet("{id}/{relationshipName}")] - public async Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) - { - var relationship = await _resourceService.GetSecondaryAsync(id, relationshipName, cancellationToken); - return Ok(relationship); - } - - [HttpPost] - public async Task PostAsync([FromBody] T resource, CancellationToken cancellationToken) - { - if (resource == null) - return UnprocessableEntity(); - - if (_options.AllowClientGeneratedIds && !string.IsNullOrEmpty(resource.StringId)) - return Forbidden(); - - resource = await _resourceService.CreateAsync(resource, cancellationToken); - - return Created($"{HttpContext.Request.Path}/{resource.Id}", resource); - } - - [HttpPatch("{id}")] - public async Task PatchAsync(TId id, [FromBody] T resource, CancellationToken cancellationToken) - { - if (resource == null) - return UnprocessableEntity(); - - try - { - var updated = await _resourceService.UpdateAsync(id, resource, cancellationToken); - return Ok(updated); - } - catch (ResourceNotFoundException) - { - return NotFound(); - } - } - - [HttpPatch("{id}/relationships/{relationshipName}")] - public async Task PatchRelationshipAsync(TId id, string relationshipName, [FromBody] object secondaryResourceIds, CancellationToken cancellationToken) - { - await _resourceService.SetRelationshipAsync(id, relationshipName, secondaryResourceIds, cancellationToken); - - return Ok(); - } - - [HttpDelete("{id}")] - public async Task DeleteAsync(TId id, CancellationToken cancellationToken) - { - await _resourceService.DeleteAsync(id, cancellationToken); - return NoContent(); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs deleted file mode 100644 index 61c467cbdc..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/CustomControllerTests.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Bogus; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Extensibility -{ - [Collection("WebHostCollection")] - public sealed class CustomControllerTests - { - private readonly TestFixture _fixture; - private readonly Faker _todoItemFaker; - private readonly Faker _personFaker; - - public CustomControllerTests(TestFixture fixture) - { - _fixture = fixture; - _todoItemFaker = new Faker() - .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); - _personFaker = new Faker() - .RuleFor(p => p.FirstName, f => f.Name.FirstName()) - .RuleFor(p => p.LastName, f => f.Name.LastName()); - } - - [Fact] - public async Task CustomRouteControllers_Uses_Dasherized_Collection_Route() - { - // Arrange - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod("GET"); - var route = "/custom/route/todoItems"; - - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task CustomRouteControllers_Uses_Dasherized_Item_Route() - { - // Arrange - var context = _fixture.GetRequiredService(); - var todoItem = _todoItemFaker.Generate(); - var person = _personFaker.Generate(); - todoItem.Owner = person; - context.TodoItems.Add(todoItem); - await context.SaveChangesAsync(); - - var builder = WebHost.CreateDefaultBuilder() - .UseStartup(); - var httpMethod = new HttpMethod("GET"); - var route = $"/custom/route/todoItems/{todoItem.Id}"; - - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - [Fact] - public async Task CustomRouteControllers_Creates_Proper_Relationship_Links() - { - // Arrange - var context = _fixture.GetRequiredService(); - var todoItem = _todoItemFaker.Generate(); - var person = _personFaker.Generate(); - todoItem.Owner = person; - context.TodoItems.Add(todoItem); - await context.SaveChangesAsync(); - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var httpMethod = new HttpMethod("GET"); - var route = $"/custom/route/todoItems/{todoItem.Id}"; - - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var body = await response.Content.ReadAsStringAsync(); - var deserializedBody = JsonConvert.DeserializeObject(body); - - var result = deserializedBody["data"]["relationships"]["owner"]["links"]["related"].ToString(); - Assert.EndsWith($"{route}/owner", result); - } - - [Fact] - public async Task ApiController_attribute_transforms_NotFound_action_result_without_arguments_into_ProblemDetails() - { - // Arrange - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var route = "/custom/route/todoItems/99999999"; - - var requestBody = new - { - data = new - { - type = "todoItems", - id = "99999999", - attributes = new Dictionary - { - ["ordinal"] = 1 - } - } - }; - - var content = JsonConvert.SerializeObject(requestBody); - - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Patch, route) {Content = new StringContent(content)}; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - - var responseBody = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(responseBody); - - Assert.Single(errorDocument.Errors); - Assert.Equal("https://tools.ietf.org/html/rfc7231#section-6.5.4", errorDocument.Errors[0].Links.About); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs new file mode 100644 index 0000000000..3a38991118 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs @@ -0,0 +1,35 @@ +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + public sealed class ApiControllerAttributeTests + : IClassFixture, CustomRouteDbContext>> + { + private readonly IntegrationTestContext, CustomRouteDbContext> _testContext; + + public ApiControllerAttributeTests(IntegrationTestContext, CustomRouteDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task ApiController_attribute_transforms_NotFound_action_result_without_arguments_into_ProblemDetails() + { + // Arrange + var route = "/world-civilians/missing"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].Links.About.Should().Be("https://tools.ietf.org/html/rfc7231#section-6.5.4"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Civilian.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Civilian.cs new file mode 100644 index 0000000000..c5f75a2c38 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Civilian.cs @@ -0,0 +1,11 @@ +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + public sealed class Civilian : Identifiable + { + [Attr] + public string Name { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CiviliansController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CiviliansController.cs new file mode 100644 index 0000000000..dd2df9b6d6 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CiviliansController.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + [ApiController] + [DisableRoutingConvention, Route("world-civilians")] + public sealed class CiviliansController : JsonApiController + { + public CiviliansController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + + [HttpGet("missing")] + public async Task GetMissingAsync() + { + await Task.Yield(); + return NotFound(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs new file mode 100644 index 0000000000..4b76077d27 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + public sealed class CustomRouteDbContext : DbContext + { + public DbSet Towns { get; set; } + public DbSet Civilians { get; set; } + + public CustomRouteDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs new file mode 100644 index 0000000000..e57febd9c4 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs @@ -0,0 +1,23 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + internal sealed class CustomRouteFakers : FakerContainer + { + private readonly Lazy> _lazyTownFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(town => town.Name, f => f.Address.City()) + .RuleFor(town => town.Latitude, f => f.Address.Latitude()) + .RuleFor(town => town.Longitude, f => f.Address.Longitude())); + + private readonly Lazy> _lazyCivilianFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(civilian => civilian.Name, f => f.Person.FullName)); + + public Faker Town => _lazyTownFaker.Value; + public Faker Civilian => _lazyCivilianFaker.Value; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs new file mode 100644 index 0000000000..e708cb8b13 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -0,0 +1,80 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + public sealed class CustomRouteTests + : IClassFixture, CustomRouteDbContext>> + { + private readonly IntegrationTestContext, CustomRouteDbContext> _testContext; + private readonly CustomRouteFakers _fakers = new CustomRouteFakers(); + + public CustomRouteTests(IntegrationTestContext, CustomRouteDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public async Task Can_get_resource_at_custom_route() + { + // Arrange + var town = _fakers.Town.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Towns.Add(town); + await dbContext.SaveChangesAsync(); + }); + + var route = "/world-api/civilization/popular/towns/" + town.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Type.Should().Be("towns"); + responseDocument.SingleData.Id.Should().Be(town.StringId); + responseDocument.SingleData.Attributes["name"].Should().Be(town.Name); + responseDocument.SingleData.Attributes["latitude"].Should().Be(town.Latitude); + responseDocument.SingleData.Attributes["longitude"].Should().Be(town.Longitude); + responseDocument.SingleData.Relationships["civilians"].Links.Self.Should().Be($"http://localhost/world-api/civilization/popular/towns/{town.Id}/relationships/civilians"); + responseDocument.SingleData.Relationships["civilians"].Links.Related.Should().Be($"http://localhost/world-api/civilization/popular/towns/{town.Id}/civilians"); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/world-api/civilization/popular/towns/{town.Id}"); + responseDocument.Links.Self.Should().Be($"http://localhost/world-api/civilization/popular/towns/{town.Id}"); + } + + [Fact] + public async Task Can_get_resources_at_custom_action_method() + { + // Arrange + var town = _fakers.Town.Generate(7); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Towns.AddRange(town); + await dbContext.SaveChangesAsync(); + }); + + var route = "/world-api/civilization/popular/towns/largest-5"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.ManyData.Should().HaveCount(5); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Type == "towns"); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Attributes.Any()); + responseDocument.ManyData.Should().OnlyContain(resourceObject => resourceObject.Relationships.Any()); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Town.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Town.cs new file mode 100644 index 0000000000..0f2dc93926 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/Town.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + public sealed class Town : Identifiable + { + [Attr] + public string Name { get; set; } + + [Attr] + public double Latitude { get; set; } + + [Attr] + public double Longitude { get; set; } + + [HasMany] + public ISet Civilians { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/TownsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/TownsController.cs new file mode 100644 index 0000000000..af390bd683 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/TownsController.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes +{ + [DisableRoutingConvention, Route("world-api/civilization/popular/towns")] + public sealed class TownsController : JsonApiController + { + private readonly CustomRouteDbContext _dbContext; + + public TownsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService, CustomRouteDbContext dbContext) + : base(options, loggerFactory, resourceService) + { + _dbContext = dbContext; + } + + [HttpGet("largest-{count}")] + public async Task GetLargestTownsAsync(int count, CancellationToken cancellationToken) + { + var query = _dbContext.Towns + .OrderByDescending(town => town.Civilians.Count) + .Take(count); + + var results = await query.ToListAsync(cancellationToken); + return Ok(results); + } + } +} From ddcd4aca6a7398bfe8eba4257c5916ebcc60908a Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 2 Feb 2021 16:42:58 +0100 Subject: [PATCH 11/47] Refactored tests for links rendering --- .../DocumentTests/LinksWithNamespaceTests.cs | 43 --- .../LinksWithoutNamespaceTests.cs | 99 ----- .../Spec/DocumentTests/Relationships.cs | 159 -------- .../Acceptance/TestFixture.cs | 60 --- .../Links/AbsoluteLinksWithNamespaceTests.cs | 355 ++++++++++++++++++ .../AbsoluteLinksWithoutNamespaceTests.cs | 355 ++++++++++++++++++ .../IntegrationTests/Links/LinksDbContext.cs | 15 + .../IntegrationTests/Links/LinksFakers.cs | 21 ++ .../IntegrationTests/Links/Photo.cs | 18 + .../IntegrationTests/Links/PhotoAlbum.cs | 19 + .../Links/PhotoAlbumsController.cs | 17 + .../Links/PhotosController.cs | 17 + .../Links/RelativeLinksWithNamespaceTests.cs | 304 ++++++++++++--- .../RelativeLinksWithoutNamespaceTests.cs | 355 ++++++++++++++++++ .../ModelStateValidationTests.cs | 1 + .../AbsoluteLinksInApiNamespaceStartup.cs | 24 ++ .../AbsoluteLinksNoNamespaceStartup.cs | 24 ++ .../ModelStateValidationStartup.cs | 6 +- .../RelativeLinksInApiNamespaceStartup.cs | 24 ++ .../RelativeLinksNoNamespaceStartup.cs | 24 ++ .../WebHostCollection.cs | 10 - 21 files changed, 1530 insertions(+), 420 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithNamespaceTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithoutNamespaceTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbumsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotosController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests/ModelStateValidation => Startups}/ModelStateValidationStartup.cs (78%) create mode 100644 test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/WebHostCollection.cs diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithNamespaceTests.cs deleted file mode 100644 index 6b51a357fd..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithNamespaceTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample.Models; -using Newtonsoft.Json; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests -{ - public sealed class LinksWithNamespaceTests : FunctionalTestCollection - { - public LinksWithNamespaceTests(StandardApplicationFactory factory) : base(factory) - { - } - - [Fact] - public async Task GET_RelativeLinks_False_With_Namespace_Returns_AbsoluteLinks() - { - // Arrange - var person = new Person(); - - _dbContext.People.Add(person); - await _dbContext.SaveChangesAsync(); - - var route = "/api/v1/people/" + person.StringId; - var request = new HttpRequestMessage(HttpMethod.Get, route); - - var options = (JsonApiOptions) _factory.GetRequiredService(); - options.UseRelativeLinks = false; - - // Act - var response = await _factory.Client.SendAsync(request); - var responseString = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseString); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("http://localhost/api/v1/people/" + person.StringId, document.Links.Self); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithoutNamespaceTests.cs deleted file mode 100644 index 9670d5003a..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/LinksWithoutNamespaceTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using FluentAssertions; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests -{ - public sealed class LinksWithoutNamespaceTests : IClassFixture> - { - private readonly IntegrationTestContext _testContext; - - public LinksWithoutNamespaceTests(IntegrationTestContext testContext) - { - _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => - { - var part = new AssemblyPart(typeof(EmptyStartup).Assembly); - services.AddMvcCore().ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part)); - }); - } - - [Fact] - public async Task GET_RelativeLinks_True_Without_Namespace_Returns_RelativeLinks() - { - // Arrange - var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); - options.UseRelativeLinks = true; - - var person = new Person(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.People.Add(person); - - await dbContext.SaveChangesAsync(); - }); - - var route = "/people/" + person.StringId; - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - - responseDocument.Links.Self.Should().Be("/people/" + person.StringId); - } - - [Fact] - public async Task GET_RelativeLinks_False_Without_Namespace_Returns_AbsoluteLinks() - { - // Arrange - var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); - options.UseRelativeLinks = false; - - var person = new Person(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.People.Add(person); - - await dbContext.SaveChangesAsync(); - }); - - var route = "/people/" + person.StringId; - - // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); - - // Assert - httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - - responseDocument.Links.Self.Should().Be("http://localhost/people/" + person.StringId); - } - } - - public sealed class NoNamespaceStartup : TestStartup - { - public NoNamespaceStartup(IConfiguration configuration) : base(configuration) - { - } - - protected override void ConfigureJsonApiOptions(JsonApiOptions options) - { - base.ConfigureJsonApiOptions(options); - - options.Namespace = null; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs deleted file mode 100644 index dacac855a8..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Bogus; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests -{ - [Collection("WebHostCollection")] - public sealed class Relationships - { - private readonly AppDbContext _context; - private readonly Faker _todoItemFaker; - private readonly Faker _personFaker; - - public Relationships(TestFixture fixture) - { - _context = fixture.GetRequiredService(); - _todoItemFaker = new Faker() - .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); - _personFaker = new Faker() - .RuleFor(p => p.FirstName, f => f.Name.FirstName()) - .RuleFor(p => p.LastName, f => f.Name.LastName()); - } - - [Fact] - public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships() - { - // Arrange - await _context.ClearTableAsync(); - await _context.SaveChangesAsync(); - - var todoItem = _todoItemFaker.Generate(); - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); - - var httpMethod = new HttpMethod("GET"); - var route = "/api/v1/todoItems"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseString = await response.Content.ReadAsStringAsync(); - var data = JsonConvert.DeserializeObject(responseString).ManyData[0]; - var expectedOwnerSelfLink = $"http://localhost/api/v1/todoItems/{todoItem.Id}/relationships/owner"; - var expectedOwnerRelatedLink = $"http://localhost/api/v1/todoItems/{todoItem.Id}/owner"; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(expectedOwnerSelfLink, data.Relationships["owner"].Links.Self); - Assert.Equal(expectedOwnerRelatedLink, data.Relationships["owner"].Links.Related); - } - - [Fact] - public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships_ById() - { - // Arrange - var todoItem = _todoItemFaker.Generate(); - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); - - var httpMethod = new HttpMethod("GET"); - var route = $"/api/v1/todoItems/{todoItem.Id}"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseString = await response.Content.ReadAsStringAsync(); - var data = JsonConvert.DeserializeObject(responseString).SingleData; - var expectedOwnerSelfLink = $"http://localhost/api/v1/todoItems/{todoItem.Id}/relationships/owner"; - var expectedOwnerRelatedLink = $"http://localhost/api/v1/todoItems/{todoItem.Id}/owner"; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(expectedOwnerSelfLink, data.Relationships["owner"].Links.Self); - Assert.Equal(expectedOwnerRelatedLink, data.Relationships["owner"].Links.Related); - } - - [Fact] - public async Task Correct_RelationshipObjects_For_OneToMany_Relationships() - { - // Arrange - await _context.ClearTableAsync(); - await _context.SaveChangesAsync(); - - var person = _personFaker.Generate(); - _context.People.Add(person); - await _context.SaveChangesAsync(); - - var httpMethod = new HttpMethod("GET"); - var route = "/api/v1/people"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseString = await response.Content.ReadAsStringAsync(); - var data = JsonConvert.DeserializeObject(responseString).ManyData[0]; - var expectedOwnerSelfLink = $"http://localhost/api/v1/people/{person.Id}/relationships/todoItems"; - var expectedOwnerRelatedLink = $"http://localhost/api/v1/people/{person.Id}/todoItems"; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(expectedOwnerSelfLink, data.Relationships["todoItems"].Links.Self); - Assert.Equal(expectedOwnerRelatedLink, data.Relationships["todoItems"].Links.Related); - } - - [Fact] - public async Task Correct_RelationshipObjects_For_OneToMany_Relationships_ById() - { - // Arrange - var person = _personFaker.Generate(); - _context.People.Add(person); - await _context.SaveChangesAsync(); - - var httpMethod = new HttpMethod("GET"); - var route = $"/api/v1/people/{person.Id}"; - - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseString = await response.Content.ReadAsStringAsync(); - var data = JsonConvert.DeserializeObject(responseString).SingleData; - var expectedOwnerSelfLink = $"http://localhost/api/v1/people/{person.Id}/relationships/todoItems"; - var expectedOwnerRelatedLink = $"http://localhost/api/v1/people/{person.Id}/todoItems"; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(expectedOwnerSelfLink, data.Relationships["todoItems"].Links.Self); - Assert.Equal(expectedOwnerRelatedLink, data.Relationships["todoItems"].Links.Related); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs deleted file mode 100644 index 9136abc00c..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using JsonApiDotNetCore.Repositories; -using JsonApiDotNetCoreExample.Data; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - public class TestFixture : IDisposable where TStartup : class - { - private readonly TestServer _server; - public readonly IServiceProvider ServiceProvider; - public TestFixture() - { - var builder = WebHost.CreateDefaultBuilder().UseStartup(); - _server = new TestServer(builder); - ServiceProvider = _server.Host.Services; - - Client = _server.CreateClient(); - Context = GetRequiredService().GetContext() as AppDbContext; - } - - public HttpClient Client { get; set; } - public AppDbContext Context { get; } - - public T GetRequiredService() => (T)ServiceProvider.GetRequiredService(typeof(T)); - - public void AssertEqualStatusCode(HttpStatusCode expected, HttpResponseMessage response) - { - var responseBody = response.Content.ReadAsStringAsync().Result; - Assert.True(expected == response.StatusCode, $"Got {response.StatusCode} status code instead of {expected}. Response body: {responseBody}"); - } - - private bool disposedValue; - - private void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - Client.Dispose(); - _server.Dispose(); - } - - disposedValue = true; - } - } - - public void Dispose() - { - Dispose(true); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs new file mode 100644 index 0000000000..337668586f --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs @@ -0,0 +1,355 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class AbsoluteLinksWithNamespaceTests + : IClassFixture, LinksDbContext>> + { + private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly LinksFakers _fakers = new LinksFakers(); + + public AbsoluteLinksWithNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + { + _testContext = testContext; + + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); + options.IncludeTotalResourceCount = true; + } + + [Fact] + public async Task Get_primary_resource_by_ID_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/api/photoAlbums/" + album.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/photos"); + } + + [Fact] + public async Task Get_primary_resources_with_include_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/api/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be("http://localhost/api/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be("http://localhost/api/photoAlbums?include=photos"); + responseDocument.Links.Last.Should().Be("http://localhost/api/photoAlbums?include=photos"); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}"); + responseDocument.ManyData[0].Relationships["photos"].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.ManyData[0].Relationships["photos"].Links.Related.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_secondary_resource_returns_absolute_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photos/{photo.StringId}/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photos/{photo.StringId}/album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{photo.Album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{photo.Album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/api/photoAlbums/{photo.Album.StringId}/photos"); + } + + [Fact] + public async Task Get_secondary_resources_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photoAlbums/{album.StringId}/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.ManyData[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.ManyData[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/api/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_HasOne_relationship_returns_absolute_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photos/{photo.StringId}/relationships/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photos/{photo.StringId}/relationships/album"); + responseDocument.Links.Related.Should().Be($"http://localhost/api/photos/{photo.StringId}/album"); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Should().BeNull(); + responseDocument.SingleData.Relationships.Should().BeNull(); + } + + [Fact] + public async Task Get_HasMany_relationship_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photoAlbums/{album.StringId}/relationships/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Related.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.First.Should().Be($"http://localhost/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Should().BeNull(); + responseDocument.ManyData[0].Relationships.Should().BeNull(); + } + + [Fact] + public async Task Create_resource_with_side_effects_and_include_returns_absolute_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(existingPhoto); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photoAlbums", + relationships = new + { + photos = new + { + data = new[] + { + new + { + type = "photos", + id = existingPhoto.StringId + } + } + } + } + } + }; + + var route = "/api/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.Links.Self.Should().Be("http://localhost/api/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + var newAlbumId = responseDocument.SingleData.Id; + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/api/photoAlbums/{newAlbumId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{newAlbumId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/api/photoAlbums/{newAlbumId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}/album"); + } + + [Fact] + public async Task Update_resource_with_side_effects_and_include_returns_absolute_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + var existingAlbum = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.AddRange(existingPhoto, existingAlbum); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photos", + id = existingPhoto.StringId, + relationships = new + { + album = new + { + data = new + { + type = "photoAlbums", + id = existingAlbum.StringId + } + } + } + } + }; + + var route = $"/api/photos/{existingPhoto.StringId}?include=album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}?include=album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}"); + responseDocument.SingleData.Relationships["album"].Links.Self.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.SingleData.Relationships["album"].Links.Related.Should().Be($"http://localhost/api/photos/{existingPhoto.StringId}/album"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{existingAlbum.StringId}"); + responseDocument.Included[0].Relationships["photos"].Links.Self.Should().Be($"http://localhost/api/photoAlbums/{existingAlbum.StringId}/relationships/photos"); + responseDocument.Included[0].Relationships["photos"].Links.Related.Should().Be($"http://localhost/api/photoAlbums/{existingAlbum.StringId}/photos"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs new file mode 100644 index 0000000000..ee4091ebc2 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs @@ -0,0 +1,355 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class AbsoluteLinksWithoutNamespaceTests + : IClassFixture, LinksDbContext>> + { + private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly LinksFakers _fakers = new LinksFakers(); + + public AbsoluteLinksWithoutNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + { + _testContext = testContext; + + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); + options.IncludeTotalResourceCount = true; + } + + [Fact] + public async Task Get_primary_resource_by_ID_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/photoAlbums/" + album.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/photoAlbums/{album.StringId}/photos"); + } + + [Fact] + public async Task Get_primary_resources_with_include_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be("http://localhost/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be("http://localhost/photoAlbums?include=photos"); + responseDocument.Links.Last.Should().Be("http://localhost/photoAlbums?include=photos"); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}"); + responseDocument.ManyData[0].Relationships["photos"].Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.ManyData[0].Relationships["photos"].Links.Related.Should().Be($"http://localhost/photoAlbums/{album.StringId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_secondary_resource_returns_absolute_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photos/{photo.StringId}/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photos/{photo.StringId}/album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/photoAlbums/{photo.Album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/photoAlbums/{photo.Album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/photoAlbums/{photo.Album.StringId}/photos"); + } + + [Fact] + public async Task Get_secondary_resources_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photoAlbums/{album.StringId}/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be($"http://localhost/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.ManyData[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.ManyData[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_HasOne_relationship_returns_absolute_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photos/{photo.StringId}/relationships/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photos/{photo.StringId}/relationships/album"); + responseDocument.Links.Related.Should().Be($"http://localhost/photos/{photo.StringId}/album"); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Should().BeNull(); + responseDocument.SingleData.Relationships.Should().BeNull(); + } + + [Fact] + public async Task Get_HasMany_relationship_returns_absolute_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photoAlbums/{album.StringId}/relationships/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Related.Should().Be($"http://localhost/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.First.Should().Be($"http://localhost/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Should().BeNull(); + responseDocument.ManyData[0].Relationships.Should().BeNull(); + } + + [Fact] + public async Task Create_resource_with_side_effects_and_include_returns_absolute_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(existingPhoto); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photoAlbums", + relationships = new + { + photos = new + { + data = new[] + { + new + { + type = "photos", + id = existingPhoto.StringId + } + } + } + } + } + }; + + var route = "/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.Links.Self.Should().Be("http://localhost/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + var newAlbumId = responseDocument.SingleData.Id; + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/photoAlbums/{newAlbumId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"http://localhost/photoAlbums/{newAlbumId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"http://localhost/photoAlbums/{newAlbumId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/photos/{existingPhoto.StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"http://localhost/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"http://localhost/photos/{existingPhoto.StringId}/album"); + } + + [Fact] + public async Task Update_resource_with_side_effects_and_include_returns_absolute_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + var existingAlbum = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.AddRange(existingPhoto, existingAlbum); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photos", + id = existingPhoto.StringId, + relationships = new + { + album = new + { + data = new + { + type = "photoAlbums", + id = existingAlbum.StringId + } + } + } + } + }; + + var route = $"/photos/{existingPhoto.StringId}?include=album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"http://localhost/photos/{existingPhoto.StringId}?include=album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"http://localhost/photos/{existingPhoto.StringId}"); + responseDocument.SingleData.Relationships["album"].Links.Self.Should().Be($"http://localhost/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.SingleData.Relationships["album"].Links.Related.Should().Be($"http://localhost/photos/{existingPhoto.StringId}/album"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"http://localhost/photoAlbums/{existingAlbum.StringId}"); + responseDocument.Included[0].Relationships["photos"].Links.Self.Should().Be($"http://localhost/photoAlbums/{existingAlbum.StringId}/relationships/photos"); + responseDocument.Included[0].Relationships["photos"].Links.Related.Should().Be($"http://localhost/photoAlbums/{existingAlbum.StringId}/photos"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs new file mode 100644 index 0000000000..bb34911642 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class LinksDbContext : DbContext + { + public DbSet PhotoAlbums { get; set; } + public DbSet Photos { get; set; } + + public LinksDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs new file mode 100644 index 0000000000..852c825b3c --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs @@ -0,0 +1,21 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + internal sealed class LinksFakers : FakerContainer + { + private readonly Lazy> _lazyPhotoAlbumFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(photoAlbum => photoAlbum.Name, f => f.Lorem.Sentence())); + + private readonly Lazy> _lazyPhotoFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(photo => photo.Url, f => f.Image.PlaceImgUrl())); + + public Faker PhotoAlbum => _lazyPhotoAlbumFaker.Value; + public Faker Photo => _lazyPhotoFaker.Value; + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs new file mode 100644 index 0000000000..da964b4392 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs @@ -0,0 +1,18 @@ +using System; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class Photo : Identifiable + { + [Attr] + public string Url { get; set; } + + [Attr] + public Guid ConcurrencyToken => Guid.NewGuid(); + + [HasOne] + public PhotoAlbum Album { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs new file mode 100644 index 0000000000..17087913d1 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class PhotoAlbum : Identifiable + { + [Attr] + public string Name { get; set; } + + [Attr] + public Guid ConcurrencyToken => Guid.NewGuid(); + + [HasMany] + public ISet Photos { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbumsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbumsController.cs new file mode 100644 index 0000000000..8d13f66b99 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbumsController.cs @@ -0,0 +1,17 @@ +using System; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class PhotoAlbumsController : JsonApiController + { + public PhotoAlbumsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotosController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotosController.cs new file mode 100644 index 0000000000..e0dcb9a316 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotosController.cs @@ -0,0 +1,17 @@ +using System; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class PhotosController : JsonApiController + { + public PhotosController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs index 7d6b023bef..9dcc0009d4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs @@ -1,47 +1,42 @@ -using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { public sealed class RelativeLinksWithNamespaceTests - : IClassFixture> + : IClassFixture, LinksDbContext>> { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly LinksFakers _fakers = new LinksFakers(); - public RelativeLinksWithNamespaceTests(IntegrationTestContext testContext) + public RelativeLinksWithNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) { _testContext = testContext; var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); - options.Namespace = "api/v1"; - options.UseRelativeLinks = true; - options.DefaultPageSize = new PageSize(10); options.IncludeTotalResourceCount = true; } [Fact] - public async Task Get_primary_resource_by_ID_returns_links() + public async Task Get_primary_resource_by_ID_returns_relative_links() { // Arrange - var person = new Person(); + var album = _fakers.PhotoAlbum.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.People.Add(person); + dbContext.PhotoAlbums.Add(album); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/people/" + person.StringId; + var route = "/api/photoAlbums/" + album.StringId; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -49,7 +44,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - responseDocument.Links.Self.Should().Be($"/api/v1/people/{person.StringId}"); + responseDocument.Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}"); responseDocument.Links.Related.Should().BeNull(); responseDocument.Links.First.Should().BeNull(); responseDocument.Links.Last.Should().BeNull(); @@ -57,32 +52,99 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.Next.Should().BeNull(); responseDocument.SingleData.Should().NotBeNull(); - responseDocument.SingleData.Links.Self.Should().Be($"/api/v1/people/{person.StringId}"); + responseDocument.SingleData.Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/api/photoAlbums/{album.StringId}/photos"); + } + + [Fact] + public async Task Get_primary_resources_with_include_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/api/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); - responseDocument.SingleData.Relationships["todoItems"].Links.Self.Should().Be($"/api/v1/people/{person.StringId}/relationships/todoItems"); - responseDocument.SingleData.Relationships["todoItems"].Links.Related.Should().Be($"/api/v1/people/{person.StringId}/todoItems"); + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be("/api/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be("/api/photoAlbums?include=photos"); + responseDocument.Links.Last.Should().Be("/api/photoAlbums?include=photos"); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}"); + responseDocument.ManyData[0].Relationships["photos"].Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.ManyData[0].Relationships["photos"].Links.Related.Should().Be($"/api/photoAlbums/{album.StringId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}/album"); } [Fact] - public async Task Get_primary_resources_with_include_returns_links() + public async Task Get_secondary_resource_returns_relative_links() { // Arrange - var person = new Person + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => { - TodoItems = new HashSet - { - new TodoItem() - } - }; + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photos/{photo.StringId}/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/api/photos/{photo.StringId}/album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/api/photoAlbums/{photo.Album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/api/photoAlbums/{photo.Album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/api/photoAlbums/{photo.Album.StringId}/photos"); + } + + [Fact] + public async Task Get_secondary_resources_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); - dbContext.People.Add(person); + dbContext.PhotoAlbums.Add(album); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/people?include=todoItems"; + var route = $"/api/photoAlbums/{album.StringId}/photos"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -90,56 +152,204 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - responseDocument.Links.Self.Should().Be("/api/v1/people?include=todoItems"); + responseDocument.Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}/photos"); responseDocument.Links.Related.Should().BeNull(); - responseDocument.Links.First.Should().Be("/api/v1/people?include=todoItems"); - responseDocument.Links.Last.Should().Be("/api/v1/people?include=todoItems"); + responseDocument.Links.First.Should().Be($"/api/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().BeNull(); responseDocument.Links.Next.Should().BeNull(); responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Links.Self.Should().Be($"/api/v1/people/{person.StringId}"); + responseDocument.ManyData[0].Links.Self.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.ManyData[0].Relationships["album"].Links.Self.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.ManyData[0].Relationships["album"].Links.Related.Should().Be($"/api/photos/{album.Photos.ElementAt(0).StringId}/album"); + } - responseDocument.Included.Should().HaveCount(1); - responseDocument.Included[0].Links.Self.Should().Be($"/api/v1/todoItems/{person.TodoItems.ElementAt(0).StringId}"); + [Fact] + public async Task Get_HasOne_relationship_returns_relative_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photos/{photo.StringId}/relationships/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/api/photos/{photo.StringId}/relationships/album"); + responseDocument.Links.Related.Should().Be($"/api/photos/{photo.StringId}/album"); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Should().BeNull(); + responseDocument.SingleData.Relationships.Should().BeNull(); } [Fact] - public async Task Get_HasMany_relationship_returns_links() + public async Task Get_HasMany_relationship_returns_relative_links() { // Arrange - var person = new Person + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/api/photoAlbums/{album.StringId}/relationships/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Related.Should().Be($"/api/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.First.Should().Be($"/api/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Should().BeNull(); + responseDocument.ManyData[0].Relationships.Should().BeNull(); + } + + [Fact] + public async Task Create_resource_with_side_effects_and_include_returns_relative_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(existingPhoto); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new { - TodoItems = new HashSet + data = new { - new TodoItem() + type = "photoAlbums", + relationships = new + { + photos = new + { + data = new[] + { + new + { + type = "photos", + id = existingPhoto.StringId + } + } + } + } } }; + var route = "/api/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.Links.Self.Should().Be("/api/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + var newAlbumId = responseDocument.SingleData.Id; + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/api/photoAlbums/{newAlbumId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/api/photoAlbums/{newAlbumId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/api/photoAlbums/{newAlbumId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/api/photos/{existingPhoto.StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"/api/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"/api/photos/{existingPhoto.StringId}/album"); + } + + [Fact] + public async Task Update_resource_with_side_effects_and_include_returns_relative_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + var existingAlbum = _fakers.PhotoAlbum.Generate(); + await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); - dbContext.People.Add(person); + dbContext.AddRange(existingPhoto, existingAlbum); await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/people/{person.StringId}/relationships/todoItems"; + var requestBody = new + { + data = new + { + type = "photos", + id = existingPhoto.StringId, + relationships = new + { + album = new + { + data = new + { + type = "photoAlbums", + id = existingAlbum.StringId + } + } + } + } + }; + + var route = $"/api/photos/{existingPhoto.StringId}?include=album"; // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - responseDocument.Links.Self.Should().Be($"/api/v1/people/{person.StringId}/relationships/todoItems"); - responseDocument.Links.Related.Should().Be($"/api/v1/people/{person.StringId}/todoItems"); - responseDocument.Links.First.Should().Be($"/api/v1/people/{person.StringId}/relationships/todoItems"); + responseDocument.Links.Self.Should().Be($"/api/photos/{existingPhoto.StringId}?include=album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().BeNull(); responseDocument.Links.Next.Should().BeNull(); - responseDocument.ManyData.Should().HaveCount(1); - responseDocument.ManyData[0].Links.Should().BeNull(); + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/api/photos/{existingPhoto.StringId}"); + responseDocument.SingleData.Relationships["album"].Links.Self.Should().Be($"/api/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.SingleData.Relationships["album"].Links.Related.Should().Be($"/api/photos/{existingPhoto.StringId}/album"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/api/photoAlbums/{existingAlbum.StringId}"); + responseDocument.Included[0].Relationships["photos"].Links.Self.Should().Be($"/api/photoAlbums/{existingAlbum.StringId}/relationships/photos"); + responseDocument.Included[0].Relationships["photos"].Links.Related.Should().Be($"/api/photoAlbums/{existingAlbum.StringId}/photos"); } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs new file mode 100644 index 0000000000..d731a0948a --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs @@ -0,0 +1,355 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links +{ + public sealed class RelativeLinksWithoutNamespaceTests + : IClassFixture, LinksDbContext>> + { + private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly LinksFakers _fakers = new LinksFakers(); + + public RelativeLinksWithoutNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + { + _testContext = testContext; + + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); + options.IncludeTotalResourceCount = true; + } + + [Fact] + public async Task Get_primary_resource_by_ID_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/photoAlbums/" + album.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photoAlbums/{album.StringId}"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/photoAlbums/{album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/photoAlbums/{album.StringId}/photos"); + } + + [Fact] + public async Task Get_primary_resources_with_include_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = "/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be("/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be("/photoAlbums?include=photos"); + responseDocument.Links.Last.Should().Be("/photoAlbums?include=photos"); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"/photoAlbums/{album.StringId}"); + responseDocument.ManyData[0].Relationships["photos"].Links.Self.Should().Be($"/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.ManyData[0].Relationships["photos"].Links.Related.Should().Be($"/photoAlbums/{album.StringId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_secondary_resource_returns_relative_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photos/{photo.StringId}/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photos/{photo.StringId}/album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/photoAlbums/{photo.Album.StringId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/photoAlbums/{photo.Album.StringId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/photoAlbums/{photo.Album.StringId}/photos"); + } + + [Fact] + public async Task Get_secondary_resources_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photoAlbums/{album.StringId}/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().Be($"/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Self.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}"); + responseDocument.ManyData[0].Relationships["album"].Links.Self.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}/relationships/album"); + responseDocument.ManyData[0].Relationships["album"].Links.Related.Should().Be($"/photos/{album.Photos.ElementAt(0).StringId}/album"); + } + + [Fact] + public async Task Get_HasOne_relationship_returns_relative_links() + { + // Arrange + var photo = _fakers.Photo.Generate(); + photo.Album = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(photo); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photos/{photo.StringId}/relationships/album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photos/{photo.StringId}/relationships/album"); + responseDocument.Links.Related.Should().Be($"/photos/{photo.StringId}/album"); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Should().BeNull(); + responseDocument.SingleData.Relationships.Should().BeNull(); + } + + [Fact] + public async Task Get_HasMany_relationship_returns_relative_links() + { + // Arrange + var album = _fakers.PhotoAlbum.Generate(); + album.Photos = _fakers.Photo.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.PhotoAlbums.Add(album); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/photoAlbums/{album.StringId}/relationships/photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Related.Should().Be($"/photoAlbums/{album.StringId}/photos"); + responseDocument.Links.First.Should().Be($"/photoAlbums/{album.StringId}/relationships/photos"); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Links.Should().BeNull(); + responseDocument.ManyData[0].Relationships.Should().BeNull(); + } + + [Fact] + public async Task Create_resource_with_side_effects_and_include_returns_relative_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Photos.Add(existingPhoto); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photoAlbums", + relationships = new + { + photos = new + { + data = new[] + { + new + { + type = "photos", + id = existingPhoto.StringId + } + } + } + } + } + }; + + var route = "/photoAlbums?include=photos"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.Links.Self.Should().Be("/photoAlbums?include=photos"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + var newAlbumId = responseDocument.SingleData.Id; + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/photoAlbums/{newAlbumId}"); + responseDocument.SingleData.Relationships["photos"].Links.Self.Should().Be($"/photoAlbums/{newAlbumId}/relationships/photos"); + responseDocument.SingleData.Relationships["photos"].Links.Related.Should().Be($"/photoAlbums/{newAlbumId}/photos"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/photos/{existingPhoto.StringId}"); + responseDocument.Included[0].Relationships["album"].Links.Self.Should().Be($"/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.Included[0].Relationships["album"].Links.Related.Should().Be($"/photos/{existingPhoto.StringId}/album"); + } + + [Fact] + public async Task Update_resource_with_side_effects_and_include_returns_relative_links() + { + // Arrange + var existingPhoto = _fakers.Photo.Generate(); + var existingAlbum = _fakers.PhotoAlbum.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.AddRange(existingPhoto, existingAlbum); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "photos", + id = existingPhoto.StringId, + relationships = new + { + album = new + { + data = new + { + type = "photoAlbums", + id = existingAlbum.StringId + } + } + } + } + }; + + var route = $"/photos/{existingPhoto.StringId}?include=album"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Links.Self.Should().Be($"/photos/{existingPhoto.StringId}?include=album"); + responseDocument.Links.Related.Should().BeNull(); + responseDocument.Links.First.Should().BeNull(); + responseDocument.Links.Last.Should().BeNull(); + responseDocument.Links.Prev.Should().BeNull(); + responseDocument.Links.Next.Should().BeNull(); + + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Links.Self.Should().Be($"/photos/{existingPhoto.StringId}"); + responseDocument.SingleData.Relationships["album"].Links.Self.Should().Be($"/photos/{existingPhoto.StringId}/relationships/album"); + responseDocument.SingleData.Relationships["album"].Links.Related.Should().Be($"/photos/{existingPhoto.StringId}/album"); + + responseDocument.Included.Should().HaveCount(1); + responseDocument.Included[0].Links.Self.Should().Be($"/photoAlbums/{existingAlbum.StringId}"); + responseDocument.Included[0].Relationships["photos"].Links.Self.Should().Be($"/photoAlbums/{existingAlbum.StringId}/relationships/photos"); + responseDocument.Included[0].Relationships["photos"].Links.Related.Should().Be($"/photoAlbums/{existingAlbum.StringId}/photos"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index 326e348ea9..5ceb8d18ac 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs new file mode 100644 index 0000000000..1f71c8b9e5 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs @@ -0,0 +1,24 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.IntegrationTests; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace JsonApiDotNetCoreExampleTests.Startups +{ + public sealed class AbsoluteLinksInApiNamespaceStartup : TestableStartup + where TDbContext : DbContext + { + public AbsoluteLinksInApiNamespaceStartup(IConfiguration configuration) + : base(configuration) + { + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.Namespace = "api"; + options.UseRelativeLinks = false; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs new file mode 100644 index 0000000000..19b76f9146 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs @@ -0,0 +1,24 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.IntegrationTests; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace JsonApiDotNetCoreExampleTests.Startups +{ + public sealed class AbsoluteLinksNoNamespaceStartup : TestableStartup + where TDbContext : DbContext + { + public AbsoluteLinksNoNamespaceStartup(IConfiguration configuration) + : base(configuration) + { + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.Namespace = null; + options.UseRelativeLinks = false; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs similarity index 78% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationStartup.cs rename to test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs index 71c15c3234..8114edfb1c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs @@ -1,13 +1,15 @@ using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation +namespace JsonApiDotNetCoreExampleTests.Startups { public sealed class ModelStateValidationStartup : TestableStartup where TDbContext : DbContext { - public ModelStateValidationStartup(IConfiguration configuration) : base(configuration) + public ModelStateValidationStartup(IConfiguration configuration) + : base(configuration) { } diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs new file mode 100644 index 0000000000..c38601a58e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs @@ -0,0 +1,24 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.IntegrationTests; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace JsonApiDotNetCoreExampleTests.Startups +{ + public sealed class RelativeLinksInApiNamespaceStartup : TestableStartup + where TDbContext : DbContext + { + public RelativeLinksInApiNamespaceStartup(IConfiguration configuration) + : base(configuration) + { + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.Namespace = "api"; + options.UseRelativeLinks = true; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs new file mode 100644 index 0000000000..b07f3d56c6 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs @@ -0,0 +1,24 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.IntegrationTests; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace JsonApiDotNetCoreExampleTests.Startups +{ + public sealed class RelativeLinksNoNamespaceStartup : TestableStartup + where TDbContext : DbContext + { + public RelativeLinksNoNamespaceStartup(IConfiguration configuration) + : base(configuration) + { + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.Namespace = null; + options.UseRelativeLinks = true; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/WebHostCollection.cs b/test/JsonApiDotNetCoreExampleTests/WebHostCollection.cs deleted file mode 100644 index c50654c30d..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/WebHostCollection.cs +++ /dev/null @@ -1,10 +0,0 @@ -using JsonApiDotNetCoreExample; -using JsonApiDotNetCoreExampleTests.Acceptance; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests -{ - [CollectionDefinition("WebHostCollection")] - public class WebHostCollection : ICollectionFixture> - { } -} From 8b443d89f0a683c6b8c4920dd923c019755ee2e4 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 2 Feb 2021 17:17:16 +0100 Subject: [PATCH 12/47] Refactored tests for exception handling in serializer --- .../ThrowingResourcesController.cs | 18 -------- .../Data/AppDbContext.cs | 2 - .../Models/ThrowingResource.cs | 30 ------------ .../Acceptance/Spec/ThrowingResourceTests.cs | 46 ------------------- .../ExceptionHandling/ErrorDbContext.cs | 1 + .../ExceptionHandlerTests.cs | 40 ++++++++++++++++ .../ExceptionHandling/ThrowingArticle.cs | 14 ++++++ .../ThrowingArticlesController.cs | 16 +++++++ 8 files changed, 71 insertions(+), 96 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/ThrowingResourcesController.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/ThrowingResource.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ThrowingResourceTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/ThrowingResourcesController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/ThrowingResourcesController.cs deleted file mode 100644 index 8b662cde09..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/ThrowingResourcesController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreExample.Controllers -{ - public sealed class ThrowingResourcesController : JsonApiController - { - public ThrowingResourcesController( - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, loggerFactory, resourceService) - { } - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 198169edcb..41e21feecc 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -28,8 +28,6 @@ public AppDbContext(DbContextOptions options, ISystemClock systemC protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity(); - modelBuilder.Entity().HasBaseType(); modelBuilder.Entity() diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/ThrowingResource.cs b/src/Examples/JsonApiDotNetCoreExample/Models/ThrowingResource.cs deleted file mode 100644 index cf2f963e2a..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Models/ThrowingResource.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; -using JsonApiDotNetCore.Serialization; - -namespace JsonApiDotNetCoreExample.Models -{ - public sealed class ThrowingResource : Identifiable - { - [Attr] - public string FailsOnSerialize - { - get - { - var isSerializingResponse = new StackTrace().GetFrames() - .Any(frame => frame.GetMethod().DeclaringType == typeof(JsonApiWriter)); - - if (isSerializingResponse) - { - throw new InvalidOperationException($"The value for the '{nameof(FailsOnSerialize)}' property is currently unavailable."); - } - - return string.Empty; - } - set { } - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ThrowingResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ThrowingResourceTests.cs deleted file mode 100644 index d597adc62c..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ThrowingResourceTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec -{ - public class ThrowingResourceTests : FunctionalTestCollection - { - public ThrowingResourceTests(StandardApplicationFactory factory) : base(factory) - { - } - - [Fact] - public async Task GetThrowingResource_Fails() - { - // Arrange - var throwingResource = new ThrowingResource(); - _dbContext.Add(throwingResource); - await _dbContext.SaveChangesAsync(); - - // Act - var (body, response) = await Get($"/api/v1/throwingResources/{throwingResource.Id}"); - - // Assert - AssertEqualStatusCode(HttpStatusCode.InternalServerError, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.InternalServerError, errorDocument.Errors[0].StatusCode); - Assert.Equal("An unhandled error occurred while processing this request.", errorDocument.Errors[0].Title); - Assert.Equal("Exception has been thrown by the target of an invocation.", errorDocument.Errors[0].Detail); - - var stackTraceLines = - ((JArray) errorDocument.Errors[0].Meta.Data["stackTrace"]).Select(token => token.Value()); - - Assert.Contains(stackTraceLines, line => line.Contains( - "System.InvalidOperationException: The value for the 'FailsOnSerialize' property is currently unavailable.")); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs index 2e9f0cc3e8..9e4d0feab8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling public sealed class ErrorDbContext : DbContext { public DbSet ConsumerArticles { get; set; } + public DbSet ThrowingArticles { get; set; } public ErrorDbContext(DbContextOptions options) : base(options) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs index fca27f6857..1c96c52dcd 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling @@ -79,7 +80,46 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Errors[0].Meta.Data["support"].Should().Be("Please contact us for info about similar articles at company@email.com."); loggerFactory.Logger.Messages.Should().HaveCount(1); + loggerFactory.Logger.Messages.Single().LogLevel.Should().Be(LogLevel.Warning); loggerFactory.Logger.Messages.Single().Text.Should().Contain("Article with code 'X123' is no longer available."); } + + [Fact] + public async Task Logs_and_produces_error_response_on_serialization_failure() + { + // Arrange + var loggerFactory = _testContext.Factory.Services.GetRequiredService(); + loggerFactory.Logger.Clear(); + + var throwingArticle = new ThrowingArticle(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ThrowingArticles.Add(throwingArticle); + await dbContext.SaveChangesAsync(); + }); + + var route = "/throwingArticles/" + throwingArticle.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.InternalServerError); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.InternalServerError); + responseDocument.Errors[0].Title.Should().Be("An unhandled error occurred while processing this request."); + responseDocument.Errors[0].Detail.Should().Be("Exception has been thrown by the target of an invocation."); + + var stackTraceLines = + ((JArray) responseDocument.Errors[0].Meta.Data["stackTrace"]).Select(token => token.Value()); + + stackTraceLines.Should().ContainMatch("* System.InvalidOperationException: Article status could not be determined.*"); + + loggerFactory.Logger.Messages.Should().HaveCount(1); + loggerFactory.Logger.Messages.Single().LogLevel.Should().Be(LogLevel.Error); + loggerFactory.Logger.Messages.Single().Text.Should().Contain("Exception has been thrown by the target of an invocation."); + } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs new file mode 100644 index 0000000000..af4f7890ac --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs @@ -0,0 +1,14 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ThrowingArticle : Identifiable + { + [Attr] + [NotMapped] + public string Status => throw new InvalidOperationException("Article status could not be determined."); + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs new file mode 100644 index 0000000000..6616498f85 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs @@ -0,0 +1,16 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling +{ + public sealed class ThrowingArticlesController : JsonApiController + { + public ThrowingArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} From 64e09db76d6a34589e0b884d848bcd1f5a58ce89 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 2 Feb 2021 23:07:31 +0100 Subject: [PATCH 13/47] Refactored tests for serialization --- .../Acceptance/SerializationTests.cs | 122 ---- .../Helpers/Extensions/StringExtensions.cs | 10 - .../Links/AbsoluteLinksWithNamespaceTests.cs | 6 + .../AbsoluteLinksWithoutNamespaceTests.cs | 6 + .../IntegrationTests/Links/Photo.cs | 3 - .../IntegrationTests/Links/PhotoAlbum.cs | 3 - .../Links/RelativeLinksWithNamespaceTests.cs | 6 + .../RelativeLinksWithoutNamespaceTests.cs | 6 + .../Meta/ResponseMetaTests.cs | 10 +- .../ObjectAssertionsExtensions.cs | 22 + .../IntegrationTests/Serialization/Meeting.cs | 42 ++ .../Serialization/MeetingAttendee.cs | 15 + .../MeetingAttendeesController.cs | 17 + .../Serialization/MeetingLocation.cs | 13 + .../Serialization/MeetingsController.cs | 17 + .../Serialization/SerializationDbContext.cs | 15 + .../Serialization/SerializationFakers.cs | 41 ++ .../Serialization/SerializationTests.cs | 555 ++++++++++++++++++ .../NeverSameResourceChangeTracker.cs | 28 + 19 files changed, 792 insertions(+), 145 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/SerializationTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/StringExtensions.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/Meeting.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendee.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendeesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingLocation.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationDbContext.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/SerializationTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/SerializationTests.cs deleted file mode 100644 index 214f8adbda..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/SerializationTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.Acceptance.Spec; -using JsonApiDotNetCoreExampleTests.Helpers.Extensions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - public sealed class SerializationTests : FunctionalTestCollection - { - public SerializationTests(StandardApplicationFactory factory) - : base(factory) - { - } - - [Fact] - public async Task When_getting_person_it_must_match_JSON_text() - { - // Arrange - var person = new Person - { - Id = 123, - FirstName = "John", - LastName = "Doe", - Age = 57, - Gender = Gender.Male, - Category = "Family" - }; - - await _dbContext.ClearTableAsync(); - _dbContext.People.Add(person); - await _dbContext.SaveChangesAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/people/" + person.Id); - - // Act - var response = await _factory.Client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var bodyText = await response.Content.ReadAsStringAsync(); - var json = JsonConvert.DeserializeObject(bodyText).ToString(); - - var expected = @"{ - ""links"": { - ""self"": ""http://localhost/api/v1/people/123"" - }, - ""data"": { - ""type"": ""people"", - ""id"": ""123"", - ""attributes"": { - ""firstName"": ""John"", - ""initials"": ""J"", - ""lastName"": ""Doe"", - ""the-Age"": 57, - ""gender"": ""Male"", - ""category"": ""Family"" - }, - ""relationships"": { - ""todoItems"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/todoItems"", - ""related"": ""http://localhost/api/v1/people/123/todoItems"" - } - }, - ""assignedTodoItems"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/assignedTodoItems"", - ""related"": ""http://localhost/api/v1/people/123/assignedTodoItems"" - } - }, - ""todoCollections"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/todoCollections"", - ""related"": ""http://localhost/api/v1/people/123/todoCollections"" - } - }, - ""role"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/role"", - ""related"": ""http://localhost/api/v1/people/123/role"" - } - }, - ""oneToOneTodoItem"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/oneToOneTodoItem"", - ""related"": ""http://localhost/api/v1/people/123/oneToOneTodoItem"" - } - }, - ""stakeHolderTodoItem"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/stakeHolderTodoItem"", - ""related"": ""http://localhost/api/v1/people/123/stakeHolderTodoItem"" - } - }, - ""unIncludeableItem"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/unIncludeableItem"", - ""related"": ""http://localhost/api/v1/people/123/unIncludeableItem"" - } - }, - ""passport"": { - ""links"": { - ""self"": ""http://localhost/api/v1/people/123/relationships/passport"", - ""related"": ""http://localhost/api/v1/people/123/passport"" - } - } - }, - ""links"": { - ""self"": ""http://localhost/api/v1/people/123"" - } - } -}"; - Assert.Equal(expected.NormalizeLineEndings(), json.NormalizeLineEndings()); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/StringExtensions.cs b/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/StringExtensions.cs deleted file mode 100644 index 8cccc0c8e2..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Helpers/Extensions/StringExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JsonApiDotNetCoreExampleTests.Helpers.Extensions -{ - public static class StringExtensions - { - public static string NormalizeLineEndings(this string text) - { - return text.Replace("\r\n", "\n").Replace("\r", "\n"); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs index 337668586f..692bc950df 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +21,11 @@ public AbsoluteLinksWithNamespaceTests(IntegrationTestContext + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs index ee4091ebc2..56949c9b99 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +21,11 @@ public AbsoluteLinksWithoutNamespaceTests(IntegrationTestContext + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs index da964b4392..8af6915e1d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs @@ -9,9 +9,6 @@ public sealed class Photo : Identifiable [Attr] public string Url { get; set; } - [Attr] - public Guid ConcurrencyToken => Guid.NewGuid(); - [HasOne] public PhotoAlbum Album { get; set; } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs index 17087913d1..d5a6b448e3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoAlbum.cs @@ -10,9 +10,6 @@ public sealed class PhotoAlbum : Identifiable [Attr] public string Name { get; set; } - [Attr] - public Guid ConcurrencyToken => Guid.NewGuid(); - [HasMany] public ISet Photos { get; set; } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs index 9dcc0009d4..e22eef9ce1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +21,11 @@ public RelativeLinksWithNamespaceTests(IntegrationTestContext + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs index d731a0948a..e80bc9b17b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +21,11 @@ public RelativeLinksWithoutNamespaceTests(IntegrationTestContext + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs index 353ae641de..73991d509f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs @@ -7,9 +7,7 @@ using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.Helpers.Extensions; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json.Linq; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta @@ -40,12 +38,12 @@ public async Task Registered_IResponseMeta_Adds_TopLevel_Meta() var route = "/api/v1/people"; // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - var expected = @"{ + responseDocument.Should().BeJson(@"{ ""meta"": { ""license"": ""MIT"", ""projectUrl"": ""https://github.com/json-api-dotnet/JsonApiDotNetCore/"", @@ -61,9 +59,7 @@ public async Task Registered_IResponseMeta_Adds_TopLevel_Meta() ""first"": ""http://localhost/api/v1/people"" }, ""data"": [] -}"; - - responseDocument.ToString().NormalizeLineEndings().Should().Be(expected.NormalizeLineEndings()); +}"); } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs index d342daf992..e5a6c4e1e1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs @@ -1,11 +1,18 @@ using System; using FluentAssertions; using FluentAssertions.Primitives; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace JsonApiDotNetCoreExampleTests.IntegrationTests { public static class ObjectAssertionsExtensions { + private static readonly JsonSerializerSettings _deserializationSettings = new JsonSerializerSettings + { + Formatting = Formatting.Indented + }; + /// /// Used to assert on a (nullable) or property, /// whose value is returned as in JSON:API response body @@ -29,5 +36,20 @@ public static void BeCloseTo(this ObjectAssertions source, DateTimeOffset? expec value.Should().BeCloseTo(expected.Value, because: because, becauseArgs: becauseArgs); } } + + /// + /// Used to assert on a JSON-formatted string, ignoring differences in insignificant whitespace and line endings. + /// + public static void BeJson(this StringAssertions source, string expected, string because = "", + params object[] becauseArgs) + { + var sourceToken = JsonConvert.DeserializeObject(source.Subject, _deserializationSettings); + var expectedToken = JsonConvert.DeserializeObject(expected, _deserializationSettings); + + string sourceText = sourceToken?.ToString(); + string expectedText = expectedToken?.ToString(); + + sourceText.Should().Be(expectedText, because, becauseArgs); + } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/Meeting.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/Meeting.cs new file mode 100644 index 0000000000..6a3092004f --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/Meeting.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class Meeting : Identifiable + { + [Attr] + public string Title { get; set; } + + [Attr] + public DateTimeOffset StartTime { get; set; } + + [Attr] + public TimeSpan Duration { get; set; } + + [Attr] + [NotMapped] + public MeetingLocation Location + { + get => new MeetingLocation + { + Latitude = Latitude, + Longitude = Longitude + }; + set + { + Latitude = value?.Latitude ?? double.NaN; + Longitude = value?.Longitude ?? double.NaN; + } + } + + public double Latitude { get; set; } + public double Longitude { get; set; } + + [HasMany] + public IList Attendees { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendee.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendee.cs new file mode 100644 index 0000000000..dd79ed029b --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendee.cs @@ -0,0 +1,15 @@ +using System; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class MeetingAttendee : Identifiable + { + [Attr] + public string DisplayName { get; set; } + + [HasOne] + public Meeting Meeting { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendeesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendeesController.cs new file mode 100644 index 0000000000..c1595a0109 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingAttendeesController.cs @@ -0,0 +1,17 @@ +using System; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class MeetingAttendeesController : JsonApiController + { + public MeetingAttendeesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingLocation.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingLocation.cs new file mode 100644 index 0000000000..55cc6be6be --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingLocation.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class MeetingLocation + { + [JsonProperty("lat")] + public double Latitude { get; set; } + + [JsonProperty("lng")] + public double Longitude { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingsController.cs new file mode 100644 index 0000000000..2e1cffbaf2 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/MeetingsController.cs @@ -0,0 +1,17 @@ +using System; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class MeetingsController : JsonApiController + { + public MeetingsController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationDbContext.cs new file mode 100644 index 0000000000..730ea37d42 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class SerializationDbContext : DbContext + { + public DbSet Meetings { get; set; } + public DbSet Attendees { get; set; } + + public SerializationDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs new file mode 100644 index 0000000000..f884e23089 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs @@ -0,0 +1,41 @@ +using System; +using Bogus; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + internal sealed class SerializationFakers : FakerContainer + { + private static readonly TimeSpan[] _meetingDurations = + { + TimeSpan.FromMinutes(15), + TimeSpan.FromMinutes(30), + TimeSpan.FromMinutes(45), + TimeSpan.FromMinutes(60) + }; + + private readonly Lazy> _lazyMeetingFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(meeting => meeting.Title, f => f.Lorem.Word()) + .RuleFor(meeting => meeting.StartTime, f => TruncateToWholeMilliseconds(f.Date.FutureOffset())) + .RuleFor(meeting => meeting.Duration, f => f.PickRandom(_meetingDurations)) + .RuleFor(meeting => meeting.Latitude, f => f.Address.Latitude()) + .RuleFor(meeting => meeting.Longitude, f => f.Address.Longitude())); + + private readonly Lazy> _lazyMeetingAttendeeFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(attendee => attendee.DisplayName, f => f.Random.Utf16String())); + + public Faker Meeting => _lazyMeetingFaker.Value; + public Faker MeetingAttendee => _lazyMeetingAttendeeFaker.Value; + + private static DateTimeOffset TruncateToWholeMilliseconds(DateTimeOffset value) + { + var ticksToSubtract = value.DateTime.Ticks % TimeSpan.TicksPerMillisecond; + var ticksInWholeMilliseconds = value.DateTime.Ticks - ticksToSubtract; + + return new DateTimeOffset(new DateTime(ticksInWholeMilliseconds), value.Offset); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs new file mode 100644 index 0000000000..62a402f564 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs @@ -0,0 +1,555 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization +{ + public sealed class SerializationTests + : IClassFixture, SerializationDbContext>> + { + private readonly IntegrationTestContext, SerializationDbContext> _testContext; + private readonly SerializationFakers _fakers = new SerializationFakers(); + + public SerializationTests(IntegrationTestContext, SerializationDbContext> testContext) + { + _testContext = testContext; + + testContext.ConfigureServicesAfterStartup(services => + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + + var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); + options.IncludeExceptionStackTraceInErrors = false; + options.AllowClientGeneratedIds = true; + } + + [Fact] + public async Task Can_get_primary_resources_with_include() + { + // Arrange + var meetings = _fakers.Meeting.Generate(1); + meetings[0].Attendees = _fakers.MeetingAttendee.Generate(1); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Meetings.AddRange(meetings); + await dbContext.SaveChangesAsync(); + }); + + var route = "/meetings?include=attendees"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings?include=attendees"", + ""first"": ""http://localhost/meetings?include=attendees"" + }, + ""data"": [ + { + ""type"": ""meetings"", + ""id"": """ + meetings[0].StringId + @""", + ""attributes"": { + ""title"": """ + meetings[0].Title + @""", + ""startTime"": """ + meetings[0].StartTime.ToString("O") + @""", + ""duration"": """ + meetings[0].Duration + @""", + ""location"": { + ""lat"": " + meetings[0].Location.Latitude.ToString(CultureInfo.InvariantCulture) + @", + ""lng"": " + meetings[0].Location.Longitude.ToString(CultureInfo.InvariantCulture) + @" + } + }, + ""relationships"": { + ""attendees"": { + ""links"": { + ""self"": ""http://localhost/meetings/" + meetings[0].StringId + @"/relationships/attendees"", + ""related"": ""http://localhost/meetings/" + meetings[0].StringId + @"/attendees"" + }, + ""data"": [ + { + ""type"": ""meetingAttendees"", + ""id"": """ + meetings[0].Attendees[0].StringId + @""" + } + ] + } + }, + ""links"": { + ""self"": ""http://localhost/meetings/" + meetings[0].StringId + @""" + } + } + ], + ""included"": [ + { + ""type"": ""meetingAttendees"", + ""id"": """ + meetings[0].Attendees[0].StringId + @""", + ""attributes"": { + ""displayName"": """ + meetings[0].Attendees[0].DisplayName + @""" + }, + ""relationships"": { + ""meeting"": { + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @"/relationships/meeting"", + ""related"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @"/meeting"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @""" + } + } + ] +}"); + } + + [Fact] + public async Task Can_get_primary_resource_by_ID() + { + // Arrange + var meeting = _fakers.Meeting.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Meetings.Add(meeting); + await dbContext.SaveChangesAsync(); + }); + + var route = "/meetings/" + meeting.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @""" + }, + ""data"": { + ""type"": ""meetings"", + ""id"": """ + meeting.StringId + @""", + ""attributes"": { + ""title"": """ + meeting.Title + @""", + ""startTime"": """ + meeting.StartTime.ToString("O") + @""", + ""duration"": """ + meeting.Duration + @""", + ""location"": { + ""lat"": " + meeting.Location.Latitude.ToString(CultureInfo.InvariantCulture) + @", + ""lng"": " + meeting.Location.Longitude.ToString(CultureInfo.InvariantCulture) + @" + } + }, + ""relationships"": { + ""attendees"": { + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @"/relationships/attendees"", + ""related"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @""" + } + } +}"); + } + + [Fact] + public async Task Cannot_get_unknown_primary_resource_by_ID() + { + // Arrange + var unknownId = Guid.NewGuid(); + + var route = "/meetings/" + unknownId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); + + var jObject = JsonConvert.DeserializeObject(responseDocument); + var errorId = jObject["errors"].Should().NotBeNull().And.Subject.Select(element => (string) element["id"]).Single(); + + responseDocument.Should().BeJson(@"{ + ""errors"": [ + { + ""id"": """ + errorId + @""", + ""status"": ""404"", + ""title"": ""The requested resource does not exist."", + ""detail"": ""Resource of type 'meetings' with ID '" + unknownId + @"' does not exist."" + } + ] +}"); + } + + [Fact] + public async Task Can_get_secondary_resource() + { + // Arrange + var attendee = _fakers.MeetingAttendee.Generate(); + attendee.Meeting = _fakers.Meeting.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Attendees.Add(attendee); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetingAttendees/{attendee.StringId}/meeting"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + attendee.StringId + @"/meeting"" + }, + ""data"": { + ""type"": ""meetings"", + ""id"": """ + attendee.Meeting.StringId + @""", + ""attributes"": { + ""title"": """ + attendee.Meeting.Title + @""", + ""startTime"": """ + attendee.Meeting.StartTime.ToString("O") + @""", + ""duration"": """ + attendee.Meeting.Duration + @""", + ""location"": { + ""lat"": " + attendee.Meeting.Location.Latitude.ToString(CultureInfo.InvariantCulture) + @", + ""lng"": " + attendee.Meeting.Location.Longitude.ToString(CultureInfo.InvariantCulture) + @" + } + }, + ""relationships"": { + ""attendees"": { + ""links"": { + ""self"": ""http://localhost/meetings/" + attendee.Meeting.StringId + @"/relationships/attendees"", + ""related"": ""http://localhost/meetings/" + attendee.Meeting.StringId + @"/attendees"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetings/" + attendee.Meeting.StringId + @""" + } + } +}"); + } + + [Fact] + public async Task Can_get_unknown_secondary_resource() + { + // Arrange + var attendee = _fakers.MeetingAttendee.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Attendees.Add(attendee); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetingAttendees/{attendee.StringId}/meeting"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + attendee.StringId + @"/meeting"" + }, + ""data"": null +}"); + } + + [Fact] + public async Task Can_get_secondary_resources() + { + // Arrange + var meeting = _fakers.Meeting.Generate(); + meeting.Attendees = _fakers.MeetingAttendee.Generate(1); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Meetings.Add(meeting); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetings/{meeting.StringId}/attendees"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"", + ""first"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"" + }, + ""data"": [ + { + ""type"": ""meetingAttendees"", + ""id"": """ + meeting.Attendees[0].StringId + @""", + ""attributes"": { + ""displayName"": """ + meeting.Attendees[0].DisplayName + @""" + }, + ""relationships"": { + ""meeting"": { + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + meeting.Attendees[0].StringId + @"/relationships/meeting"", + ""related"": ""http://localhost/meetingAttendees/" + meeting.Attendees[0].StringId + @"/meeting"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + meeting.Attendees[0].StringId + @""" + } + } + ] +}"); + } + + [Fact] + public async Task Can_get_unknown_secondary_resources() + { + // Arrange + var meeting = _fakers.Meeting.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Meetings.Add(meeting); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetings/{meeting.StringId}/attendees"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"", + ""first"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"" + }, + ""data"": [] +}"); + } + + [Fact] + public async Task Can_get_HasOne_relationship() + { + // Arrange + var attendee = _fakers.MeetingAttendee.Generate(); + attendee.Meeting = _fakers.Meeting.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Attendees.Add(attendee); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetingAttendees/{attendee.StringId}/relationships/meeting"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + attendee.StringId + @"/relationships/meeting"", + ""related"": ""http://localhost/meetingAttendees/" + attendee.StringId + @"/meeting"" + }, + ""data"": { + ""type"": ""meetings"", + ""id"": """ + attendee.Meeting.StringId + @""" + } +}"); + } + + [Fact] + public async Task Can_get_HasMany_relationship() + { + // Arrange + var meeting = _fakers.Meeting.Generate(); + meeting.Attendees = _fakers.MeetingAttendee.Generate(2); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Meetings.Add(meeting); + await dbContext.SaveChangesAsync(); + }); + + var route = $"/meetings/{meeting.StringId}/relationships/attendees"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + var meetingIds = meeting.Attendees.Select(attendee => attendee.StringId).OrderBy(id => id).ToArray(); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings/" + meeting.StringId + @"/relationships/attendees"", + ""related"": ""http://localhost/meetings/" + meeting.StringId + @"/attendees"", + ""first"": ""http://localhost/meetings/" + meeting.StringId + @"/relationships/attendees"" + }, + ""data"": [ + { + ""type"": ""meetingAttendees"", + ""id"": """ + meetingIds[0] + @""" + }, + { + ""type"": ""meetingAttendees"", + ""id"": """ + meetingIds[1] + @""" + } + ] +}"); + } + + [Fact] + public async Task Can_create_resource_with_side_effects() + { + // Arrange + var newMeeting = _fakers.Meeting.Generate(); + newMeeting.Id = Guid.NewGuid(); + + var requestBody = new + { + data = new + { + type = "meetings", + id = newMeeting.StringId, + attributes = new + { + title = newMeeting.Title, + startTime = newMeeting.StartTime, + duration = newMeeting.Duration, + location = new + { + lat = newMeeting.Latitude, + lng = newMeeting.Longitude + } + } + } + }; + + var route = "/meetings"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetings"" + }, + ""data"": { + ""type"": ""meetings"", + ""id"": """ + newMeeting.StringId + @""", + ""attributes"": { + ""title"": """ + newMeeting.Title + @""", + ""startTime"": """ + newMeeting.StartTime.ToString("O") + @""", + ""duration"": """ + newMeeting.Duration + @""", + ""location"": { + ""lat"": " + newMeeting.Location.Latitude.ToString(CultureInfo.InvariantCulture) + @", + ""lng"": " + newMeeting.Location.Longitude.ToString(CultureInfo.InvariantCulture) + @" + } + }, + ""relationships"": { + ""attendees"": { + ""links"": { + ""self"": ""http://localhost/meetings/" + newMeeting.StringId + @"/relationships/attendees"", + ""related"": ""http://localhost/meetings/" + newMeeting.StringId + @"/attendees"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetings/" + newMeeting.StringId + @""" + } + } +}"); + } + + [Fact] + public async Task Can_update_resource_with_side_effects() + { + // Arrange + var existingAttendee = _fakers.MeetingAttendee.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Attendees.Add(existingAttendee); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "meetingAttendees", + id = existingAttendee.StringId, + attributes = new + { + displayName = existingAttendee.DisplayName + } + } + }; + + var route = "/meetingAttendees/" + existingAttendee.StringId; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); + + responseDocument.Should().BeJson(@"{ + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + existingAttendee.StringId + @""" + }, + ""data"": { + ""type"": ""meetingAttendees"", + ""id"": """ + existingAttendee.StringId + @""", + ""attributes"": { + ""displayName"": """ + existingAttendee.DisplayName + @""" + }, + ""relationships"": { + ""meeting"": { + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + existingAttendee.StringId + @"/relationships/meeting"", + ""related"": ""http://localhost/meetingAttendees/" + existingAttendee.StringId + @"/meeting"" + } + } + }, + ""links"": { + ""self"": ""http://localhost/meetingAttendees/" + existingAttendee.StringId + @""" + } + } +}"); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs b/test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs new file mode 100644 index 0000000000..b518811dfd --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs @@ -0,0 +1,28 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCoreExampleTests +{ + /// + /// Ensures the resource attributes are returned when creating/updating a resource. + /// + internal sealed class NeverSameResourceChangeTracker : IResourceChangeTracker + where TResource : class, IIdentifiable + { + public void SetInitiallyStoredAttributeValues(TResource resource) + { + } + + public void SetRequestedAttributeValues(TResource resource) + { + } + + public void SetFinallyStoredAttributeValues(TResource resource) + { + } + + public bool HasImplicitChanges() + { + return true; + } + } +} From 8bf098940766d9a6cf7123636a0cc55bce073291 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Wed, 3 Feb 2021 00:49:41 +0100 Subject: [PATCH 14/47] Refactored tests for resource hooks --- ...finition.cs => TodoItemHooksDefinition.cs} | 4 +- .../JsonApiDotNetCoreExample/Dockerfile | 13 - .../JsonApiDotNetCoreExample/Models/User.cs | 9 +- .../Services/CustomArticleService.cs | 37 - .../Startups/TestStartup.cs | 47 -- .../ResourceDefinitionTests.cs | 611 -------------- .../Spec/FunctionalTestCollection.cs | 116 --- .../Factories/CustomApplicationFactoryBase.cs | 38 - .../ResourceHooksApplicationFactory.cs | 33 - .../Factories/StandardApplicationFactory.cs | 19 - .../Helpers/Models/TodoItemClient.cs | 34 - .../ResourceHooks/HookFakers.cs | 79 ++ .../ResourceHooks/ResourceHookTests.cs | 754 ++++++++++++++++++ .../ResourceHooks/ResourceHooksStartup.cs | 36 + .../ServiceCollectionExtensions.cs | 15 + 15 files changed, 892 insertions(+), 953 deletions(-) rename src/Examples/JsonApiDotNetCoreExample/Definitions/{TodoHooksDefinition.cs => TodoItemHooksDefinition.cs} (86%) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Dockerfile delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Startups/TestStartup.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FunctionalTestCollection.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Factories/CustomApplicationFactoryBase.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Factories/ResourceHooksApplicationFactory.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Factories/StandardApplicationFactory.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/Helpers/Models/TodoItemClient.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoHooksDefinition.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemHooksDefinition.cs similarity index 86% rename from src/Examples/JsonApiDotNetCoreExample/Definitions/TodoHooksDefinition.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemHooksDefinition.cs index c3fc7af2e5..33fa998337 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoHooksDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemHooksDefinition.cs @@ -9,9 +9,9 @@ namespace JsonApiDotNetCoreExample.Definitions { - public class TodoHooksDefinition : LockableHooksDefinition + public class TodoItemHooksDefinition : LockableHooksDefinition { - public TodoHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TodoItemHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Dockerfile b/src/Examples/JsonApiDotNetCoreExample/Dockerfile deleted file mode 100644 index c5a5d90ff4..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM microsoft/dotnet:latest - -COPY . /app - -WORKDIR /app - -RUN ["dotnet", "restore"] - -RUN ["dotnet", "build"] - -EXPOSE 14140/tcp - -CMD ["dotnet", "run", "--server.urls", "http://*:14140"] diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs index fde27f2922..dea3f26be5 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs @@ -11,7 +11,8 @@ public class User : Identifiable private readonly ISystemClock _systemClock; private string _password; - [Attr] public string UserName { get; set; } + [Attr] + public string UserName { get; set; } [Attr(Capabilities = AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange)] public string Password @@ -27,7 +28,8 @@ public string Password } } - [Attr] public DateTime LastPasswordChange { get; set; } + [Attr] + public DateTime LastPasswordChange { get; set; } public User(AppDbContext appDbContext) { @@ -37,7 +39,8 @@ public User(AppDbContext appDbContext) public sealed class SuperUser : User { - [Attr] public int SecurityLevel { get; set; } + [Attr] + public int SecurityLevel { get; set; } public SuperUser(AppDbContext appDbContext) : base(appDbContext) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs b/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs deleted file mode 100644 index b71a1e9e57..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Queries; -using JsonApiDotNetCore.Repositories; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Services; -using JsonApiDotNetCoreExample.Models; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreExample.Services -{ - public class CustomArticleService : JsonApiResourceService
- { - public CustomArticleService( - IResourceRepositoryAccessor repositoryAccessor, - IQueryLayerComposer queryLayerComposer, - IPaginationContext paginationContext, - IJsonApiOptions options, - ILoggerFactory loggerFactory, - IJsonApiRequest request, - IResourceChangeTracker
resourceChangeTracker, - IResourceHookExecutorFacade hookExecutor) - : base(repositoryAccessor, queryLayerComposer, paginationContext, options, loggerFactory, request, - resourceChangeTracker, hookExecutor) - { } - - public override async Task
GetAsync(int id, CancellationToken cancellationToken) - { - var resource = await base.GetAsync(id, cancellationToken); - resource.Caption = "None for you Glen Coco"; - return resource; - } - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/TestStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/TestStartup.cs deleted file mode 100644 index e1af39084c..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/TestStartup.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace JsonApiDotNetCoreExample -{ - public class TestStartup : Startup - { - public TestStartup(IConfiguration configuration) : base(configuration) - { - } - - protected override void ConfigureClock(IServiceCollection services) - { - services.AddSingleton(); - } - - /// - /// Advances the clock one second each time the current time is requested. - /// - private class TickingSystemClock : ISystemClock - { - private DateTimeOffset _utcNow; - - public DateTimeOffset UtcNow - { - get - { - var utcNow = _utcNow; - _utcNow = _utcNow.AddSeconds(1); - return utcNow; - } - } - - public TickingSystemClock() - : this(new DateTimeOffset(new DateTime(2000, 1, 1))) - { - } - - public TickingSystemClock(DateTimeOffset utcNow) - { - _utcNow = utcNow; - } - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs deleted file mode 100644 index ac589aa0a8..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ResourceDefinitions/ResourceDefinitionTests.cs +++ /dev/null @@ -1,611 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Bogus; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Serialization.Objects; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.Acceptance.Spec; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; - -namespace JsonApiDotNetCoreExampleTests.Acceptance -{ - public sealed class ResourceDefinitionTests : FunctionalTestCollection - { - private readonly Faker _userFaker; - private readonly Faker _todoItemFaker; - private readonly Faker _personFaker; - private readonly Faker
_articleFaker; - private readonly Faker _authorFaker; - private readonly Faker _tagFaker; - - public ResourceDefinitionTests(ResourceHooksApplicationFactory factory) : base(factory) - { - _authorFaker = new Faker() - .RuleFor(a => a.LastName, f => f.Random.Words(2)); - - _articleFaker = new Faker
() - .RuleFor(a => a.Caption, f => f.Random.AlphaNumeric(10)) - .RuleFor(a => a.Author, f => _authorFaker.Generate()); - - _userFaker = new Faker() - .CustomInstantiator(f => new User(_dbContext)) - .RuleFor(u => u.UserName, f => f.Internet.UserName()) - .RuleFor(u => u.Password, f => f.Internet.Password()); - - _todoItemFaker = new Faker() - .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); - - _personFaker = new Faker() - .RuleFor(p => p.FirstName, f => f.Name.FirstName()) - .RuleFor(p => p.LastName, f => f.Name.LastName()); - - _tagFaker = new Faker() - .CustomInstantiator(f => new Tag()) - .RuleFor(a => a.Name, f => f.Random.AlphaNumeric(10)); - - var options = (JsonApiOptions) _factory.Services.GetRequiredService(); - options.DisableTopPagination = false; - options.DisableChildrenPagination = false; - } - - [Fact] - public async Task Can_Create_User_With_Password() - { - // Arrange - var user = _userFaker.Generate(); - - var serializer = GetSerializer(p => new { p.Password, p.UserName }); - - var httpMethod = new HttpMethod("POST"); - var route = "/api/v1/users"; - - var request = new HttpRequestMessage(httpMethod, route) - { - Content = new StringContent(serializer.Serialize(user)) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.Created, response.StatusCode); - - // response assertions - var body = await response.Content.ReadAsStringAsync(); - var returnedUser = _deserializer.DeserializeSingle(body).Data; - var document = JsonConvert.DeserializeObject(body); - Assert.False(document.SingleData.Attributes.ContainsKey("password")); - Assert.Equal(user.UserName, document.SingleData.Attributes["userName"]); - - // db assertions - var dbUser = await _dbContext.Users.FindAsync(returnedUser.Id); - Assert.Equal(user.UserName, dbUser.UserName); - Assert.Equal(user.Password, dbUser.Password); - } - - [Fact] - public async Task Can_Update_User_Password() - { - // Arrange - var user = _userFaker.Generate(); - _dbContext.Users.Add(user); - await _dbContext.SaveChangesAsync(); - - user.Password = _userFaker.Generate().Password; - var serializer = GetSerializer(p => new { p.Password }); - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/users/{user.Id}"; - var request = new HttpRequestMessage(httpMethod, route) - { - Content = new StringContent(serializer.Serialize(user)) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - // response assertions - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); - Assert.False(document.SingleData.Attributes.ContainsKey("password")); - Assert.Equal(user.UserName, document.SingleData.Attributes["userName"]); - - // db assertions - var dbUser = _dbContext.Users.AsNoTracking().Single(u => u.Id == user.Id); - Assert.Equal(user.Password, dbUser.Password); - } - - [Fact] - public async Task Unauthorized_TodoItem() - { - // Arrange - var route = "/api/v1/todoItems/1337"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update the author of todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Unauthorized_Passport() - { - // Arrange - var route = "/api/v1/people/1?include=passport"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to include passports on individual persons.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Unauthorized_Article() - { - // Arrange - var article = _articleFaker.Generate(); - article.Caption = "Classified"; - _dbContext.Articles.Add(article); - await _dbContext.SaveChangesAsync(); - - var route = $"/api/v1/articles/{article.Id}"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to see this article.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Article_Is_Hidden() - { - // Arrange - var articles = _articleFaker.Generate(3); - string toBeExcluded = "This should not be included"; - articles[0].Caption = toBeExcluded; - - _dbContext.Articles.AddRange(articles); - await _dbContext.SaveChangesAsync(); - - var route = "/api/v1/articles"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with body: {body}"); - Assert.DoesNotContain(toBeExcluded, body); - } - - [Fact] - public async Task Article_Through_Secondary_Endpoint_Is_Hidden() - { - // Arrange - var articles = _articleFaker.Generate(3); - string toBeExcluded = "This should not be included"; - articles[0].Caption = toBeExcluded; - var author = _authorFaker.Generate(); - author.Articles = articles; - - _dbContext.AuthorDifferentDbContextName.Add(author); - await _dbContext.SaveChangesAsync(); - - var route = $"/api/v1/authors/{author.Id}/articles"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with body: {body}"); - Assert.DoesNotContain(toBeExcluded, body); - } - - [Fact] - public async Task Tag_Is_Hidden() - { - // Arrange - var article = _articleFaker.Generate(); - var tags = _tagFaker.Generate(2); - string toBeExcluded = "This should not be included"; - tags[0].Name = toBeExcluded; - - var articleTags = new[] - { - new ArticleTag - { - Article = article, - Tag = tags[0] - }, - new ArticleTag - { - Article = article, - Tag = tags[1] - } - }; - _dbContext.ArticleTags.AddRange(articleTags); - await _dbContext.SaveChangesAsync(); - - // Workaround for https://github.com/dotnet/efcore/issues/21026 - var options = (JsonApiOptions) _factory.Services.GetRequiredService(); - options.DisableTopPagination = false; - options.DisableChildrenPagination = true; - - var route = "/api/v1/articles?include=tags"; - - // Act - var response = await _client.GetAsync(route); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - Assert.True(HttpStatusCode.OK == response.StatusCode, $"{route} returned {response.StatusCode} status code with body: {body}"); - Assert.DoesNotContain(toBeExcluded, body); - } - ///// - ///// In the Cascade Permission Error tests, we ensure that all the relevant - ///// resources are provided in the hook definitions. In this case, - ///// re-relating the meta object to a different article would require - ///// also a check for the lockedTodo, because we're implicitly updating - ///// its foreign key. - ///// - [Fact] - public async Task Cascade_Permission_Error_Create_ToOne_Relationship() - { - // Arrange - var lockedPerson = _personFaker.Generate(); - lockedPerson.IsLocked = true; - var passport = new Passport(_dbContext); - lockedPerson.Passport = passport; - _dbContext.People.AddRange(lockedPerson); - await _dbContext.SaveChangesAsync(); - - var content = new - { - data = new - { - type = "people", - relationships = new Dictionary - { - { "passport", new - { - data = new { type = "passports", id = lockedPerson.Passport.StringId } - } - } - } - } - }; - - var httpMethod = new HttpMethod("POST"); - var route = "/api/v1/people"; - var request = new HttpRequestMessage(httpMethod, route); - - string serializedContent = JsonConvert.SerializeObject(content); - request.Content = new StringContent(serializedContent); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Updating_ToOne_Relationship() - { - // Arrange - var person = _personFaker.Generate(); - var passport = new Passport(_dbContext) { IsLocked = true }; - person.Passport = passport; - _dbContext.People.AddRange(person); - var newPassport = new Passport(_dbContext); - _dbContext.Passports.Add(newPassport); - await _dbContext.SaveChangesAsync(); - - var content = new - { - data = new - { - type = "people", - id = person.Id, - relationships = new Dictionary - { - { "passport", new - { - data = new { type = "passports", id = newPassport.StringId } - } - } - } - } - }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/people/{person.Id}"; - var request = new HttpRequestMessage(httpMethod, route); - - string serializedContent = JsonConvert.SerializeObject(content); - request.Content = new StringContent(serializedContent); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked persons.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Updating_ToOne_Relationship_Deletion() - { - // Arrange - var person = _personFaker.Generate(); - var passport = new Passport(_dbContext) { IsLocked = true }; - person.Passport = passport; - _dbContext.People.AddRange(person); - var newPassport = new Passport(_dbContext); - _dbContext.Passports.Add(newPassport); - await _dbContext.SaveChangesAsync(); - - var content = new - { - data = new - { - type = "people", - id = person.Id, - relationships = new Dictionary - { - { "passport", new - { - data = (object)null - } - } - } - } - }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/people/{person.Id}"; - var request = new HttpRequestMessage(httpMethod, route); - - string serializedContent = JsonConvert.SerializeObject(content); - request.Content = new StringContent(serializedContent); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked persons.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Delete_ToOne_Relationship() - { - // Arrange - var lockedPerson = _personFaker.Generate(); - lockedPerson.IsLocked = true; - var passport = new Passport(_dbContext); - lockedPerson.Passport = passport; - _dbContext.People.AddRange(lockedPerson); - await _dbContext.SaveChangesAsync(); - - var httpMethod = new HttpMethod("DELETE"); - var route = $"/api/v1/passports/{lockedPerson.Passport.StringId}"; - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Create_ToMany_Relationship() - { - // Arrange - var persons = _personFaker.Generate(2); - var lockedTodo = _todoItemFaker.Generate(); - lockedTodo.IsLocked = true; - lockedTodo.StakeHolders = persons.ToHashSet(); - _dbContext.TodoItems.Add(lockedTodo); - await _dbContext.SaveChangesAsync(); - - var content = new - { - data = new - { - type = "todoItems", - relationships = new Dictionary - { - { "stakeHolders", new - { - data = new[] - { - new { type = "people", id = persons[0].StringId }, - new { type = "people", id = persons[1].StringId } - } - - } - } - } - } - }; - - var httpMethod = new HttpMethod("POST"); - var route = "/api/v1/todoItems"; - var request = new HttpRequestMessage(httpMethod, route); - - string serializedContent = JsonConvert.SerializeObject(content); - request.Content = new StringContent(serializedContent); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Updating_ToMany_Relationship() - { - // Arrange - var persons = _personFaker.Generate(2); - var lockedTodo = _todoItemFaker.Generate(); - lockedTodo.IsLocked = true; - lockedTodo.StakeHolders = persons.ToHashSet(); - _dbContext.TodoItems.Add(lockedTodo); - var unlockedTodo = _todoItemFaker.Generate(); - _dbContext.TodoItems.Add(unlockedTodo); - await _dbContext.SaveChangesAsync(); - - var content = new - { - data = new - { - type = "todoItems", - id = unlockedTodo.Id, - relationships = new Dictionary - { - { "stakeHolders", new - { - data = new[] - { - new { type = "people", id = persons[0].StringId }, - new { type = "people", id = persons[1].StringId } - } - - } - } - } - } - }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/todoItems/{unlockedTodo.Id}"; - var request = new HttpRequestMessage(httpMethod, route); - - string serializedContent = JsonConvert.SerializeObject(content); - request.Content = new StringContent(serializedContent); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - - [Fact] - public async Task Cascade_Permission_Error_Delete_ToMany_Relationship() - { - // Arrange - var persons = _personFaker.Generate(2); - var lockedTodo = _todoItemFaker.Generate(); - lockedTodo.IsLocked = true; - lockedTodo.StakeHolders = persons.ToHashSet(); - _dbContext.TodoItems.Add(lockedTodo); - await _dbContext.SaveChangesAsync(); - - var httpMethod = new HttpMethod("DELETE"); - var route = $"/api/v1/people/{persons[0].Id}"; - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await _client.SendAsync(request); - - // Assert - var body = await response.Content.ReadAsStringAsync(); - AssertEqualStatusCode(HttpStatusCode.Forbidden, response); - - var errorDocument = JsonConvert.DeserializeObject(body); - Assert.Single(errorDocument.Errors); - Assert.Equal(HttpStatusCode.Forbidden, errorDocument.Errors[0].StatusCode); - Assert.Equal("You are not allowed to update fields or relationships of locked todo items.", errorDocument.Errors[0].Title); - Assert.Null(errorDocument.Errors[0].Detail); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FunctionalTestCollection.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FunctionalTestCollection.cs deleted file mode 100644 index 1030ff426c..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FunctionalTestCollection.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Serialization.Client.Internal; -using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.Helpers.Models; -using Microsoft.Extensions.Logging.Abstractions; -using Xunit; - -namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec -{ - public class FunctionalTestCollection : IClassFixture where TFactory : class, IApplicationFactory - { - public static MediaTypeHeaderValue JsonApiContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - protected readonly TFactory _factory; - protected readonly HttpClient _client; - protected readonly AppDbContext _dbContext; - protected IResponseDeserializer _deserializer; - - public FunctionalTestCollection(TFactory factory) - { - _factory = factory; - _client = _factory.CreateClient(); - _dbContext = _factory.GetRequiredService(); - _deserializer = GetDeserializer(); - ClearDbContext(); - } - - protected Task<(string, HttpResponseMessage)> Get(string route) - { - return SendRequest("GET", route); - } - - protected Task<(string, HttpResponseMessage)> Post(string route, string content) - { - return SendRequest("POST", route, content); - } - - protected Task<(string, HttpResponseMessage)> Patch(string route, string content) - { - return SendRequest("PATCH", route, content); - } - - protected Task<(string, HttpResponseMessage)> Delete(string route) - { - return SendRequest("DELETE", route); - } - - protected IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable - { - var serializer = GetService(); - var graph = GetService(); - serializer.AttributesToSerialize = attributes != null ? graph.GetAttributes(attributes) : null; - serializer.RelationshipsToSerialize = relationships != null ? graph.GetRelationships(relationships) : null; - return serializer; - } - - protected IResponseDeserializer GetDeserializer() - { - var options = GetService(); - var formatter = new ResourceNameFormatter(options); - var resourcesContexts = GetService().GetResourceContexts(); - var builder = new ResourceGraphBuilder(options, NullLoggerFactory.Instance); - foreach (var rc in resourcesContexts) - { - if (rc.ResourceType == typeof(TodoItem) || rc.ResourceType == typeof(TodoItemCollection)) - { - continue; - } - builder.Add(rc.ResourceType, rc.IdentityType, rc.PublicName); - } - builder.Add(formatter.FormatResourceName(typeof(TodoItem))); - builder.Add(formatter.FormatResourceName(typeof(TodoItemCollection))); - return new ResponseDeserializer(builder.Build(), new ResourceFactory(_factory.ServiceProvider)); - } - - protected AppDbContext GetDbContext() => GetService(); - - protected T GetService() => _factory.GetRequiredService(); - - protected void AssertEqualStatusCode(HttpStatusCode expected, HttpResponseMessage response) - { - var responseBody = response.Content.ReadAsStringAsync().Result; - Assert.True(expected == response.StatusCode, $"Got {response.StatusCode} status code instead of {expected}. Response body: {responseBody}"); - } - - protected void ClearDbContext() - { - _dbContext.ClearTable(); - _dbContext.ClearTable(); - _dbContext.ClearTable(); - _dbContext.ClearTable(); - _dbContext.SaveChanges(); - } - - private async Task<(string, HttpResponseMessage)> SendRequest(string method, string route, string content = null) - { - var request = new HttpRequestMessage(new HttpMethod(method), route); - if (content != null) - { - request.Content = new StringContent(content); - request.Content.Headers.ContentType = JsonApiContentType; - } - var response = await _client.SendAsync(request); - var body = await response.Content?.ReadAsStringAsync(); - return (body, response); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Factories/CustomApplicationFactoryBase.cs b/test/JsonApiDotNetCoreExampleTests/Factories/CustomApplicationFactoryBase.cs deleted file mode 100644 index 04e237acb8..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Factories/CustomApplicationFactoryBase.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Net.Http; -using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.Extensions.DependencyInjection; - -namespace JsonApiDotNetCoreExampleTests -{ - public class CustomApplicationFactoryBase : WebApplicationFactory, IApplicationFactory - { - public readonly HttpClient Client; - private readonly IServiceScope _scope; - - public IServiceProvider ServiceProvider => _scope.ServiceProvider; - - public CustomApplicationFactoryBase() - { - Client = CreateClient(); - _scope = Services.CreateScope(); - } - - public T GetRequiredService() => (T)_scope.ServiceProvider.GetRequiredService(typeof(T)); - - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - builder.UseStartup(); - } - } - - public interface IApplicationFactory - { - IServiceProvider ServiceProvider { get; } - - T GetRequiredService(); - HttpClient CreateClient(); - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Factories/ResourceHooksApplicationFactory.cs b/test/JsonApiDotNetCoreExampleTests/Factories/ResourceHooksApplicationFactory.cs deleted file mode 100644 index 72f879054b..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Factories/ResourceHooksApplicationFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; - -namespace JsonApiDotNetCoreExampleTests -{ - public class ResourceHooksApplicationFactory : CustomApplicationFactoryBase - { - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - base.ConfigureWebHost(builder); - - builder.ConfigureServices(services => - { - services.AddClientSerialization(); - }); - - builder.ConfigureTestServices(services => - { - services.AddJsonApi(options => - { - options.Namespace = "api/v1"; - options.DefaultPageSize = new PageSize(5); - options.IncludeTotalResourceCount = true; - options.EnableResourceHooks = true; - options.LoadDatabaseValues = true; - options.IncludeExceptionStackTraceInErrors = true; - }, - discovery => discovery.AddAssembly(typeof(JsonApiDotNetCoreExample.Program).Assembly)); - }); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Factories/StandardApplicationFactory.cs b/test/JsonApiDotNetCoreExampleTests/Factories/StandardApplicationFactory.cs deleted file mode 100644 index 8c45e2e5e7..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Factories/StandardApplicationFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; - -namespace JsonApiDotNetCoreExampleTests -{ - public class StandardApplicationFactory : CustomApplicationFactoryBase - { - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - base.ConfigureWebHost(builder); - - builder.ConfigureTestServices(services => - { - services.AddClientSerialization(); - }); - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/Helpers/Models/TodoItemClient.cs b/test/JsonApiDotNetCoreExampleTests/Helpers/Models/TodoItemClient.cs deleted file mode 100644 index 3cbe45501c..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/Helpers/Models/TodoItemClient.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; -using JsonApiDotNetCoreExample.Models; - -namespace JsonApiDotNetCoreExampleTests.Helpers.Models -{ - /// - /// this "client" version of the is required because the - /// base property that is overridden here does not have a setter. For a model - /// defined on a JSON:API client, it would not make sense to have an exposed attribute - /// without a setter. - /// - public class TodoItemClient : TodoItem - { - [Attr] - public new string CalculatedValue { get; set; } - } - - [Resource("todoCollections")] - public sealed class TodoItemCollectionClient : Identifiable - { - [Attr] - public string Name { get; set; } - public int OwnerId { get; set; } - - [HasMany] - public ISet TodoItems { get; set; } - - [HasOne] - public Person Owner { get; set; } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs new file mode 100644 index 0000000000..1ffabb7c0a --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs @@ -0,0 +1,79 @@ +using System; +using Bogus; +using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Models; +using Microsoft.Extensions.DependencyInjection; +using Person = JsonApiDotNetCoreExample.Models.Person; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks +{ + internal sealed class HookFakers : FakerContainer + { + private readonly IServiceProvider _serviceProvider; + + private readonly Lazy> _lazyAuthorFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(author => author.FirstName, f => f.Person.FirstName) + .RuleFor(author => author.LastName, f => f.Person.LastName) + .RuleFor(author => author.DateOfBirth, f => f.Person.DateOfBirth) + .RuleFor(author => author.BusinessEmail, f => f.Person.Email)); + + private readonly Lazy> _lazyArticleFaker = new Lazy>(() => + new Faker
() + .UseSeed(GetFakerSeed()) + .RuleFor(article => article.Caption, f => f.Lorem.Word()) + .RuleFor(article => article.Url, f => f.Internet.Url())); + + private readonly Lazy> _lazyUserFaker; + + private readonly Lazy> _lazyTodoItemFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(todoItem => todoItem.Description, f => f.Random.Words()) + .RuleFor(todoItem => todoItem.Ordinal, f => f.Random.Long(1, 999999)) + .RuleFor(todoItem => todoItem.CreatedDate, f => f.Date.Past()) + .RuleFor(todoItem => todoItem.AchievedDate, f => f.Date.Past()) + .RuleFor(todoItem => todoItem.OffsetDate, f => f.Date.FutureOffset())); + + private readonly Lazy> _lazyPersonFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(person => person.FirstName, f => f.Person.FirstName) + .RuleFor(person => person.LastName, f => f.Person.LastName) + .RuleFor(person => person.Age, f => f.Random.Int(25, 50)) + .RuleFor(person => person.Gender, f => f.PickRandom()) + .RuleFor(person => person.Category, f => f.Lorem.Word())); + + private readonly Lazy> _lazyTagFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(tag => tag.Name, f => f.Lorem.Word()) + .RuleFor(tag => tag.Color, f => f.PickRandom())); + + public Faker Author => _lazyAuthorFaker.Value; + public Faker
Article => _lazyArticleFaker.Value; + public Faker User => _lazyUserFaker.Value; + public Faker TodoItem => _lazyTodoItemFaker.Value; + public Faker Person => _lazyPersonFaker.Value; + public Faker Tag => _lazyTagFaker.Value; + + public HookFakers(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + _lazyUserFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .CustomInstantiator(f => new User(ResolveDbContext())) + .RuleFor(user => user.UserName, f => f.Person.UserName) + .RuleFor(user => user.Password, f => f.Internet.Password())); + } + + private AppDbContext ResolveDbContext() + { + using var scope = _serviceProvider.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs new file mode 100644 index 0000000000..b0b0214f05 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -0,0 +1,754 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Serialization.Client.Internal; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Definitions; +using JsonApiDotNetCoreExample.Models; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks +{ + public sealed class ResourceHookTests + : IClassFixture, AppDbContext>> + { + private readonly IntegrationTestContext, AppDbContext> _testContext; + private readonly HookFakers _fakers; + + public ResourceHookTests(IntegrationTestContext, AppDbContext> testContext) + { + _testContext = testContext; + + testContext.ConfigureServicesAfterStartup(services => + { + services.AddScoped, ArticleHooksDefinition>(); + services.AddScoped, PassportHooksDefinition>(); + services.AddScoped, PersonHooksDefinition>(); + services.AddScoped, TagHooksDefinition>(); + services.AddScoped, TodoItemHooksDefinition>(); + }); + + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.DisableTopPagination = false; + options.DisableChildrenPagination = false; + + _fakers = new HookFakers(testContext.Factory.Services); + } + + [Fact] + public async Task Can_Create_User_With_Password() + { + // Arrange + var user = _fakers.User.Generate(); + + var serializer = GetRequestSerializer(p => new {p.Password, p.UserName}); + + var route = "/api/v1/users"; + + var request = new HttpRequestMessage(HttpMethod.Post, route) + { + Content = new StringContent(serializer.Serialize(user)) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Created); + + var body = await response.Content.ReadAsStringAsync(); + var returnedUser = GetResponseDeserializer().DeserializeSingle(body).Data; + var document = JsonConvert.DeserializeObject(body); + + document.SingleData.Attributes.Should().NotContainKey("password"); + document.SingleData.Attributes["userName"].Should().Be(user.UserName); + + using var scope = _testContext.Factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var dbUser = await dbContext.Users.FindAsync(returnedUser.Id); + + dbUser.UserName.Should().Be(user.UserName); + dbUser.Password.Should().Be(user.Password); + } + + [Fact] + public async Task Can_Update_User_Password() + { + // Arrange + var user = _fakers.User.Generate(); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.Users.Add(user); + await dbContext.SaveChangesAsync(); + } + + user.Password = _fakers.User.Generate().Password; + + var serializer = GetRequestSerializer(p => new {p.Password}); + + var route = $"/api/v1/users/{user.Id}"; + + var request = new HttpRequestMessage(HttpMethod.Patch, route) + { + Content = new StringContent(serializer.Serialize(user)) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.OK); + + var body = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(body); + + document.SingleData.Attributes.Should().NotContainKey("password"); + document.SingleData.Attributes["userName"].Should().Be(user.UserName); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + var dbUser = dbContext.Users.Single(u => u.Id == user.Id); + + dbUser.Password.Should().Be(user.Password); + } + } + + [Fact] + public async Task Unauthorized_TodoItem() + { + // Arrange + var route = "/api/v1/todoItems/1337"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update the author of todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Unauthorized_Passport() + { + // Arrange + var route = "/api/v1/people/1?include=passport"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to include passports on individual persons."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Unauthorized_Article() + { + // Arrange + var article = _fakers.Article.Generate(); + article.Caption = "Classified"; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.Articles.Add(article); + await dbContext.SaveChangesAsync(); + } + + var route = $"/api/v1/articles/{article.Id}"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to see this article."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Article_Is_Hidden() + { + // Arrange + var articles = _fakers.Article.Generate(3); + string toBeExcluded = "This should not be included"; + articles[0].Caption = toBeExcluded; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.Articles.AddRange(articles); + await dbContext.SaveChangesAsync(); + } + + var route = "/api/v1/articles"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.OK); + + var body = await response.Content.ReadAsStringAsync(); + body.Should().NotContain(toBeExcluded); + } + + [Fact] + public async Task Article_Through_Secondary_Endpoint_Is_Hidden() + { + // Arrange + var articles = _fakers.Article.Generate(3); + string toBeExcluded = "This should not be included"; + articles[0].Caption = toBeExcluded; + + var author = _fakers.Author.Generate(); + author.Articles = articles; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.AuthorDifferentDbContextName.Add(author); + await dbContext.SaveChangesAsync(); + } + + var route = $"/api/v1/authors/{author.Id}/articles"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.OK); + + var body = await response.Content.ReadAsStringAsync(); + body.Should().NotContain(toBeExcluded); + } + + [Fact] + public async Task Tag_Is_Hidden() + { + // Arrange + var article = _fakers.Article.Generate(); + var tags = _fakers.Tag.Generate(2); + string toBeExcluded = "This should not be included"; + tags[0].Name = toBeExcluded; + + var articleTags = new[] + { + new ArticleTag + { + Article = article, + Tag = tags[0] + }, + new ArticleTag + { + Article = article, + Tag = tags[1] + } + }; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.ArticleTags.AddRange(articleTags); + await dbContext.SaveChangesAsync(); + } + + // Workaround for https://github.com/dotnet/efcore/issues/21026 + var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); + options.DisableTopPagination = false; + options.DisableChildrenPagination = true; + + var route = "/api/v1/articles?include=tags"; + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.GetAsync(route); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.OK); + + var body = await response.Content.ReadAsStringAsync(); + body.Should().NotContain(toBeExcluded); + } + + [Fact] + public async Task Cascade_Permission_Error_Create_ToOne_Relationship() + { + // In the Cascade Permission Error tests, we ensure that all the relevant resources are provided in the hook definitions. In this case, + // re-relating the meta object to a different article would require also a check for the lockedTodo, because we're implicitly updating + // its foreign key. + + // Arrange + var lockedPerson = _fakers.Person.Generate(); + lockedPerson.IsLocked = true; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var passport = new Passport(dbContext); + lockedPerson.Passport = passport; + + dbContext.People.Add(lockedPerson); + await dbContext.SaveChangesAsync(); + } + + var requestBody = new + { + data = new + { + type = "people", + relationships = new + { + passport = new + { + data = new + { + type = "passports", + id = lockedPerson.Passport.StringId + } + } + } + } + }; + + var route = "/api/v1/people"; + + var request = new HttpRequestMessage(HttpMethod.Post, route); + + string requestText = JsonConvert.SerializeObject(requestBody); + request.Content = new StringContent(requestText); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Updating_ToOne_Relationship() + { + // Arrange + var person = _fakers.Person.Generate(); + Passport newPassport; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var passport = new Passport(dbContext) {IsLocked = true}; + person.Passport = passport; + dbContext.People.Add(person); + newPassport = new Passport(dbContext); + dbContext.Passports.Add(newPassport); + await dbContext.SaveChangesAsync(); + } + + var requestBody = new + { + data = new + { + type = "people", + id = person.Id, + relationships = new + { + passport = new + { + data = new + { + type = "passports", + id = newPassport.StringId + } + } + } + } + }; + + var route = $"/api/v1/people/{person.Id}"; + + var request = new HttpRequestMessage(HttpMethod.Patch, route); + + string requestText = JsonConvert.SerializeObject(requestBody); + request.Content = new StringContent(requestText); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Updating_ToOne_Relationship_Deletion() + { + // Arrange + var person = _fakers.Person.Generate(); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var passport = new Passport(dbContext) {IsLocked = true}; + person.Passport = passport; + dbContext.People.Add(person); + var newPassport = new Passport(dbContext); + dbContext.Passports.Add(newPassport); + await dbContext.SaveChangesAsync(); + } + + var requestBody = new + { + data = new + { + type = "people", + id = person.Id, + relationships = new + { + passport = new + { + data = (object) null + } + } + } + }; + + var route = $"/api/v1/people/{person.Id}"; + + var request = new HttpRequestMessage(HttpMethod.Patch, route); + + string requestText = JsonConvert.SerializeObject(requestBody); + request.Content = new StringContent(requestText); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Delete_ToOne_Relationship() + { + // Arrange + var lockedPerson = _fakers.Person.Generate(); + lockedPerson.IsLocked = true; + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var passport = new Passport(dbContext); + lockedPerson.Passport = passport; + dbContext.People.Add(lockedPerson); + await dbContext.SaveChangesAsync(); + } + + var route = $"/api/v1/passports/{lockedPerson.Passport.StringId}"; + + var request = new HttpRequestMessage(HttpMethod.Delete, route); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Create_ToMany_Relationship() + { + // Arrange + var persons = _fakers.Person.Generate(2); + var lockedTodo = _fakers.TodoItem.Generate(); + lockedTodo.IsLocked = true; + lockedTodo.StakeHolders = persons.ToHashSet(); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.TodoItems.Add(lockedTodo); + await dbContext.SaveChangesAsync(); + } + + var requestBody = new + { + data = new + { + type = "todoItems", + relationships = new + { + stakeHolders = new + { + data = new[] + { + new + { + type = "people", + id = persons[0].StringId + }, + new + { + type = "people", + id = persons[1].StringId + } + } + } + } + } + }; + + var route = "/api/v1/todoItems"; + + var request = new HttpRequestMessage(HttpMethod.Post, route); + + string requestText = JsonConvert.SerializeObject(requestBody); + request.Content = new StringContent(requestText); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Updating_ToMany_Relationship() + { + // Arrange + var persons = _fakers.Person.Generate(2); + + var lockedTodo = _fakers.TodoItem.Generate(); + lockedTodo.IsLocked = true; + lockedTodo.StakeHolders = persons.ToHashSet(); + + var unlockedTodo = _fakers.TodoItem.Generate(); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.TodoItems.AddRange(lockedTodo, unlockedTodo); + await dbContext.SaveChangesAsync(); + } + + var requestBody = new + { + data = new + { + type = "todoItems", + id = unlockedTodo.Id, + relationships = new + { + stakeHolders = new + { + data = new[] + { + new + { + type = "people", + id = persons[0].StringId + }, + new + { + type = "people", + id = persons[1].StringId + } + } + } + } + } + }; + + var route = $"/api/v1/todoItems/{unlockedTodo.Id}"; + + var request = new HttpRequestMessage(HttpMethod.Patch, route); + + string requestText = JsonConvert.SerializeObject(requestBody); + request.Content = new StringContent(requestText); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + [Fact] + public async Task Cascade_Permission_Error_Delete_ToMany_Relationship() + { + // Arrange + var persons = _fakers.Person.Generate(2); + var lockedTodo = _fakers.TodoItem.Generate(); + lockedTodo.IsLocked = true; + lockedTodo.StakeHolders = persons.ToHashSet(); + + using (var scope = _testContext.Factory.Services.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + + dbContext.TodoItems.Add(lockedTodo); + await dbContext.SaveChangesAsync(); + } + + var route = $"/api/v1/people/{persons[0].Id}"; + + var request = new HttpRequestMessage(HttpMethod.Delete, route); + + using var client = _testContext.Factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + var body = await response.Content.ReadAsStringAsync(); + var errorDocument = JsonConvert.DeserializeObject(body); + + errorDocument.Errors.Should().HaveCount(1); + errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + errorDocument.Errors[0].Detail.Should().BeNull(); + } + + private IRequestSerializer GetRequestSerializer(Expression> attributes = null, + Expression> relationships = null) + where TResource : class, IIdentifiable + { + var graph = _testContext.Factory.Services.GetRequiredService(); + + var serializer = _testContext.Factory.Services.GetRequiredService(); + serializer.AttributesToSerialize = attributes != null ? graph.GetAttributes(attributes) : null; + serializer.RelationshipsToSerialize = relationships != null ? graph.GetRelationships(relationships) : null; + return serializer; + } + + private IResponseDeserializer GetResponseDeserializer() + { + return _testContext.Factory.Services.GetRequiredService(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs new file mode 100644 index 0000000000..fdcfa09376 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -0,0 +1,36 @@ +using JsonApiDotNetCore.Configuration; +using Microsoft.AspNetCore.Authentication; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks +{ + public sealed class ResourceHooksStartup : TestableStartup + where TDbContext : DbContext + { + public ResourceHooksStartup(IConfiguration configuration) + : base(configuration) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + + base.ConfigureServices(services); + + services.AddControllersFromTestProject(); + services.AddClientSerialization(); + } + + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.Namespace = "api/v1"; + options.EnableResourceHooks = true; + options.LoadDatabaseValues = true; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs new file mode 100644 index 0000000000..f0b214af83 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCoreExample; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests +{ + public static class ServiceCollectionExtensions + { + public static void AddControllersFromTestProject(this IServiceCollection services) + { + var part = new AssemblyPart(typeof(EmptyStartup).Assembly); + services.AddMvcCore().ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part)); + } + } +} From 255ed36df6e9f4ab1af0f9962b410785f6b8389d Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Wed, 3 Feb 2021 01:14:32 +0100 Subject: [PATCH 15/47] General cleanup --- .../Controllers/PassportsController.cs | 2 +- .../Controllers/TagsController.cs | 2 +- .../Data/AppDbContext.cs | 3 -- .../Startups/Startup.cs | 24 ++++++---------- .../AppDbContextExtensions.cs | 22 --------------- .../HookFakers.cs => ExampleFakers.cs} | 7 +++-- .../{IntegrationTests => }/FakerContainer.cs | 2 +- .../PaginationWithTotalCountTests.cs | 26 ++++++++--------- .../PaginationWithoutTotalCountTests.cs | 12 ++++---- .../Pagination/RangeValidationTests.cs | 7 +++-- .../ResourceHooks/ResourceHookTests.cs | 4 +-- .../BlockingHttpDeleteController.cs | 2 +- .../BlockingHttpPatchController.cs | 2 +- .../BlockingHttpPostController.cs | 2 +- .../BlockingWritesController.cs | 2 +- .../IntegrationTests/Sorting/SortTests.cs | 15 +++------- .../SparseFieldSets/SparseFieldSetTests.cs | 28 ++++--------------- .../ObjectAssertionsExtensions.cs | 2 +- .../ServiceCollectionExtensions.cs | 2 +- .../{IntegrationTests => }/TestableStartup.cs | 2 +- .../BaseJsonApiController_Tests.cs | 2 +- 21 files changed, 58 insertions(+), 112 deletions(-) rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests/ResourceHooks/HookFakers.cs => ExampleFakers.cs} (94%) rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests => }/FakerContainer.cs (97%) rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests => }/ObjectAssertionsExtensions.cs (97%) rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests => }/ServiceCollectionExtensions.cs (89%) rename test/JsonApiDotNetCoreExampleTests/{IntegrationTests => }/TestableStartup.cs (95%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs index 62fa1e96c3..0a737184b7 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs @@ -8,7 +8,7 @@ namespace JsonApiDotNetCoreExample.Controllers { public sealed class PassportsController : JsonApiController { - public PassportsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService resourceService) + public PassportsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService resourceService) : base(options, loggerFactory, resourceService) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs index c06452cef7..154892df7a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs @@ -11,7 +11,7 @@ public sealed class TagsController : JsonApiController public TagsController( IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 41e21feecc..6107186144 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -12,13 +12,10 @@ public sealed class AppDbContext : DbContext public DbSet TodoItems { get; set; } public DbSet Passports { get; set; } public DbSet People { get; set; } - public DbSet TodoItemCollections { get; set; } public DbSet
Articles { get; set; } public DbSet AuthorDifferentDbContextName { get; set; } public DbSet Users { get; set; } - public DbSet PersonRoles { get; set; } public DbSet ArticleTags { get; set; } - public DbSet Tags { get; set; } public DbSet Blogs { get; set; } public AppDbContext(DbContextOptions options, ISystemClock systemClock) : base(options) diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs index 983f6a9dad..553ad27999 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs @@ -14,9 +14,11 @@ namespace JsonApiDotNetCoreExample { public class Startup : EmptyStartup { + private static readonly Version _postgresCiBuildVersion = new Version(9, 6); private readonly string _connectionString; - public Startup(IConfiguration configuration) : base(configuration) + public Startup(IConfiguration configuration) + : base(configuration) { string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); @@ -24,28 +26,18 @@ public Startup(IConfiguration configuration) : base(configuration) public override void ConfigureServices(IServiceCollection services) { - ConfigureClock(services); + services.AddSingleton(); services.AddDbContext(options => { options.EnableSensitiveDataLogging(); - options.UseNpgsql(_connectionString, innerOptions => innerOptions.SetPostgresVersion(new Version(9, 6))); - }, - // TODO: Remove ServiceLifetime.Transient, after all integration tests have been converted to use IntegrationTestContext. - ServiceLifetime.Transient); + options.UseNpgsql(_connectionString, postgresOptions => postgresOptions.SetPostgresVersion(_postgresCiBuildVersion)); + }); services.AddJsonApi(ConfigureJsonApiOptions, discovery => discovery.AddCurrentAssembly()); - - // once all tests have been moved to WebApplicationFactory format we can get rid of this line below - services.AddClientSerialization(); } - protected virtual void ConfigureClock(IServiceCollection services) - { - services.AddSingleton(); - } - - protected virtual void ConfigureJsonApiOptions(JsonApiOptions options) + protected void ConfigureJsonApiOptions(JsonApiOptions options) { options.IncludeExceptionStackTraceInErrors = true; options.Namespace = "api/v1"; @@ -63,7 +55,7 @@ public override void Configure(IApplicationBuilder app, IWebHostEnvironment envi var appDbContext = scope.ServiceProvider.GetRequiredService(); appDbContext.Database.EnsureCreated(); } - + app.UseRouting(); app.UseJsonApi(); app.UseEndpoints(endpoints => endpoints.MapControllers()); diff --git a/test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs b/test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs index a96642ad14..430cd60968 100644 --- a/test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs @@ -57,27 +57,5 @@ private static async Task ClearTablesAsync(this DbContext dbContext, params Type } } } - - public static void ClearTable(this DbContext dbContext) where TEntity : class - { - var entityType = dbContext.Model.FindEntityType(typeof(TEntity)); - if (entityType == null) - { - throw new InvalidOperationException($"Table for '{typeof(TEntity).Name}' not found."); - } - - string tableName = entityType.GetTableName(); - - // PERF: We first try to clear the table, which is fast and usually succeeds, unless foreign key constraints are violated. - // In that case, we recursively delete all related data, which is slow. - try - { - dbContext.Database.ExecuteSqlRaw("delete from \"" + tableName + "\""); - } - catch (PostgresException) - { - dbContext.Database.ExecuteSqlRaw("truncate table \"" + tableName + "\" cascade"); - } - } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs similarity index 94% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs rename to test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs index 1ffabb7c0a..28ed67a335 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/HookFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs @@ -2,12 +2,13 @@ using Bogus; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.Extensions.DependencyInjection; using Person = JsonApiDotNetCoreExample.Models.Person; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks +namespace JsonApiDotNetCoreExampleTests { - internal sealed class HookFakers : FakerContainer + internal sealed class ExampleFakers : FakerContainer { private readonly IServiceProvider _serviceProvider; @@ -58,7 +59,7 @@ internal sealed class HookFakers : FakerContainer public Faker Person => _lazyPersonFaker.Value; public Faker Tag => _lazyTagFaker.Value; - public HookFakers(IServiceProvider serviceProvider) + public ExampleFakers(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/FakerContainer.cs b/test/JsonApiDotNetCoreExampleTests/FakerContainer.cs similarity index 97% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/FakerContainer.cs rename to test/JsonApiDotNetCoreExampleTests/FakerContainer.cs index 50ce77c416..be07fe5851 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/FakerContainer.cs +++ b/test/JsonApiDotNetCoreExampleTests/FakerContainer.cs @@ -4,7 +4,7 @@ using System.Reflection; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests +namespace JsonApiDotNetCoreExampleTests { internal abstract class FakerContainer { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs index 00801c4690..d1ea71a2ea 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bogus; using FluentAssertions; using FluentAssertions.Extensions; using JsonApiDotNetCore.Configuration; @@ -12,7 +11,6 @@ using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination { @@ -21,7 +19,7 @@ public sealed class PaginationWithTotalCountTests : IClassFixture _testContext; - private readonly Faker _todoItemFaker = new Faker(); + private readonly ExampleFakers _fakers; public PaginationWithTotalCountTests(IntegrationTestContext testContext) { @@ -36,6 +34,8 @@ public PaginationWithTotalCountTests(IntegrationTestContext // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - Assert.Equal("http://localhost" + route, responseDocument.Links.Self); + responseDocument.Links.Self.Should().Be("http://localhost" + route); if (firstLink != null) { var expected = "http://localhost" + SetPageNumberInUrl(routePrefix, firstLink.Value); - Assert.Equal(expected, responseDocument.Links.First); + responseDocument.Links.First.Should().Be(expected); } else { - Assert.Null(responseDocument.Links.First); + responseDocument.Links.First.Should().BeNull(); } if (prevLink != null) { var expected = "http://localhost" + SetPageNumberInUrl(routePrefix, prevLink.Value); - Assert.Equal(expected, responseDocument.Links.Prev); + responseDocument.Links.Prev.Should().Be(expected); } else { - Assert.Null(responseDocument.Links.Prev); + responseDocument.Links.Prev.Should().BeNull(); } if (nextLink != null) { var expected = "http://localhost" + SetPageNumberInUrl(routePrefix, nextLink.Value); - Assert.Equal(expected, responseDocument.Links.Next); + responseDocument.Links.Next.Should().Be(expected); } else { - Assert.Null(responseDocument.Links.Next); + responseDocument.Links.Next.Should().BeNull(); } if (lastLink != null) { var expected = "http://localhost" + SetPageNumberInUrl(routePrefix, lastLink.Value); - Assert.Equal(expected, responseDocument.Links.Last); + responseDocument.Links.Last.Should().Be(expected); } else { - Assert.Null(responseDocument.Links.Last); + responseDocument.Links.Last.Should().BeNull(); } static string SetPageNumberInUrl(string url, int pageNumber) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs index 82c87d0429..2b9d71850d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs @@ -1,6 +1,5 @@ using System.Net; using System.Threading.Tasks; -using Bogus; using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; @@ -17,8 +16,7 @@ public sealed class PaginationWithoutTotalCountTests : IClassFixture _testContext; - - private readonly Faker
_articleFaker = new Faker
(); + private readonly ExampleFakers _fakers; public PaginationWithoutTotalCountTests(IntegrationTestContext testContext) { @@ -29,6 +27,8 @@ public PaginationWithoutTotalCountTests(IntegrationTestContext public async Task When_page_number_is_specified_in_query_string_with_partially_filled_page_it_should_render_pagination_links() { // Arrange - var articles = _articleFaker.Generate(12); + var articles = _fakers.Article.Generate(12); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -145,7 +145,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task When_page_number_is_specified_in_query_string_with_full_page_it_should_render_pagination_links() { // Arrange - var articles = _articleFaker.Generate(_defaultPageSize * 3); + var articles = _fakers.Article.Generate(_defaultPageSize * 3); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -179,7 +179,7 @@ public async Task When_page_number_is_specified_in_query_string_with_full_page_o // Arrange var author = new Author { - Articles = _articleFaker.Generate(_defaultPageSize * 3) + Articles = _fakers.Article.Generate(_defaultPageSize * 3) }; await _testContext.RunOnDatabaseAsync(async dbContext => diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs index 387eb8f99a..de65c323e2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs @@ -1,6 +1,5 @@ using System.Net; using System.Threading.Tasks; -using Bogus; using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; @@ -15,7 +14,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination public sealed class RangeValidationTests : IClassFixture> { private readonly IntegrationTestContext _testContext; - private readonly Faker _todoItemFaker = new Faker(); + private readonly ExampleFakers _fakers; private const int _defaultPageSize = 5; @@ -27,6 +26,8 @@ public RangeValidationTests(IntegrationTestContext testCo options.DefaultPageSize = new PageSize(_defaultPageSize); options.MaximumPageSize = null; options.MaximumPageNumber = null; + + _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] @@ -84,7 +85,7 @@ public async Task When_page_number_is_positive_it_must_succeed() public async Task When_page_number_is_too_high_it_must_return_empty_set_of_resources() { // Arrange - var todoItems = _todoItemFaker.Generate(3); + var todoItems = _fakers.TodoItem.Generate(3); await _testContext.RunOnDatabaseAsync(async dbContext => { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index b0b0214f05..e798e1a3c6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -24,7 +24,7 @@ public sealed class ResourceHookTests : IClassFixture, AppDbContext>> { private readonly IntegrationTestContext, AppDbContext> _testContext; - private readonly HookFakers _fakers; + private readonly ExampleFakers _fakers; public ResourceHookTests(IntegrationTestContext, AppDbContext> testContext) { @@ -43,7 +43,7 @@ public ResourceHookTests(IntegrationTestContext { public BlockingHttpDeleteController(IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs index 9edfdb07c5..4e9349be06 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPatchController.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers public sealed class BlockingHttpPatchController : JsonApiController { public BlockingHttpPatchController(IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs index 2dbaaadf80..18a0589559 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingHttpPostController.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers public sealed class BlockingHttpPostController : JsonApiController
{ public BlockingHttpPostController(IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService
resourceService) : base(options, loggerFactory, resourceService) { } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs index 21602e5487..a78da3511e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/BlockingWritesController.cs @@ -11,7 +11,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers public sealed class BlockingWritesController : JsonApiController { public BlockingWritesController(IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs index d37ff005f4..950e0a752c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bogus; using FluentAssertions; using FluentAssertions.Extensions; using JsonApiDotNetCore.Serialization.Objects; @@ -10,25 +9,19 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Xunit; -using Person = JsonApiDotNetCoreExample.Models.Person; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Sorting { public sealed class SortTests : IClassFixture> { private readonly IntegrationTestContext _testContext; - private readonly Faker
_articleFaker; - private readonly Faker _authorFaker; + private readonly ExampleFakers _fakers; public SortTests(IntegrationTestContext testContext) { _testContext = testContext; - _articleFaker = new Faker
() - .RuleFor(a => a.Caption, f => f.Random.AlphaNumeric(10)); - - _authorFaker = new Faker() - .RuleFor(a => a.LastName, f => f.Random.Words(2)); + _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] @@ -282,7 +275,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_in_scope_of_HasMany_relationship() { // Arrange - var author = _authorFaker.Generate(); + var author = _fakers.Author.Generate(); author.Articles = new List
{ new Article {Caption = "B"}, @@ -360,7 +353,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_in_scope_of_HasManyThrough_relationship() { // Arrange - var article = _articleFaker.Generate(); + var article = _fakers.Article.Generate(); article.ArticleTags = new HashSet { new ArticleTag diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs index 029ffe87ac..445788b4f1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bogus; using FluentAssertions; using FluentAssertions.Extensions; using JsonApiDotNetCore.Configuration; @@ -11,8 +10,6 @@ using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -21,9 +18,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets public sealed class SparseFieldSetTests : IClassFixture> { private readonly IntegrationTestContext _testContext; - private readonly Faker
_articleFaker; - private readonly Faker _authorFaker; - private readonly Faker _userFaker; + private readonly ExampleFakers _fakers; public SparseFieldSetTests(IntegrationTestContext testContext) { @@ -41,18 +36,7 @@ public SparseFieldSetTests(IntegrationTestContext testCon services.AddScoped, JsonApiResourceService
>(); }); - _articleFaker = new Faker
() - .RuleFor(a => a.Caption, f => f.Random.AlphaNumeric(10)); - - _authorFaker = new Faker() - .RuleFor(a => a.LastName, f => f.Random.Words(2)); - - var systemClock = testContext.Factory.Services.GetRequiredService(); - var options = testContext.Factory.Services.GetRequiredService>(); - var tempDbContext = new AppDbContext(options, systemClock); - - _userFaker = new Faker() - .CustomInstantiator(f => new User(tempDbContext)); + _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] @@ -282,7 +266,7 @@ public async Task Can_select_fields_of_HasOne_relationship() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var article = _articleFaker.Generate(); + var article = _fakers.Article.Generate(); article.Caption = "Some"; article.Author = new Author { @@ -338,7 +322,7 @@ public async Task Can_select_fields_of_HasMany_relationship() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var author = _authorFaker.Generate(); + var author = _fakers.Author.Generate(); author.LastName = "Smith"; author.Articles = new List
{ @@ -460,7 +444,7 @@ public async Task Can_select_fields_of_HasManyThrough_relationship() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var article = _articleFaker.Generate(); + var article = _fakers.Article.Generate(); article.Caption = "Some"; article.ArticleTags = new HashSet { @@ -724,7 +708,7 @@ public async Task Cannot_select_on_unknown_resource_type() public async Task Cannot_select_attribute_with_blocked_capability() { // Arrange - var user = _userFaker.Generate(); + var user = _fakers.User.Generate(); var route = $"/api/v1/users/{user.Id}?fields[users]=password"; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs b/test/JsonApiDotNetCoreExampleTests/ObjectAssertionsExtensions.cs similarity index 97% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs rename to test/JsonApiDotNetCoreExampleTests/ObjectAssertionsExtensions.cs index e5a6c4e1e1..db75f5edd8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ObjectAssertionsExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/ObjectAssertionsExtensions.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests +namespace JsonApiDotNetCoreExampleTests { public static class ObjectAssertionsExtensions { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs similarity index 89% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs rename to test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs index f0b214af83..93321b9897 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ServiceCollectionExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests +namespace JsonApiDotNetCoreExampleTests { public static class ServiceCollectionExtensions { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/TestableStartup.cs b/test/JsonApiDotNetCoreExampleTests/TestableStartup.cs similarity index 95% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/TestableStartup.cs rename to test/JsonApiDotNetCoreExampleTests/TestableStartup.cs index e69ed360b8..aaba661a65 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/TestableStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/TestableStartup.cs @@ -8,7 +8,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests +namespace JsonApiDotNetCoreExampleTests { public class TestableStartup : EmptyStartup where TDbContext : DbContext diff --git a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs index b11004d236..a2a63c638a 100644 --- a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs +++ b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs @@ -29,7 +29,7 @@ public sealed class ResourceController : BaseJsonApiController public ResourceController( IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } From c7000b803df9af168a4ed640bae5bd8d43b079a5 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 14:32:07 +0100 Subject: [PATCH 16/47] Extracted test building blocks in shared project --- JsonApiDotNetCore.sln | 15 ++ test/DiscoveryTests/DiscoveryTests.csproj | 2 +- .../ExampleFakers.cs | 1 - .../IntegrationTestContext.cs | 230 +--------------- .../JsonApiDotNetCoreExampleTests.csproj | 4 +- .../AbsoluteLinksInApiNamespaceStartup.cs | 1 - .../AbsoluteLinksNoNamespaceStartup.cs | 1 - .../Startups/ModelStateValidationStartup.cs | 1 - .../RelativeLinksInApiNamespaceStartup.cs | 1 - .../RelativeLinksNoNamespaceStartup.cs | 1 - .../{ => Startups}/TestableStartup.cs | 0 .../MultiDbContextTests.csproj | 3 +- .../NoEntityFrameworkTests.csproj | 2 +- .../AppDbContextExtensions.cs | 0 .../BaseIntegrationTestContext.cs | 248 ++++++++++++++++++ .../FakeLoggerFactory.cs | 14 +- .../FakerContainer.cs | 2 +- .../FrozenSystemClock.cs | 2 +- .../HttpResponseMessageExtensions.cs | 0 .../IntegrationTestConfiguration.cs | 2 +- .../NeverSameResourceChangeTracker.cs | 2 +- .../ObjectAssertionsExtensions.cs | 0 .../TestBuildingBlocks.csproj | 19 ++ .../EntityFrameworkCoreRepositoryTests.cs | 1 + .../IServiceCollectionExtensionsTests.cs | 1 + test/UnitTests/FakeLoggerFactory.cs | 41 --- test/UnitTests/FrozenSystemClock.cs | 20 -- .../Internal/ResourceGraphBuilderTests.cs | 6 +- .../ResourceConstructionExpressionTests.cs | 1 + .../Models/ResourceConstructionTests.cs | 1 + .../ResourceHooks/ResourceHooksTestsSetup.cs | 1 + test/UnitTests/UnitTests.csproj | 4 +- 32 files changed, 308 insertions(+), 319 deletions(-) rename test/JsonApiDotNetCoreExampleTests/{ => Startups}/TestableStartup.cs (100%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/AppDbContextExtensions.cs (100%) create mode 100644 test/TestBuildingBlocks/BaseIntegrationTestContext.cs rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/FakeLoggerFactory.cs (71%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/FakerContainer.cs (98%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/FrozenSystemClock.cs (84%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/HttpResponseMessageExtensions.cs (100%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/IntegrationTestConfiguration.cs (94%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/NeverSameResourceChangeTracker.cs (85%) rename test/{JsonApiDotNetCoreExampleTests => TestBuildingBlocks}/ObjectAssertionsExtensions.cs (100%) create mode 100644 test/TestBuildingBlocks/TestBuildingBlocks.csproj delete mode 100644 test/UnitTests/FakeLoggerFactory.cs delete mode 100644 test/UnitTests/FrozenSystemClock.cs diff --git a/JsonApiDotNetCore.sln b/JsonApiDotNetCore.sln index 0fc8aa3995..8c20805c1f 100644 --- a/JsonApiDotNetCore.sln +++ b/JsonApiDotNetCore.sln @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextExample", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test\MultiDbContextTests\MultiDbContextTests.csproj", "{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBuildingBlocks", "test\TestBuildingBlocks\TestBuildingBlocks.csproj", "{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -199,6 +201,18 @@ Global {EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x64.Build.0 = Release|Any CPU {EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.ActiveCfg = Release|Any CPU {EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.Build.0 = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|x64.ActiveCfg = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|x64.Build.0 = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|x86.ActiveCfg = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Debug|x86.Build.0 = Debug|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|Any CPU.Build.0 = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x64.ActiveCfg = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x64.Build.0 = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.ActiveCfg = Release|Any CPU + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,6 +230,7 @@ Global {21D27239-138D-4604-8E49-DCBE41BCE4C8} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF} {6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD} {EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} + {210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4} diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 94d67936e3..93223b9c41 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -11,11 +11,11 @@ + - \ No newline at end of file diff --git a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs index 28ed67a335..eb33e1a141 100644 --- a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs @@ -2,7 +2,6 @@ using Bogus; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.Extensions.DependencyInjection; using Person = JsonApiDotNetCoreExample.Models.Person; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs index ea0f41f279..0632e170ae 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs @@ -1,17 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using JsonApiDotNetCore.Middleware; using JsonApiDotNetCoreExample; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; namespace JsonApiDotNetCoreExampleTests { @@ -23,225 +11,9 @@ namespace JsonApiDotNetCoreExampleTests /// /// The server Startup class, which can be defined in the test project. /// The EF Core database context, which can be defined in the test project. - public class IntegrationTestContext : IDisposable + public class IntegrationTestContext : BaseIntegrationTestContext where TStartup : class where TDbContext : DbContext { - private readonly Lazy> _lazyFactory; - private Action _loggingConfiguration; - private Action _beforeServicesConfiguration; - private Action _afterServicesConfiguration; - - public WebApplicationFactory Factory => _lazyFactory.Value; - - public IntegrationTestContext() - { - _lazyFactory = new Lazy>(CreateFactory); - } - - private WebApplicationFactory CreateFactory() - { - string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; - string dbConnectionString = - $"Host=localhost;Port=5432;Database=JsonApiTest-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; - - var factory = new IntegrationTestWebApplicationFactory(); - - factory.ConfigureLogging(_loggingConfiguration); - - factory.ConfigureServicesBeforeStartup(services => - { - _beforeServicesConfiguration?.Invoke(services); - - services.AddDbContext(options => - { - options.UseNpgsql(dbConnectionString, - postgresOptions => postgresOptions.SetPostgresVersion(new Version(9, 6))); - - options.EnableSensitiveDataLogging(); - options.EnableDetailedErrors(); - }); - }); - - factory.ConfigureServicesAfterStartup(_afterServicesConfiguration); - - using IServiceScope scope = factory.Services.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Database.EnsureCreated(); - - return factory; - } - - public void Dispose() - { - RunOnDatabaseAsync(async context => await context.Database.EnsureDeletedAsync()).Wait(); - - Factory.Dispose(); - } - - public void ConfigureLogging(Action loggingConfiguration) - { - _loggingConfiguration = loggingConfiguration; - } - - public void ConfigureServicesBeforeStartup(Action servicesConfiguration) - { - _beforeServicesConfiguration = servicesConfiguration; - } - - public void ConfigureServicesAfterStartup(Action servicesConfiguration) - { - _afterServicesConfiguration = servicesConfiguration; - } - - public async Task RunOnDatabaseAsync(Func asyncAction) - { - using IServiceScope scope = Factory.Services.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - - await asyncAction(dbContext); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteGetAsync(string requestUrl, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecutePostAsync(string requestUrl, object requestBody, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, - acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecutePatchAsync(string requestUrl, object requestBody, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, - acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteDeleteAsync(string requestUrl, object requestBody = null, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, - acceptHeaders); - } - - private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteRequestAsync(HttpMethod method, string requestUrl, object requestBody, - string contentType, IEnumerable acceptHeaders) - { - var request = new HttpRequestMessage(method, requestUrl); - string requestText = SerializeRequest(requestBody); - - if (!string.IsNullOrEmpty(requestText)) - { - request.Content = new StringContent(requestText); - - if (contentType != null) - { - request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); - } - } - - using HttpClient client = Factory.CreateClient(); - - if (acceptHeaders != null) - { - foreach (var acceptHeader in acceptHeaders) - { - client.DefaultRequestHeaders.Accept.Add(acceptHeader); - } - } - - HttpResponseMessage responseMessage = await client.SendAsync(request); - - string responseText = await responseMessage.Content.ReadAsStringAsync(); - var responseDocument = DeserializeResponse(responseText); - - return (responseMessage, responseDocument); - } - - private string SerializeRequest(object requestBody) - { - return requestBody == null - ? null - : requestBody is string stringRequestBody - ? stringRequestBody - : JsonConvert.SerializeObject(requestBody); - } - - private TResponseDocument DeserializeResponse(string responseText) - { - if (typeof(TResponseDocument) == typeof(string)) - { - return (TResponseDocument)(object)responseText; - } - - try - { - return JsonConvert.DeserializeObject(responseText, - IntegrationTestConfiguration.DeserializationSettings); - } - catch (JsonException exception) - { - throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); - } - } - - private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory - { - private Action _loggingConfiguration; - private Action _beforeServicesConfiguration; - private Action _afterServicesConfiguration; - - public void ConfigureLogging(Action loggingConfiguration) - { - _loggingConfiguration = loggingConfiguration; - } - - public void ConfigureServicesBeforeStartup(Action servicesConfiguration) - { - _beforeServicesConfiguration = servicesConfiguration; - } - - public void ConfigureServicesAfterStartup(Action servicesConfiguration) - { - _afterServicesConfiguration = servicesConfiguration; - } - - protected override IHostBuilder CreateHostBuilder() - { - return Host.CreateDefaultBuilder(null) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureLogging(options => - { - _loggingConfiguration?.Invoke(options); - }); - - webBuilder.ConfigureServices(services => - { - _beforeServicesConfiguration?.Invoke(services); - }); - - webBuilder.UseStartup(); - - webBuilder.ConfigureServices(services => - { - _afterServicesConfiguration?.Invoke(services); - }); - }); - } - } } } diff --git a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj index e03f87d3e5..77e604e150 100644 --- a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj +++ b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj @@ -11,13 +11,11 @@ + - - - diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs index 1f71c8b9e5..a9950b3da7 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs @@ -1,5 +1,4 @@ using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs index 19b76f9146..636a84ca14 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/AbsoluteLinksNoNamespaceStartup.cs @@ -1,5 +1,4 @@ using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs index 8114edfb1c..8816d19f21 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/ModelStateValidationStartup.cs @@ -1,5 +1,4 @@ using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs index c38601a58e..f8e3b18814 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksInApiNamespaceStartup.cs @@ -1,5 +1,4 @@ using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs index b07f3d56c6..365f3454ca 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/RelativeLinksNoNamespaceStartup.cs @@ -1,5 +1,4 @@ using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCoreExampleTests.IntegrationTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/test/JsonApiDotNetCoreExampleTests/TestableStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs similarity index 100% rename from test/JsonApiDotNetCoreExampleTests/TestableStartup.cs rename to test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs diff --git a/test/MultiDbContextTests/MultiDbContextTests.csproj b/test/MultiDbContextTests/MultiDbContextTests.csproj index c319c91e7a..3e90eb5a54 100644 --- a/test/MultiDbContextTests/MultiDbContextTests.csproj +++ b/test/MultiDbContextTests/MultiDbContextTests.csproj @@ -11,13 +11,12 @@ + - - diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index f4f39b2be4..42964abb58 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -11,12 +11,12 @@ + - diff --git a/test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs b/test/TestBuildingBlocks/AppDbContextExtensions.cs similarity index 100% rename from test/JsonApiDotNetCoreExampleTests/AppDbContextExtensions.cs rename to test/TestBuildingBlocks/AppDbContextExtensions.cs diff --git a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs new file mode 100644 index 0000000000..0e179e8006 --- /dev/null +++ b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using JsonApiDotNetCore.Middleware; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace JsonApiDotNetCoreExampleTests +{ + /// + /// A test context that creates a new database and server instance before running tests and cleans up afterwards. + /// You can either use this as a fixture on your tests class (init/cleanup runs once before/after all tests) or + /// have your tests class inherit from it (init/cleanup runs once before/after each test). See + /// for details on shared context usage. + /// + /// The server Startup class, which can be defined in the test project. + /// The base class for , which MUST be defined in the API project. + /// The EF Core database context, which can be defined in the test project. + public abstract class BaseIntegrationTestContext : IDisposable + where TStartup : class + where TRemoteStartup : class + where TDbContext : DbContext + { + private readonly Lazy> _lazyFactory; + private Action _loggingConfiguration; + private Action _beforeServicesConfiguration; + private Action _afterServicesConfiguration; + + public WebApplicationFactory Factory => _lazyFactory.Value; + + protected BaseIntegrationTestContext() + { + _lazyFactory = new Lazy>(CreateFactory); + } + + private WebApplicationFactory CreateFactory() + { + string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; + string dbConnectionString = + $"Host=localhost;Port=5432;Database=JsonApiTest-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; + + var factory = new IntegrationTestWebApplicationFactory(); + + factory.ConfigureLogging(_loggingConfiguration); + + factory.ConfigureServicesBeforeStartup(services => + { + _beforeServicesConfiguration?.Invoke(services); + + services.AddDbContext(options => + { + options.UseNpgsql(dbConnectionString, + postgresOptions => postgresOptions.SetPostgresVersion(new Version(9, 6))); + + options.EnableSensitiveDataLogging(); + options.EnableDetailedErrors(); + }); + }); + + factory.ConfigureServicesAfterStartup(_afterServicesConfiguration); + + using IServiceScope scope = factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.EnsureCreated(); + + return factory; + } + + public void Dispose() + { + RunOnDatabaseAsync(async context => await context.Database.EnsureDeletedAsync()).Wait(); + + Factory.Dispose(); + } + + public void ConfigureLogging(Action loggingConfiguration) + { + _loggingConfiguration = loggingConfiguration; + } + + public void ConfigureServicesBeforeStartup(Action servicesConfiguration) + { + _beforeServicesConfiguration = servicesConfiguration; + } + + public void ConfigureServicesAfterStartup(Action servicesConfiguration) + { + _afterServicesConfiguration = servicesConfiguration; + } + + public async Task RunOnDatabaseAsync(Func asyncAction) + { + using IServiceScope scope = Factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await asyncAction(dbContext); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteGetAsync(string requestUrl, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecutePostAsync(string requestUrl, object requestBody, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, + acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecutePatchAsync(string requestUrl, object requestBody, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, + acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteDeleteAsync(string requestUrl, object requestBody = null, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, + acceptHeaders); + } + + private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteRequestAsync(HttpMethod method, string requestUrl, object requestBody, + string contentType, IEnumerable acceptHeaders) + { + var request = new HttpRequestMessage(method, requestUrl); + string requestText = SerializeRequest(requestBody); + + if (!string.IsNullOrEmpty(requestText)) + { + request.Content = new StringContent(requestText); + + if (contentType != null) + { + request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + } + + using HttpClient client = Factory.CreateClient(); + + if (acceptHeaders != null) + { + foreach (var acceptHeader in acceptHeaders) + { + client.DefaultRequestHeaders.Accept.Add(acceptHeader); + } + } + + HttpResponseMessage responseMessage = await client.SendAsync(request); + + string responseText = await responseMessage.Content.ReadAsStringAsync(); + var responseDocument = DeserializeResponse(responseText); + + return (responseMessage, responseDocument); + } + + private string SerializeRequest(object requestBody) + { + return requestBody == null + ? null + : requestBody is string stringRequestBody + ? stringRequestBody + : JsonConvert.SerializeObject(requestBody); + } + + private TResponseDocument DeserializeResponse(string responseText) + { + if (typeof(TResponseDocument) == typeof(string)) + { + return (TResponseDocument)(object)responseText; + } + + try + { + return JsonConvert.DeserializeObject(responseText, + IntegrationTestConfiguration.DeserializationSettings); + } + catch (JsonException exception) + { + throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); + } + } + + private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory + { + private Action _loggingConfiguration; + private Action _beforeServicesConfiguration; + private Action _afterServicesConfiguration; + + public void ConfigureLogging(Action loggingConfiguration) + { + _loggingConfiguration = loggingConfiguration; + } + + public void ConfigureServicesBeforeStartup(Action servicesConfiguration) + { + _beforeServicesConfiguration = servicesConfiguration; + } + + public void ConfigureServicesAfterStartup(Action servicesConfiguration) + { + _afterServicesConfiguration = servicesConfiguration; + } + + protected override IHostBuilder CreateHostBuilder() + { + return Host.CreateDefaultBuilder(null) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureLogging(options => + { + _loggingConfiguration?.Invoke(options); + }); + + webBuilder.ConfigureServices(services => + { + _beforeServicesConfiguration?.Invoke(services); + }); + + webBuilder.UseStartup(); + + webBuilder.ConfigureServices(services => + { + _afterServicesConfiguration?.Invoke(services); + }); + }); + } + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/FakeLoggerFactory.cs b/test/TestBuildingBlocks/FakeLoggerFactory.cs similarity index 71% rename from test/JsonApiDotNetCoreExampleTests/FakeLoggerFactory.cs rename to test/TestBuildingBlocks/FakeLoggerFactory.cs index 4fb18a008b..1dd7b6ea5b 100644 --- a/test/JsonApiDotNetCoreExampleTests/FakeLoggerFactory.cs +++ b/test/TestBuildingBlocks/FakeLoggerFactory.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreExampleTests { - internal sealed class FakeLoggerFactory : ILoggerFactory, ILoggerProvider + public sealed class FakeLoggerFactory : ILoggerFactory, ILoggerProvider { public FakeLogger Logger { get; } @@ -24,11 +24,11 @@ public void Dispose() { } - internal sealed class FakeLogger : ILogger + public sealed class FakeLogger : ILogger { - private readonly ConcurrentBag _messages = new ConcurrentBag(); + private readonly ConcurrentBag _messages = new ConcurrentBag(); - public IReadOnlyCollection Messages => _messages; + public IReadOnlyCollection Messages => _messages; public bool IsEnabled(LogLevel logLevel) => true; @@ -41,18 +41,18 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except Func formatter) { var message = formatter(state, exception); - _messages.Add(new LogMessage(logLevel, message)); + _messages.Add(new FakeLogMessage(logLevel, message)); } public IDisposable BeginScope(TState state) => null; } - internal sealed class LogMessage + public sealed class FakeLogMessage { public LogLevel LogLevel { get; } public string Text { get; } - public LogMessage(LogLevel logLevel, string text) + public FakeLogMessage(LogLevel logLevel, string text) { LogLevel = logLevel; Text = text; diff --git a/test/JsonApiDotNetCoreExampleTests/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs similarity index 98% rename from test/JsonApiDotNetCoreExampleTests/FakerContainer.cs rename to test/TestBuildingBlocks/FakerContainer.cs index be07fe5851..50dcab019e 100644 --- a/test/JsonApiDotNetCoreExampleTests/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreExampleTests { - internal abstract class FakerContainer + public abstract class FakerContainer { protected static int GetFakerSeed() { diff --git a/test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs b/test/TestBuildingBlocks/FrozenSystemClock.cs similarity index 84% rename from test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs rename to test/TestBuildingBlocks/FrozenSystemClock.cs index f08b81b905..e907d0f0b4 100644 --- a/test/JsonApiDotNetCoreExampleTests/FrozenSystemClock.cs +++ b/test/TestBuildingBlocks/FrozenSystemClock.cs @@ -3,7 +3,7 @@ namespace JsonApiDotNetCoreExampleTests { - internal sealed class FrozenSystemClock : ISystemClock + public sealed class FrozenSystemClock : ISystemClock { private static readonly DateTimeOffset _defaultTime = new DateTimeOffset(new DateTime(2000, 1, 1, 1, 1, 1), TimeSpan.FromHours(1)); diff --git a/test/JsonApiDotNetCoreExampleTests/HttpResponseMessageExtensions.cs b/test/TestBuildingBlocks/HttpResponseMessageExtensions.cs similarity index 100% rename from test/JsonApiDotNetCoreExampleTests/HttpResponseMessageExtensions.cs rename to test/TestBuildingBlocks/HttpResponseMessageExtensions.cs diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs b/test/TestBuildingBlocks/IntegrationTestConfiguration.cs similarity index 94% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs rename to test/TestBuildingBlocks/IntegrationTestConfiguration.cs index 7f92b3ee67..d60fc18bf6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTestConfiguration.cs +++ b/test/TestBuildingBlocks/IntegrationTestConfiguration.cs @@ -2,7 +2,7 @@ namespace JsonApiDotNetCoreExampleTests { - internal static class IntegrationTestConfiguration + public static class IntegrationTestConfiguration { // Because our tests often deserialize incoming responses into weakly-typed string-to-object dictionaries (as part of ResourceObject), // Newtonsoft.JSON is unable to infer the target type in such cases. So we steer a bit using explicit configuration. diff --git a/test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs b/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs similarity index 85% rename from test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs rename to test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs index b518811dfd..d8304fc1cf 100644 --- a/test/JsonApiDotNetCoreExampleTests/NeverSameResourceChangeTracker.cs +++ b/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreExampleTests /// /// Ensures the resource attributes are returned when creating/updating a resource. /// - internal sealed class NeverSameResourceChangeTracker : IResourceChangeTracker + public sealed class NeverSameResourceChangeTracker : IResourceChangeTracker where TResource : class, IIdentifiable { public void SetInitiallyStoredAttributeValues(TResource resource) diff --git a/test/JsonApiDotNetCoreExampleTests/ObjectAssertionsExtensions.cs b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs similarity index 100% rename from test/JsonApiDotNetCoreExampleTests/ObjectAssertionsExtensions.cs rename to test/TestBuildingBlocks/ObjectAssertionsExtensions.cs diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj new file mode 100644 index 0000000000..42f159dfa1 --- /dev/null +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -0,0 +1,19 @@ + + + $(NetCoreAppVersion) + + + + + + + + + + + + + + + + diff --git a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs index a80e5c076d..d17980d1c3 100644 --- a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs +++ b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs @@ -11,6 +11,7 @@ using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index b42e16e701..c78d83ad88 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -14,6 +14,7 @@ using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; diff --git a/test/UnitTests/FakeLoggerFactory.cs b/test/UnitTests/FakeLoggerFactory.cs deleted file mode 100644 index 999230e214..0000000000 --- a/test/UnitTests/FakeLoggerFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Logging; - -namespace UnitTests -{ - internal sealed class FakeLoggerFactory : ILoggerFactory, ILoggerProvider - { - public FakeLogger Logger { get; } - - public FakeLoggerFactory() - { - Logger = new FakeLogger(); - } - - public ILogger CreateLogger(string categoryName) => Logger; - - public void AddProvider(ILoggerProvider provider) - { - } - - public void Dispose() - { - } - - internal sealed class FakeLogger : ILogger - { - public List<(LogLevel LogLevel, string Text)> Messages = new List<(LogLevel, string)>(); - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, - Func formatter) - { - var message = formatter(state, exception); - Messages.Add((logLevel, message)); - } - - public bool IsEnabled(LogLevel logLevel) => true; - public IDisposable BeginScope(TState state) => null; - } - } -} diff --git a/test/UnitTests/FrozenSystemClock.cs b/test/UnitTests/FrozenSystemClock.cs deleted file mode 100644 index 218fe1cb54..0000000000 --- a/test/UnitTests/FrozenSystemClock.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Microsoft.AspNetCore.Authentication; - -namespace UnitTests -{ - internal class FrozenSystemClock : ISystemClock - { - public DateTimeOffset UtcNow { get; } - - public FrozenSystemClock() - : this(new DateTimeOffset(new DateTime(2000, 1, 1))) - { - } - - public FrozenSystemClock(DateTimeOffset utcNow) - { - UtcNow = utcNow; - } - } -} diff --git a/test/UnitTests/Internal/ResourceGraphBuilderTests.cs b/test/UnitTests/Internal/ResourceGraphBuilderTests.cs index c5350640db..be665afe09 100644 --- a/test/UnitTests/Internal/ResourceGraphBuilderTests.cs +++ b/test/UnitTests/Internal/ResourceGraphBuilderTests.cs @@ -1,6 +1,8 @@ +using System.Linq; using Castle.DynamicProxy; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; +using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -37,8 +39,8 @@ public void Adding_DbContext_Members_That_Do_Not_Implement_IIdentifiable_Logs_Wa // Assert Assert.Single(loggerFactory.Logger.Messages); - Assert.Equal(LogLevel.Warning, loggerFactory.Logger.Messages[0].LogLevel); - Assert.Equal("Entity 'UnitTests.Internal.ResourceGraphBuilderTests+TestContext' does not implement 'IIdentifiable'.", loggerFactory.Logger.Messages[0].Text); + Assert.Equal(LogLevel.Warning, loggerFactory.Logger.Messages.Single().LogLevel); + Assert.Equal("Entity 'UnitTests.Internal.ResourceGraphBuilderTests+TestContext' does not implement 'IIdentifiable'.", loggerFactory.Logger.Messages.Single().Text); } [Fact] diff --git a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs index 242ee0feef..b4663146cb 100644 --- a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs @@ -3,6 +3,7 @@ using System.Linq.Expressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Xunit; diff --git a/test/UnitTests/Models/ResourceConstructionTests.cs b/test/UnitTests/Models/ResourceConstructionTests.cs index 6fca6fc3ec..b21bec2641 100644 --- a/test/UnitTests/Models/ResourceConstructionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionTests.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization; using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index 017e61fcb1..d1b50b8ace 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -15,6 +15,7 @@ using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 7d4cc30981..f4773c8b2b 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -11,15 +11,13 @@ + - - - From db9778e2790bc2119b67f97fb3ba7911fb3d82e1 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 14:56:19 +0100 Subject: [PATCH 17/47] Auto-adjust namespaces --- test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs | 1 + test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs | 1 + .../IntegrationTests/CompositeKeys/CompositeKeyTests.cs | 1 + .../IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs | 1 + .../ContentNegotiation/ContentTypeHeaderTests.cs | 1 + .../ControllerActionResults/ActionResultTests.cs | 1 + .../CustomRoutes/ApiControllerAttributeTests.cs | 1 + .../IntegrationTests/CustomRoutes/CustomRouteFakers.cs | 1 + .../IntegrationTests/CustomRoutes/CustomRouteTests.cs | 1 + .../IntegrationTests/EagerLoading/EagerLoadingFakers.cs | 1 + .../IntegrationTests/EagerLoading/EagerLoadingTests.cs | 1 + .../IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs | 1 + .../IntegrationTests/Filtering/FilterDataTypeTests.cs | 1 + .../IntegrationTests/Filtering/FilterDepthTests.cs | 1 + .../IntegrationTests/Filtering/FilterOperatorTests.cs | 1 + .../IntegrationTests/Filtering/FilterTests.cs | 1 + .../IntegrationTests/IdObfuscation/IdObfuscationTests.cs | 1 + .../IntegrationTests/IdObfuscation/ObfuscationFakers.cs | 1 + .../IntegrationTests/Includes/IncludeTests.cs | 1 + .../IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs | 1 + .../Links/AbsoluteLinksWithoutNamespaceTests.cs | 1 + .../IntegrationTests/Links/LinksFakers.cs | 1 + .../IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs | 1 + .../Links/RelativeLinksWithoutNamespaceTests.cs | 1 + .../IntegrationTests/Logging/LoggingTests.cs | 1 + .../IntegrationTests/Meta/ResourceMetaTests.cs | 1 + .../IntegrationTests/Meta/ResponseMetaTests.cs | 1 + .../IntegrationTests/Meta/TopLevelCountTests.cs | 1 + .../ModelStateValidation/ModelStateValidationTests.cs | 1 + .../ModelStateValidation/NoModelStateValidationTests.cs | 1 + .../IntegrationTests/NamingConventions/KebabCasingTests.cs | 1 + .../IntegrationTests/NamingConventions/SwimmingFakers.cs | 1 + .../NonJsonApiControllers/NonJsonApiControllerTests.cs | 1 + .../Pagination/PaginationWithTotalCountTests.cs | 1 + .../Pagination/PaginationWithoutTotalCountTests.cs | 1 + .../IntegrationTests/Pagination/RangeValidationTests.cs | 1 + .../Pagination/RangeValidationWithMaximumTests.cs | 1 + .../IntegrationTests/QueryStrings/QueryStringFakers.cs | 1 + .../IntegrationTests/QueryStrings/QueryStringTests.cs | 1 + .../QueryStrings/SerializerDefaultValueHandlingTests.cs | 1 + .../QueryStrings/SerializerNullValueHandlingTests.cs | 1 + .../IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs | 1 + .../Creating/CreateResourceWithClientGeneratedIdTests.cs | 1 + .../Creating/CreateResourceWithToManyRelationshipTests.cs | 1 + .../Creating/CreateResourceWithToOneRelationshipTests.cs | 1 + .../IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs | 1 + .../ReadWrite/Fetching/FetchRelationshipTests.cs | 1 + .../IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs | 1 + .../IntegrationTests/ReadWrite/ReadWriteFakers.cs | 1 + .../Updating/Relationships/AddToToManyRelationshipTests.cs | 1 + .../Updating/Relationships/RemoveFromToManyRelationshipTests.cs | 1 + .../Updating/Relationships/ReplaceToManyRelationshipTests.cs | 1 + .../Updating/Relationships/UpdateToOneRelationshipTests.cs | 1 + .../Updating/Resources/ReplaceToManyRelationshipTests.cs | 1 + .../ReadWrite/Updating/Resources/UpdateResourceTests.cs | 1 + .../Updating/Resources/UpdateToOneRelationshipTests.cs | 1 + .../RequiredRelationships/DefaultBehaviorFakers.cs | 1 + .../RequiredRelationships/DefaultBehaviorTests.cs | 1 + .../ResourceConstructorInjection/InjectionFakers.cs | 1 + .../ResourceConstructorInjection/ResourceInjectionTests.cs | 1 + .../ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs | 1 + .../IntegrationTests/ResourceHooks/ResourceHookTests.cs | 1 + .../IntegrationTests/ResourceHooks/ResourceHooksStartup.cs | 1 + .../IntegrationTests/ResourceInheritance/InheritanceTests.cs | 1 + .../RestrictedControllers/DisableQueryStringTests.cs | 1 + .../IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs | 1 + .../IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs | 1 + .../IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs | 1 + .../IntegrationTests/RestrictedControllers/NoHttpPostTests.cs | 1 + .../IntegrationTests/RestrictedControllers/RestrictionFakers.cs | 1 + .../IntegrationTests/Serialization/SerializationFakers.cs | 1 + .../IntegrationTests/Serialization/SerializationTests.cs | 1 + .../IntegrationTests/SoftDeletion/SoftDeletionTests.cs | 1 + .../IntegrationTests/Sorting/SortTests.cs | 1 + .../IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs | 1 + .../IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs | 1 + .../IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs | 1 + .../IntegrationTests/ZeroKeys/ZeroKeyFakers.cs | 1 + test/TestBuildingBlocks/AppDbContextExtensions.cs | 2 +- test/TestBuildingBlocks/BaseIntegrationTestContext.cs | 2 +- test/TestBuildingBlocks/FakeLoggerFactory.cs | 2 +- test/TestBuildingBlocks/FakerContainer.cs | 2 +- test/TestBuildingBlocks/FrozenSystemClock.cs | 2 +- test/TestBuildingBlocks/HttpResponseMessageExtensions.cs | 2 +- test/TestBuildingBlocks/IntegrationTestConfiguration.cs | 2 +- test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs | 2 +- test/TestBuildingBlocks/ObjectAssertionsExtensions.cs | 2 +- test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs | 2 +- test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs | 2 +- test/UnitTests/Internal/ResourceGraphBuilderTests.cs | 2 +- test/UnitTests/Models/ResourceConstructionExpressionTests.cs | 2 +- test/UnitTests/Models/ResourceConstructionTests.cs | 2 +- test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs | 2 +- 93 files changed, 93 insertions(+), 15 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs index eb33e1a141..a8908ad93e 100644 --- a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs @@ -3,6 +3,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Person = JsonApiDotNetCoreExample.Models.Person; namespace JsonApiDotNetCoreExampleTests diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs index 0632e170ae..87e79eb419 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs @@ -1,5 +1,6 @@ using JsonApiDotNetCoreExample; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs index b55f9ab1b0..37a783410a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CompositeKeys diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs index 3a1019c249..7da9aea601 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ContentNegotiation diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs index 2cce83a8fe..fb8822a764 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ContentNegotiation diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs index 2b0adc0580..fdcf1fb8b1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs index 3a38991118..6b39d8fa69 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs index e57febd9c4..0fa5c5d008 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs index e708cb8b13..a416e84a21 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingFakers.cs index 6ca3157f55..80ae5c9d14 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.EagerLoading { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs index c86e8f5bd8..57369d0c45 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.EagerLoading diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs index 1c96c52dcd..1e0c61baa9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs index d3f062b99a..1ed25c4b3b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs @@ -8,6 +8,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs index e497ab68dd..23f4036358 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs @@ -10,6 +10,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs index fb38aeee8d..d7ed5030e8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs @@ -9,6 +9,7 @@ using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs index 6c4703f71d..f08231b2c3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs index e6c9226cc4..40bea53580 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.IdObfuscation diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/ObfuscationFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/ObfuscationFakers.cs index 4d31f063da..06fd5ac9ab 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/ObfuscationFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/ObfuscationFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.IdObfuscation { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs index f97e5e6a1c..5c3a0e447a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs @@ -11,6 +11,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Includes diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs index 692bc950df..9a027ab7f5 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs index 56949c9b99..451b1bf7c7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs index 852c825b3c..6e751dd00b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs index e22eef9ce1..2a1d7c604b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs index e80bc9b17b..6054ee0e66 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs index 61c99f6bb7..d2f72569c2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCoreExample.Data; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Logging diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs index ab33245072..30c415ff96 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs index 73991d509f..d13df8b731 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs @@ -8,6 +8,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs index df6affafb5..edd4ac6d7b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index 5ceb8d18ac..20024943c9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.Startups; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs index 33619cb477..b7de6d3098 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs index 20bda54a9f..997ebe9aa0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs index 71615bc567..8636078884 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/SwimmingFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs index e1e41af7be..768757e43f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCoreExample; using Microsoft.AspNetCore.Mvc.Testing; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NonJsonApiControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs index d1ea71a2ea..ac398c8bb0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs @@ -10,6 +10,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs index 2b9d71850d..ed3cf6a25c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs index de65c323e2..718aafa7cd 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs index a8ee41db71..ee44ec3ed5 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs index 3a14f8f64e..aa7857110f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs index 2084a8dadb..8b61692981 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs index ec9c5275eb..3768a8501d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs index 9487ae3b9e..30ed412203 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index da2ecd70d6..cf1be53b1e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -8,6 +8,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index 74d21e5e66..ea3037926a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index 50d94940a8..d84310876c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index 2001171eaf..16bf2623ce 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs index dc3887d2d9..82343a9a3f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Deleting diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs index 1ca3e6a512..7ce2d71484 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Fetching diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index 9d05882a92..0ccb85d25c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Fetching diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs index 66da7ee498..cab209819c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index 4b1c9e90ca..701322743e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index bc54d9d57a..e284e0edfc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index ad9883b5e1..e0f901c4d8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index d85d817e6f..fd9eeadad9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index 3290752b3b..c4d083d534 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index 2305fa532a..b44199bf14 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index f6661aad48..c489306d62 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorFakers.cs index 7c93242dcd..0363ddda5b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RequiredRelationships { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs index 26869d87c6..df033d8c5f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RequiredRelationships diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs index 6dbf85a9c3..35aea4b060 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs @@ -1,6 +1,7 @@ using System; using Bogus; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs index 17b3262114..dcec12f073 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs index 5a49ad59e5..d75a3a3774 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceDefinitions diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index e798e1a3c6..d08615ad66 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -16,6 +16,7 @@ using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs index fdcfa09376..c41f798a00 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs index f2c2c6001e..3f0fa157f2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceInheritance.Models; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceInheritance diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs index 15e3987762..f1dadcece0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.QueryStrings; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs index 2cefccbf8d..0ce068bf3d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs index d8160f1b60..3fecae6275 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs index 62484888d0..d972d2f429 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs index d8f5fda941..eefdda4aa6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs index 70d2dab8a3..b81d66dda5 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/RestrictionFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs index f884e23089..1bda09f4ae 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs index 62a402f564..e90f0e1b50 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs index 7aaf444c8c..874b1626fd 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SoftDeletion diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs index 950e0a752c..b0d60fed21 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs @@ -8,6 +8,7 @@ using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Sorting diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs index 445788b4f1..31ecebed2a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs @@ -11,6 +11,7 @@ using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs index 2e444baaf4..304d18b253 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ZeroKeys diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs index 5b58daf39f..fa58257c4a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ZeroKeys diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroKeyFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroKeyFakers.cs index 894f4d6482..300e76382b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroKeyFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroKeyFakers.cs @@ -1,5 +1,6 @@ using System; using Bogus; +using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ZeroKeys { diff --git a/test/TestBuildingBlocks/AppDbContextExtensions.cs b/test/TestBuildingBlocks/AppDbContextExtensions.cs index 430cd60968..e518d103c0 100644 --- a/test/TestBuildingBlocks/AppDbContextExtensions.cs +++ b/test/TestBuildingBlocks/AppDbContextExtensions.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; using Npgsql; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public static class AppDbContextExtensions { diff --git a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs index 0e179e8006..b0d61c1e59 100644 --- a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs +++ b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { /// /// A test context that creates a new database and server instance before running tests and cleans up afterwards. diff --git a/test/TestBuildingBlocks/FakeLoggerFactory.cs b/test/TestBuildingBlocks/FakeLoggerFactory.cs index 1dd7b6ea5b..f474e6a2e3 100644 --- a/test/TestBuildingBlocks/FakeLoggerFactory.cs +++ b/test/TestBuildingBlocks/FakeLoggerFactory.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public sealed class FakeLoggerFactory : ILoggerFactory, ILoggerProvider { diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs index 50dcab019e..d377d1d448 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -4,7 +4,7 @@ using System.Reflection; using Xunit; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public abstract class FakerContainer { diff --git a/test/TestBuildingBlocks/FrozenSystemClock.cs b/test/TestBuildingBlocks/FrozenSystemClock.cs index e907d0f0b4..91a361624e 100644 --- a/test/TestBuildingBlocks/FrozenSystemClock.cs +++ b/test/TestBuildingBlocks/FrozenSystemClock.cs @@ -1,7 +1,7 @@ using System; using Microsoft.AspNetCore.Authentication; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public sealed class FrozenSystemClock : ISystemClock { diff --git a/test/TestBuildingBlocks/HttpResponseMessageExtensions.cs b/test/TestBuildingBlocks/HttpResponseMessageExtensions.cs index 8f550a4722..c8be07454a 100644 --- a/test/TestBuildingBlocks/HttpResponseMessageExtensions.cs +++ b/test/TestBuildingBlocks/HttpResponseMessageExtensions.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public static class HttpResponseMessageExtensions { diff --git a/test/TestBuildingBlocks/IntegrationTestConfiguration.cs b/test/TestBuildingBlocks/IntegrationTestConfiguration.cs index d60fc18bf6..32014a9630 100644 --- a/test/TestBuildingBlocks/IntegrationTestConfiguration.cs +++ b/test/TestBuildingBlocks/IntegrationTestConfiguration.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public static class IntegrationTestConfiguration { diff --git a/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs b/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs index d8304fc1cf..a98fd5a8e2 100644 --- a/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs +++ b/test/TestBuildingBlocks/NeverSameResourceChangeTracker.cs @@ -1,6 +1,6 @@ using JsonApiDotNetCore.Resources; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { /// /// Ensures the resource attributes are returned when creating/updating a resource. diff --git a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs index db75f5edd8..d13759e1b6 100644 --- a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs +++ b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace JsonApiDotNetCoreExampleTests +namespace TestBuildingBlocks { public static class ObjectAssertionsExtensions { diff --git a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs index d17980d1c3..0d8b177ddc 100644 --- a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs +++ b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs @@ -11,11 +11,11 @@ using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using Moq; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Data diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index c78d83ad88..bc6318b774 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -14,11 +14,11 @@ using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Extensions diff --git a/test/UnitTests/Internal/ResourceGraphBuilderTests.cs b/test/UnitTests/Internal/ResourceGraphBuilderTests.cs index be665afe09..ba1fc670b1 100644 --- a/test/UnitTests/Internal/ResourceGraphBuilderTests.cs +++ b/test/UnitTests/Internal/ResourceGraphBuilderTests.cs @@ -2,10 +2,10 @@ using Castle.DynamicProxy; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; -using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Internal diff --git a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs index b4663146cb..71a4f9642c 100644 --- a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs @@ -3,9 +3,9 @@ using System.Linq.Expressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Models diff --git a/test/UnitTests/Models/ResourceConstructionTests.cs b/test/UnitTests/Models/ResourceConstructionTests.cs index b21bec2641..bfae197a83 100644 --- a/test/UnitTests/Models/ResourceConstructionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionTests.cs @@ -5,12 +5,12 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization; using JsonApiDotNetCoreExample.Data; -using JsonApiDotNetCoreExampleTests; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Newtonsoft.Json; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Models diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index d1b50b8ace..bcfdba7cf1 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -15,11 +15,11 @@ using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCoreExampleTests; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using Moq; +using TestBuildingBlocks; using Person = JsonApiDotNetCoreExample.Models.Person; namespace UnitTests.ResourceHooks From d9a2ac7863c0faf2d4ad28fcab9e6aad5c999463 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 15:08:51 +0100 Subject: [PATCH 18/47] Refactored tests for service discovery --- .../ServiceDiscoveryFacadeTests.cs | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index 4c8141f974..6845430880 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Hooks; using JsonApiDotNetCore.Middleware; @@ -47,10 +48,10 @@ public ServiceDiscoveryFacadeTests() } [Fact] - public void DiscoverResources_Adds_Resources_From_Added_Assembly_To_Graph() + public void Can_add_resources_from_assembly_to_graph() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddAssembly(typeof(Person).Assembly); // Act @@ -58,17 +59,19 @@ public void DiscoverResources_Adds_Resources_From_Added_Assembly_To_Graph() // Assert var resourceGraph = _resourceGraphBuilder.Build(); + var personResource = resourceGraph.GetResourceContext(typeof(Person)); + personResource.Should().NotBeNull(); + var articleResource = resourceGraph.GetResourceContext(typeof(Article)); - Assert.NotNull(personResource); - Assert.NotNull(articleResource); + articleResource.Should().NotBeNull(); } [Fact] - public void DiscoverResources_Adds_Resources_From_Current_Assembly_To_Graph() + public void Can_add_resource_from_current_assembly_to_graph() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddCurrentAssembly(); // Act @@ -76,15 +79,16 @@ public void DiscoverResources_Adds_Resources_From_Current_Assembly_To_Graph() // Assert var resourceGraph = _resourceGraphBuilder.Build(); - var testModelResource = resourceGraph.GetResourceContext(typeof(TestModel)); - Assert.NotNull(testModelResource); + + var resource = resourceGraph.GetResourceContext(typeof(TestResource)); + resource.Should().NotBeNull(); } [Fact] - public void DiscoverInjectables_Adds_Resource_Services_From_Current_Assembly_To_Container() + public void Can_add_resource_service_from_current_assembly_to_container() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddCurrentAssembly(); // Act @@ -92,15 +96,16 @@ public void DiscoverInjectables_Adds_Resource_Services_From_Current_Assembly_To_ // Assert var services = _services.BuildServiceProvider(); - var service = services.GetRequiredService>(); - Assert.IsType(service); + + var resourceService = services.GetRequiredService>(); + resourceService.Should().BeOfType(); } [Fact] - public void DiscoverInjectables_Adds_Resource_Repositories_From_Current_Assembly_To_Container() + public void Can_add_resource_repository_from_current_assembly_to_container() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddCurrentAssembly(); // Act @@ -108,14 +113,16 @@ public void DiscoverInjectables_Adds_Resource_Repositories_From_Current_Assembly // Assert var services = _services.BuildServiceProvider(); - Assert.IsType(services.GetRequiredService>()); + + var resourceRepository = services.GetRequiredService>(); + resourceRepository.Should().BeOfType(); } [Fact] - public void AddCurrentAssembly_Adds_Resource_Definitions_From_Current_Assembly_To_Container() + public void Can_add_resource_definition_from_current_assembly_to_container() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddCurrentAssembly(); // Act @@ -123,14 +130,16 @@ public void AddCurrentAssembly_Adds_Resource_Definitions_From_Current_Assembly_T // Assert var services = _services.BuildServiceProvider(); - Assert.IsType(services.GetRequiredService>()); + + var resourceDefinition = services.GetRequiredService>(); + resourceDefinition.Should().BeOfType(); } [Fact] - public void AddCurrentAssembly_Adds_Resource_Hooks_Definitions_From_Current_Assembly_To_Container() + public void Can_add_resource_hooks_definition_from_current_assembly_to_container() { // Arrange - ServiceDiscoveryFacade facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); + var facade = new ServiceDiscoveryFacade(_services, _resourceGraphBuilder, _options, _loggerFactory); facade.AddCurrentAssembly(); _options.EnableResourceHooks = true; @@ -140,21 +149,23 @@ public void AddCurrentAssembly_Adds_Resource_Hooks_Definitions_From_Current_Asse // Assert var services = _services.BuildServiceProvider(); - Assert.IsType(services.GetRequiredService>()); + + var resourceHooksDefinition = services.GetRequiredService>(); + resourceHooksDefinition.Should().BeOfType(); } - public sealed class TestModel : Identifiable { } + public sealed class TestResource : Identifiable { } - public class TestModelService : JsonApiResourceService + public class TestResourceService : JsonApiResourceService { - public TestModelService( + public TestResourceService( IResourceRepositoryAccessor repositoryAccessor, IQueryLayerComposer queryLayerComposer, IPaginationContext paginationContext, IJsonApiOptions options, ILoggerFactory loggerFactory, IJsonApiRequest request, - IResourceChangeTracker resourceChangeTracker, + IResourceChangeTracker resourceChangeTracker, IResourceHookExecutorFacade hookExecutor) : base(repositoryAccessor, queryLayerComposer, paginationContext, options, loggerFactory, request, resourceChangeTracker, hookExecutor) @@ -162,9 +173,9 @@ public TestModelService( } } - public class TestModelRepository : EntityFrameworkCoreRepository + public class TestResourceRepository : EntityFrameworkCoreRepository { - public TestModelRepository( + public TestResourceRepository( ITargetedFields targetedFields, IDbContextResolver contextResolver, IResourceGraph resourceGraph, @@ -175,14 +186,14 @@ public TestModelRepository( { } } - public class TestModelResourceHooksDefinition : ResourceHooksDefinition + public class TestResourceHooksDefinition : ResourceHooksDefinition { - public TestModelResourceHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TestResourceHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } } - public class TestModelResourceDefinition : JsonApiResourceDefinition + public class TestResourceDefinition : JsonApiResourceDefinition { - public TestModelResourceDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TestResourceDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } } } } From 53f8c82aca4bfc161979368646ced3e87e525636 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 15:30:06 +0100 Subject: [PATCH 19/47] Refactored tests for no EF Core --- test/NoEntityFrameworkTests/WorkItemTests.cs | 113 ++++++------------ .../RemoteIntegrationTestContext.cs | 18 +++ 2 files changed, 52 insertions(+), 79 deletions(-) create mode 100644 test/TestBuildingBlocks/RemoteIntegrationTestContext.cs diff --git a/test/NoEntityFrameworkTests/WorkItemTests.cs b/test/NoEntityFrameworkTests/WorkItemTests.cs index f118483b76..f616691af3 100644 --- a/test/NoEntityFrameworkTests/WorkItemTests.cs +++ b/test/NoEntityFrameworkTests/WorkItemTests.cs @@ -1,167 +1,122 @@ using System; using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; -using JsonApiDotNetCore.Middleware; +using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using NoEntityFrameworkExample; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; +using TestBuildingBlocks; using Xunit; namespace NoEntityFrameworkTests { - public sealed class WorkItemTests : IClassFixture> + public sealed class WorkItemTests : IClassFixture> { - private readonly WebApplicationFactory _factory; + private readonly RemoteIntegrationTestContext _testContext; - public WorkItemTests(WebApplicationFactory factory) + public WorkItemTests(RemoteIntegrationTestContext testContext) { - _factory = factory; + _testContext = testContext; } [Fact] - public async Task Can_Get_WorkItems() + public async Task Can_get_WorkItems() { // Arrange - await ExecuteOnDbContextAsync(async dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(new WorkItem()); await dbContext.SaveChangesAsync(); }); - var client = _factory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/workItems"); + var route = "/api/v1/workItems"; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - AssertStatusCode(HttpStatusCode.OK, response); - - string responseBody = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseBody); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - Assert.NotEmpty(document.ManyData); + responseDocument.ManyData.Should().NotBeEmpty(); } [Fact] - public async Task Can_Get_WorkItem_By_Id() + public async Task Can_get_WorkItem_by_ID() { // Arrange var workItem = new WorkItem(); - await ExecuteOnDbContextAsync(async dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); await dbContext.SaveChangesAsync(); }); - var client = _factory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/workItems/" + workItem.StringId); + var route = "/api/v1/workItems/" + workItem.StringId; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - AssertStatusCode(HttpStatusCode.OK, response); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - string responseBody = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseBody); - - Assert.NotNull(document.SingleData); - Assert.Equal(workItem.StringId, document.SingleData.Id); + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Id.Should().Be(workItem.StringId); } [Fact] - public async Task Can_Create_WorkItem() + public async Task Can_create_WorkItem() { // Arrange - var title = Guid.NewGuid().ToString(); + var newTitle = Guid.NewGuid().ToString(); - var requestContent = new + var requestBody = new { data = new { type = "workItems", attributes = new { - title, + title = newTitle, ordinal = 1 } } }; - var requestBody = JsonConvert.SerializeObject(requestContent); - - var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/workItems/") - { - Content = new StringContent(requestBody) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - var client = _factory.CreateClient(); + var route = "/api/v1/workItems/"; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); // Assert - AssertStatusCode(HttpStatusCode.Created, response); - - string responseBody = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseBody); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); - Assert.NotNull(document.SingleData); - Assert.Equal(title, document.SingleData.Attributes["title"]); + responseDocument.SingleData.Should().NotBeNull(); + responseDocument.SingleData.Attributes["title"].Should().Be(newTitle); } [Fact] - public async Task Can_Delete_WorkItem() + public async Task Can_delete_WorkItem() { // Arrange var workItem = new WorkItem(); - await ExecuteOnDbContextAsync(async dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); await dbContext.SaveChangesAsync(); }); - var client = _factory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Delete, "/api/v1/workItems/" + workItem.StringId); + var route = "/api/v1/workItems/" + workItem.StringId; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); // Assert - AssertStatusCode(HttpStatusCode.NoContent, response); - - string responseBody = await response.Content.ReadAsStringAsync(); - Assert.Empty(responseBody); - } - - private async Task ExecuteOnDbContextAsync(Func asyncAction) - { - using IServiceScope scope = _factory.Services.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); + httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); - await asyncAction(dbContext); - } - - private static void AssertStatusCode(HttpStatusCode expected, HttpResponseMessage response) - { - if (expected != response.StatusCode) - { - var responseBody = response.Content.ReadAsStringAsync().Result; - Assert.True(expected == response.StatusCode, $"Got {response.StatusCode} status code instead of {expected}. Response body: {responseBody}"); - } + responseDocument.Should().BeEmpty(); } } } diff --git a/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs b/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs new file mode 100644 index 0000000000..d9555737d9 --- /dev/null +++ b/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; + +namespace TestBuildingBlocks +{ + /// + /// A test context that creates a new database and server instance before running tests and cleans up afterwards. + /// You can either use this as a fixture on your tests class (init/cleanup runs once before/after all tests) or + /// have your tests class inherit from it (init/cleanup runs once before/after each test). See + /// for details on shared context usage. + /// + /// The server Startup class, which MUST be defined in the API project. + /// The EF Core database context, which MUST be defined in the API project. + public class RemoteIntegrationTestContext : BaseIntegrationTestContext + where TStartup : class + where TDbContext : DbContext + { + } +} From 86bac20f1beb9e97312fe4675d80af7c8bc9cbec Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 15:48:56 +0100 Subject: [PATCH 20/47] Renamed IntegrationTestContext to ExampleIntegrationTestContext because it has a strong dependency on EmptyStartup from JsonApiDotNetCoreExamples project. --- .../ExampleIntegrationTestContext.cs | 17 ++++++++++++++++ .../IntegrationTestContext.cs | 20 ------------------- .../CompositeKeys/CompositeKeyTests.cs | 6 +++--- .../ContentNegotiation/AcceptHeaderTests.cs | 6 +++--- .../ContentTypeHeaderTests.cs | 6 +++--- .../ActionResultTests.cs | 6 +++--- .../ApiControllerAttributeTests.cs | 6 +++--- .../CustomRoutes/CustomRouteTests.cs | 6 +++--- .../EagerLoading/EagerLoadingTests.cs | 6 +++--- .../ExceptionHandlerTests.cs | 6 +++--- .../Filtering/FilterDataTypeTests.cs | 6 +++--- .../Filtering/FilterDepthTests.cs | 6 +++--- .../Filtering/FilterOperatorTests.cs | 6 +++--- .../IntegrationTests/Filtering/FilterTests.cs | 6 +++--- .../IdObfuscation/IdObfuscationTests.cs | 6 +++--- .../IntegrationTests/Includes/IncludeTests.cs | 6 +++--- .../Links/AbsoluteLinksWithNamespaceTests.cs | 6 +++--- .../AbsoluteLinksWithoutNamespaceTests.cs | 6 +++--- .../Links/RelativeLinksWithNamespaceTests.cs | 6 +++--- .../RelativeLinksWithoutNamespaceTests.cs | 6 +++--- .../IntegrationTests/Logging/LoggingTests.cs | 6 +++--- .../Meta/ResourceMetaTests.cs | 6 +++--- .../Meta/ResponseMetaTests.cs | 6 +++--- .../Meta/TopLevelCountTests.cs | 6 +++--- .../ModelStateValidationTests.cs | 6 +++--- .../NoModelStateValidationTests.cs | 6 +++--- .../NamingConventions/KebabCasingTests.cs | 6 +++--- .../PaginationWithTotalCountTests.cs | 6 +++--- .../PaginationWithoutTotalCountTests.cs | 6 +++--- .../Pagination/RangeValidationTests.cs | 6 +++--- .../RangeValidationWithMaximumTests.cs | 6 +++--- .../QueryStrings/QueryStringTests.cs | 6 +++--- .../SerializerDefaultValueHandlingTests.cs | 6 +++--- .../SerializerNullValueHandlingTests.cs | 6 +++--- .../ReadWrite/Creating/CreateResourceTests.cs | 6 +++--- ...reateResourceWithClientGeneratedIdTests.cs | 6 +++--- ...eateResourceWithToManyRelationshipTests.cs | 6 +++--- ...reateResourceWithToOneRelationshipTests.cs | 6 +++--- .../ReadWrite/Deleting/DeleteResourceTests.cs | 6 +++--- .../Fetching/FetchRelationshipTests.cs | 6 +++--- .../ReadWrite/Fetching/FetchResourceTests.cs | 6 +++--- .../AddToToManyRelationshipTests.cs | 6 +++--- .../RemoveFromToManyRelationshipTests.cs | 6 +++--- .../ReplaceToManyRelationshipTests.cs | 6 +++--- .../UpdateToOneRelationshipTests.cs | 6 +++--- .../ReplaceToManyRelationshipTests.cs | 6 +++--- .../Updating/Resources/UpdateResourceTests.cs | 6 +++--- .../Resources/UpdateToOneRelationshipTests.cs | 6 +++--- .../DefaultBehaviorTests.cs | 6 +++--- .../ResourceInjectionTests.cs | 6 +++--- .../ResourceDefinitionQueryCallbackTests.cs | 6 +++--- .../ResourceHooks/ResourceHookTests.cs | 6 +++--- .../ResourceHooks/ResourceHooksStartup.cs | 2 +- .../ResourceInheritance/InheritanceTests.cs | 6 +++--- .../DisableQueryStringTests.cs | 6 +++--- .../HttpReadOnlyTests.cs | 6 +++--- .../NoHttpDeleteTests.cs | 6 +++--- .../RestrictedControllers/NoHttpPatchTests.cs | 6 +++--- .../RestrictedControllers/NoHttpPostTests.cs | 6 +++--- .../Serialization/SerializationTests.cs | 6 +++--- .../SoftDeletion/SoftDeletionTests.cs | 6 +++--- .../IntegrationTests/Sorting/SortTests.cs | 6 +++--- .../SparseFieldSets/SparseFieldSetTests.cs | 6 +++--- .../ZeroKeys/EmptyGuidAsKeyTests.cs | 6 +++--- .../ZeroKeys/ZeroAsKeyTests.cs | 6 +++--- .../ServiceCollectionExtensions.cs | 2 +- .../BaseIntegrationTestContext.cs | 2 +- 67 files changed, 206 insertions(+), 209 deletions(-) create mode 100644 test/JsonApiDotNetCoreExampleTests/ExampleIntegrationTestContext.cs delete mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs diff --git a/test/JsonApiDotNetCoreExampleTests/ExampleIntegrationTestContext.cs b/test/JsonApiDotNetCoreExampleTests/ExampleIntegrationTestContext.cs new file mode 100644 index 0000000000..249b61cad4 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/ExampleIntegrationTestContext.cs @@ -0,0 +1,17 @@ +using JsonApiDotNetCoreExample; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace JsonApiDotNetCoreExampleTests +{ + /// + /// A test context for tests that reference the JsonApiDotNetCoreExample project. + /// + /// The server Startup class, which can be defined in the test project. + /// The EF Core database context, which can be defined in the test project. + public class ExampleIntegrationTestContext : BaseIntegrationTestContext + where TStartup : class + where TDbContext : DbContext + { + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs deleted file mode 100644 index 87e79eb419..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTestContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using JsonApiDotNetCoreExample; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace JsonApiDotNetCoreExampleTests -{ - /// - /// A test context that creates a new database and server instance before running tests and cleans up afterwards. - /// You can either use this as a fixture on your tests class (init/cleanup runs once before/after all tests) or - /// have your tests class inherit from it (init/cleanup runs once before/after each test). See - /// for details on shared context usage. - /// - /// The server Startup class, which can be defined in the test project. - /// The EF Core database context, which can be defined in the test project. - public class IntegrationTestContext : BaseIntegrationTestContext - where TStartup : class - where TDbContext : DbContext - { - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs index 37a783410a..c177137cd7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs @@ -13,11 +13,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CompositeKeys { public sealed class CompositeKeyTests - : IClassFixture, CompositeDbContext>> + : IClassFixture, CompositeDbContext>> { - private readonly IntegrationTestContext, CompositeDbContext> _testContext; + private readonly ExampleIntegrationTestContext, CompositeDbContext> _testContext; - public CompositeKeyTests(IntegrationTestContext, CompositeDbContext> testContext) + public CompositeKeyTests(ExampleIntegrationTestContext, CompositeDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs index 7da9aea601..0169c316b5 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs @@ -10,11 +10,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ContentNegotiation { public sealed class AcceptHeaderTests - : IClassFixture, PolicyDbContext>> + : IClassFixture, PolicyDbContext>> { - private readonly IntegrationTestContext, PolicyDbContext> _testContext; + private readonly ExampleIntegrationTestContext, PolicyDbContext> _testContext; - public AcceptHeaderTests(IntegrationTestContext, PolicyDbContext> testContext) + public AcceptHeaderTests(ExampleIntegrationTestContext, PolicyDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs index fb8822a764..14367717ad 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs @@ -9,11 +9,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ContentNegotiation { public sealed class ContentTypeHeaderTests - : IClassFixture, PolicyDbContext>> + : IClassFixture, PolicyDbContext>> { - private readonly IntegrationTestContext, PolicyDbContext> _testContext; + private readonly ExampleIntegrationTestContext, PolicyDbContext> _testContext; - public ContentTypeHeaderTests(IntegrationTestContext, PolicyDbContext> testContext) + public ContentTypeHeaderTests(ExampleIntegrationTestContext, PolicyDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs index fdcf1fb8b1..fa6ccebf05 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs @@ -8,11 +8,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults { public sealed class ActionResultTests - : IClassFixture, ActionResultDbContext>> + : IClassFixture, ActionResultDbContext>> { - private readonly IntegrationTestContext, ActionResultDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ActionResultDbContext> _testContext; - public ActionResultTests(IntegrationTestContext, ActionResultDbContext> testContext) + public ActionResultTests(ExampleIntegrationTestContext, ActionResultDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs index 6b39d8fa69..4a3237ee9e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs @@ -8,11 +8,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes { public sealed class ApiControllerAttributeTests - : IClassFixture, CustomRouteDbContext>> + : IClassFixture, CustomRouteDbContext>> { - private readonly IntegrationTestContext, CustomRouteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, CustomRouteDbContext> _testContext; - public ApiControllerAttributeTests(IntegrationTestContext, CustomRouteDbContext> testContext) + public ApiControllerAttributeTests(ExampleIntegrationTestContext, CustomRouteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs index a416e84a21..5028008eab 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -9,12 +9,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.CustomRoutes { public sealed class CustomRouteTests - : IClassFixture, CustomRouteDbContext>> + : IClassFixture, CustomRouteDbContext>> { - private readonly IntegrationTestContext, CustomRouteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, CustomRouteDbContext> _testContext; private readonly CustomRouteFakers _fakers = new CustomRouteFakers(); - public CustomRouteTests(IntegrationTestContext, CustomRouteDbContext> testContext) + public CustomRouteTests(ExampleIntegrationTestContext, CustomRouteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs index 57369d0c45..1dbe44d277 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.EagerLoading { public sealed class EagerLoadingTests - : IClassFixture, EagerLoadingDbContext>> + : IClassFixture, EagerLoadingDbContext>> { - private readonly IntegrationTestContext, EagerLoadingDbContext> _testContext; + private readonly ExampleIntegrationTestContext, EagerLoadingDbContext> _testContext; private readonly EagerLoadingFakers _fakers = new EagerLoadingFakers(); - public EagerLoadingTests(IntegrationTestContext, EagerLoadingDbContext> testContext) + public EagerLoadingTests(ExampleIntegrationTestContext, EagerLoadingDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs index 1e0c61baa9..a03f9060aa 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -14,11 +14,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling { public sealed class ExceptionHandlerTests - : IClassFixture, ErrorDbContext>> + : IClassFixture, ErrorDbContext>> { - private readonly IntegrationTestContext, ErrorDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ErrorDbContext> _testContext; - public ExceptionHandlerTests(IntegrationTestContext, ErrorDbContext> testContext) + public ExceptionHandlerTests(ExampleIntegrationTestContext, ErrorDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs index 1ed25c4b3b..805fd8b513 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs @@ -13,11 +13,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering { - public sealed class FilterDataTypeTests : IClassFixture, FilterDbContext>> + public sealed class FilterDataTypeTests : IClassFixture, FilterDbContext>> { - private readonly IntegrationTestContext, FilterDbContext> _testContext; + private readonly ExampleIntegrationTestContext, FilterDbContext> _testContext; - public FilterDataTypeTests(IntegrationTestContext, FilterDbContext> testContext) + public FilterDataTypeTests(ExampleIntegrationTestContext, FilterDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs index 23f4036358..a0748bf943 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs @@ -15,11 +15,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering { - public sealed class FilterDepthTests : IClassFixture> + public sealed class FilterDepthTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public FilterDepthTests(IntegrationTestContext testContext) + public FilterDepthTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs index d7ed5030e8..71a6f54f21 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs @@ -14,11 +14,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering { - public sealed class FilterOperatorTests : IClassFixture, FilterDbContext>> + public sealed class FilterOperatorTests : IClassFixture, FilterDbContext>> { - private readonly IntegrationTestContext, FilterDbContext> _testContext; + private readonly ExampleIntegrationTestContext, FilterDbContext> _testContext; - public FilterOperatorTests(IntegrationTestContext, FilterDbContext> testContext) + public FilterOperatorTests(ExampleIntegrationTestContext, FilterDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs index f08231b2c3..c60671a5f3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering { - public sealed class FilterTests : IClassFixture> + public sealed class FilterTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public FilterTests(IntegrationTestContext testContext) + public FilterTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs index 40bea53580..ae8ab26b42 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs @@ -9,12 +9,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.IdObfuscation { public sealed class IdObfuscationTests - : IClassFixture, ObfuscationDbContext>> + : IClassFixture, ObfuscationDbContext>> { - private readonly IntegrationTestContext, ObfuscationDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ObfuscationDbContext> _testContext; private readonly ObfuscationFakers _fakers = new ObfuscationFakers(); - public IdObfuscationTests(IntegrationTestContext, ObfuscationDbContext> testContext) + public IdObfuscationTests(ExampleIntegrationTestContext, ObfuscationDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs index 5c3a0e447a..6afd1fd707 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs @@ -16,11 +16,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Includes { - public sealed class IncludeTests : IClassFixture> + public sealed class IncludeTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public IncludeTests(IntegrationTestContext testContext) + public IncludeTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs index 9a027ab7f5..a5fa5e1f26 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithNamespaceTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { public sealed class AbsoluteLinksWithNamespaceTests - : IClassFixture, LinksDbContext>> + : IClassFixture, LinksDbContext>> { - private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly ExampleIntegrationTestContext, LinksDbContext> _testContext; private readonly LinksFakers _fakers = new LinksFakers(); - public AbsoluteLinksWithNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + public AbsoluteLinksWithNamespaceTests(ExampleIntegrationTestContext, LinksDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs index 451b1bf7c7..7de9adbaee 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { public sealed class AbsoluteLinksWithoutNamespaceTests - : IClassFixture, LinksDbContext>> + : IClassFixture, LinksDbContext>> { - private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly ExampleIntegrationTestContext, LinksDbContext> _testContext; private readonly LinksFakers _fakers = new LinksFakers(); - public AbsoluteLinksWithoutNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + public AbsoluteLinksWithoutNamespaceTests(ExampleIntegrationTestContext, LinksDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs index 2a1d7c604b..e06f78f6a7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { public sealed class RelativeLinksWithNamespaceTests - : IClassFixture, LinksDbContext>> + : IClassFixture, LinksDbContext>> { - private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly ExampleIntegrationTestContext, LinksDbContext> _testContext; private readonly LinksFakers _fakers = new LinksFakers(); - public RelativeLinksWithNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + public RelativeLinksWithNamespaceTests(ExampleIntegrationTestContext, LinksDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs index 6054ee0e66..dfbffa6123 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links { public sealed class RelativeLinksWithoutNamespaceTests - : IClassFixture, LinksDbContext>> + : IClassFixture, LinksDbContext>> { - private readonly IntegrationTestContext, LinksDbContext> _testContext; + private readonly ExampleIntegrationTestContext, LinksDbContext> _testContext; private readonly LinksFakers _fakers = new LinksFakers(); - public RelativeLinksWithoutNamespaceTests(IntegrationTestContext, LinksDbContext> testContext) + public RelativeLinksWithoutNamespaceTests(ExampleIntegrationTestContext, LinksDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs index d2f72569c2..388c77aa58 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Logging/LoggingTests.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Logging { - public sealed class LoggingTests : IClassFixture> + public sealed class LoggingTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public LoggingTests(IntegrationTestContext testContext) + public LoggingTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs index 30c415ff96..5fcd446864 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs @@ -11,11 +11,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta { - public sealed class ResourceMetaTests : IClassFixture> + public sealed class ResourceMetaTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public ResourceMetaTests(IntegrationTestContext testContext) + public ResourceMetaTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs index d13df8b731..f90f29223c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs @@ -13,11 +13,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta { - public sealed class ResponseMetaTests : IClassFixture> + public sealed class ResponseMetaTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public ResponseMetaTests(IntegrationTestContext testContext) + public ResponseMetaTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs index edd4ac6d7b..3b19ebb9fc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta { - public sealed class TopLevelCountTests : IClassFixture> + public sealed class TopLevelCountTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; - public TopLevelCountTests(IntegrationTestContext testContext) + public TopLevelCountTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index 20024943c9..0d8176ff36 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -9,11 +9,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation { - public sealed class ModelStateValidationTests : IClassFixture, ModelStateDbContext>> + public sealed class ModelStateValidationTests : IClassFixture, ModelStateDbContext>> { - private readonly IntegrationTestContext, ModelStateDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ModelStateDbContext> _testContext; - public ModelStateValidationTests(IntegrationTestContext, ModelStateDbContext> testContext) + public ModelStateValidationTests(ExampleIntegrationTestContext, ModelStateDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs index b7de6d3098..0923df55e1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs @@ -7,11 +7,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation { - public sealed class NoModelStateValidationTests : IClassFixture, ModelStateDbContext>> + public sealed class NoModelStateValidationTests : IClassFixture, ModelStateDbContext>> { - private readonly IntegrationTestContext, ModelStateDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ModelStateDbContext> _testContext; - public NoModelStateValidationTests(IntegrationTestContext, ModelStateDbContext> testContext) + public NoModelStateValidationTests(ExampleIntegrationTestContext, ModelStateDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs index 997ebe9aa0..a19dee6ab3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.NamingConventions { public sealed class KebabCasingTests - : IClassFixture, SwimmingDbContext>> + : IClassFixture, SwimmingDbContext>> { - private readonly IntegrationTestContext, SwimmingDbContext> _testContext; + private readonly ExampleIntegrationTestContext, SwimmingDbContext> _testContext; private readonly SwimmingFakers _fakers = new SwimmingFakers(); - public KebabCasingTests(IntegrationTestContext, SwimmingDbContext> testContext) + public KebabCasingTests(ExampleIntegrationTestContext, SwimmingDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs index ac398c8bb0..fb9d31449b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs @@ -15,14 +15,14 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination { - public sealed class PaginationWithTotalCountTests : IClassFixture> + public sealed class PaginationWithTotalCountTests : IClassFixture> { private const int _defaultPageSize = 5; - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private readonly ExampleFakers _fakers; - public PaginationWithTotalCountTests(IntegrationTestContext testContext) + public PaginationWithTotalCountTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs index ed3cf6a25c..2f291af433 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs @@ -12,14 +12,14 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination { - public sealed class PaginationWithoutTotalCountTests : IClassFixture> + public sealed class PaginationWithoutTotalCountTests : IClassFixture> { private const int _defaultPageSize = 5; - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private readonly ExampleFakers _fakers; - public PaginationWithoutTotalCountTests(IntegrationTestContext testContext) + public PaginationWithoutTotalCountTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs index 718aafa7cd..e9974a3d29 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs @@ -12,14 +12,14 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination { - public sealed class RangeValidationTests : IClassFixture> + public sealed class RangeValidationTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private readonly ExampleFakers _fakers; private const int _defaultPageSize = 5; - public RangeValidationTests(IntegrationTestContext testContext) + public RangeValidationTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs index ee44ec3ed5..62f773be25 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs @@ -11,14 +11,14 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination { - public sealed class RangeValidationWithMaximumTests : IClassFixture> + public sealed class RangeValidationWithMaximumTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private const int _maximumPageSize = 15; private const int _maximumPageNumber = 20; - public RangeValidationWithMaximumTests(IntegrationTestContext testContext) + public RangeValidationWithMaximumTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs index 8b61692981..b3a5db84c8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings { public sealed class QueryStringTests - : IClassFixture, QueryStringDbContext>> + : IClassFixture, QueryStringDbContext>> { - private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly ExampleIntegrationTestContext, QueryStringDbContext> _testContext; private readonly QueryStringFakers _fakers = new QueryStringFakers(); - public QueryStringTests(IntegrationTestContext, QueryStringDbContext> testContext) + public QueryStringTests(ExampleIntegrationTestContext, QueryStringDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs index 3768a8501d..829cd5de4d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs @@ -12,12 +12,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings { public sealed class SerializerDefaultValueHandlingTests - : IClassFixture, QueryStringDbContext>> + : IClassFixture, QueryStringDbContext>> { - private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly ExampleIntegrationTestContext, QueryStringDbContext> _testContext; private readonly QueryStringFakers _fakers = new QueryStringFakers(); - public SerializerDefaultValueHandlingTests(IntegrationTestContext, QueryStringDbContext> testContext) + public SerializerDefaultValueHandlingTests(ExampleIntegrationTestContext, QueryStringDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs index 30ed412203..aaa2029c6b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs @@ -12,12 +12,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings { public sealed class SerializerNullValueHandlingTests - : IClassFixture, QueryStringDbContext>> + : IClassFixture, QueryStringDbContext>> { - private readonly IntegrationTestContext, QueryStringDbContext> _testContext; + private readonly ExampleIntegrationTestContext, QueryStringDbContext> _testContext; private readonly QueryStringFakers _fakers = new QueryStringFakers(); - public SerializerNullValueHandlingTests(IntegrationTestContext, QueryStringDbContext> testContext) + public SerializerNullValueHandlingTests(ExampleIntegrationTestContext, QueryStringDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index cf1be53b1e..9c5d5e775b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -14,12 +14,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating { public sealed class CreateResourceTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public CreateResourceTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public CreateResourceTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index ea3037926a..86ad8fd335 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating { public sealed class CreateResourceWithClientGeneratedIdTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public CreateResourceWithClientGeneratedIdTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public CreateResourceWithClientGeneratedIdTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index d84310876c..558c375149 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating { public sealed class CreateResourceWithToManyRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public CreateResourceWithToManyRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public CreateResourceWithToManyRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index 16bf2623ce..ad8865d712 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Creating { public sealed class CreateResourceWithToOneRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public CreateResourceWithToOneRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public CreateResourceWithToOneRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs index 82343a9a3f..05f1ac71b7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Deleting { public sealed class DeleteResourceTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public DeleteResourceTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public DeleteResourceTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs index 7ce2d71484..fc34b593d2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Fetching { public sealed class FetchRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public FetchRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public FetchRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index 0ccb85d25c..57c49860c3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Fetching { public sealed class FetchResourceTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public FetchResourceTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public FetchResourceTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index 701322743e..c8ac2c66ff 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -11,12 +11,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships { public sealed class AddToToManyRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public AddToToManyRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public AddToToManyRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index e284e0edfc..7dbadd7717 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -11,12 +11,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships { public sealed class RemoveFromToManyRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public RemoveFromToManyRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public RemoveFromToManyRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index e0f901c4d8..bfdd768bd6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -11,12 +11,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships { public sealed class ReplaceToManyRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public ReplaceToManyRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public ReplaceToManyRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index fd9eeadad9..e0f202b068 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Relationships { public sealed class UpdateToOneRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public UpdateToOneRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public UpdateToOneRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index c4d083d534..163d6b9d5e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -11,12 +11,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources { public sealed class ReplaceToManyRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public ReplaceToManyRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public ReplaceToManyRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index b44199bf14..bc9f15e423 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources { public sealed class UpdateResourceTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public UpdateResourceTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public UpdateResourceTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index c489306d62..7c4eb34229 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ReadWrite.Updating.Resources { public sealed class UpdateToOneRelationshipTests - : IClassFixture, ReadWriteDbContext>> + : IClassFixture, ReadWriteDbContext>> { - private readonly IntegrationTestContext, ReadWriteDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ReadWriteDbContext> _testContext; private readonly ReadWriteFakers _fakers = new ReadWriteFakers(); - public UpdateToOneRelationshipTests(IntegrationTestContext, ReadWriteDbContext> testContext) + public UpdateToOneRelationshipTests(ExampleIntegrationTestContext, ReadWriteDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs index df033d8c5f..0f8d4fdda9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs @@ -9,13 +9,13 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RequiredRelationships { - public sealed class DefaultBehaviorTests : IClassFixture, DefaultBehaviorDbContext>> + public sealed class DefaultBehaviorTests : IClassFixture, DefaultBehaviorDbContext>> { - private readonly IntegrationTestContext, DefaultBehaviorDbContext> _testContext; + private readonly ExampleIntegrationTestContext, DefaultBehaviorDbContext> _testContext; private readonly DefaultBehaviorFakers _fakers = new DefaultBehaviorFakers(); - public DefaultBehaviorTests(IntegrationTestContext, DefaultBehaviorDbContext> testContext) + public DefaultBehaviorTests(ExampleIntegrationTestContext, DefaultBehaviorDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs index dcec12f073..e381cdece2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceConstructorInjection { public sealed class ResourceInjectionTests - : IClassFixture, InjectionDbContext>> + : IClassFixture, InjectionDbContext>> { - private readonly IntegrationTestContext, InjectionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, InjectionDbContext> _testContext; private readonly InjectionFakers _fakers; - public ResourceInjectionTests(IntegrationTestContext, InjectionDbContext> testContext) + public ResourceInjectionTests(ExampleIntegrationTestContext, InjectionDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs index d75a3a3774..41e010ad5a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs @@ -12,11 +12,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceDefinitions { - public sealed class ResourceDefinitionQueryCallbackTests : IClassFixture, CallableDbContext>> + public sealed class ResourceDefinitionQueryCallbackTests : IClassFixture, CallableDbContext>> { - private readonly IntegrationTestContext, CallableDbContext> _testContext; + private readonly ExampleIntegrationTestContext, CallableDbContext> _testContext; - public ResourceDefinitionQueryCallbackTests(IntegrationTestContext, CallableDbContext> testContext) + public ResourceDefinitionQueryCallbackTests(ExampleIntegrationTestContext, CallableDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index d08615ad66..96b042c7e4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -22,12 +22,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks { public sealed class ResourceHookTests - : IClassFixture, AppDbContext>> + : IClassFixture, AppDbContext>> { - private readonly IntegrationTestContext, AppDbContext> _testContext; + private readonly ExampleIntegrationTestContext, AppDbContext> _testContext; private readonly ExampleFakers _fakers; - public ResourceHookTests(IntegrationTestContext, AppDbContext> testContext) + public ResourceHookTests(ExampleIntegrationTestContext, AppDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs index c41f798a00..af60191944 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -21,7 +21,7 @@ public override void ConfigureServices(IServiceCollection services) base.ConfigureServices(services); - services.AddControllersFromTestProject(); + services.AddControllersFromExampleProject(); services.AddClientSerialization(); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs index 3f0fa157f2..0ce3721116 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs @@ -10,11 +10,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceInheritance { - public sealed class InheritanceTests : IClassFixture, InheritanceDbContext>> + public sealed class InheritanceTests : IClassFixture, InheritanceDbContext>> { - private readonly IntegrationTestContext, InheritanceDbContext> _testContext; + private readonly ExampleIntegrationTestContext, InheritanceDbContext> _testContext; - public InheritanceTests(IntegrationTestContext, InheritanceDbContext> testContext) + public InheritanceTests(ExampleIntegrationTestContext, InheritanceDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs index f1dadcece0..6f73c145da 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs @@ -10,12 +10,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class DisableQueryStringTests - : IClassFixture, RestrictionDbContext>> + : IClassFixture, RestrictionDbContext>> { - private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, RestrictionDbContext> _testContext; private readonly RestrictionFakers _fakers = new RestrictionFakers(); - public DisableQueryStringTests(IntegrationTestContext, RestrictionDbContext> testContext) + public DisableQueryStringTests(ExampleIntegrationTestContext, RestrictionDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs index 0ce068bf3d..0895e7d8b4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs @@ -8,12 +8,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class HttpReadOnlyTests - : IClassFixture, RestrictionDbContext>> + : IClassFixture, RestrictionDbContext>> { - private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, RestrictionDbContext> _testContext; private readonly RestrictionFakers _fakers = new RestrictionFakers(); - public HttpReadOnlyTests(IntegrationTestContext, RestrictionDbContext> testContext) + public HttpReadOnlyTests(ExampleIntegrationTestContext, RestrictionDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs index 3fecae6275..699d1fc7df 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs @@ -8,12 +8,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class NoHttpDeleteTests - : IClassFixture, RestrictionDbContext>> + : IClassFixture, RestrictionDbContext>> { - private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, RestrictionDbContext> _testContext; private readonly RestrictionFakers _fakers = new RestrictionFakers(); - public NoHttpDeleteTests(IntegrationTestContext, RestrictionDbContext> testContext) + public NoHttpDeleteTests(ExampleIntegrationTestContext, RestrictionDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs index d972d2f429..9b2d7a3ea3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs @@ -8,12 +8,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class NoHttpPatchTests - : IClassFixture, RestrictionDbContext>> + : IClassFixture, RestrictionDbContext>> { - private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, RestrictionDbContext> _testContext; private readonly RestrictionFakers _fakers = new RestrictionFakers(); - public NoHttpPatchTests(IntegrationTestContext, RestrictionDbContext> testContext) + public NoHttpPatchTests(ExampleIntegrationTestContext, RestrictionDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs index eefdda4aa6..837a0a1eeb 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs @@ -8,12 +8,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class NoHttpPostTests - : IClassFixture, RestrictionDbContext>> + : IClassFixture, RestrictionDbContext>> { - private readonly IntegrationTestContext, RestrictionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, RestrictionDbContext> _testContext; private readonly RestrictionFakers _fakers = new RestrictionFakers(); - public NoHttpPostTests(IntegrationTestContext, RestrictionDbContext> testContext) + public NoHttpPostTests(ExampleIntegrationTestContext, RestrictionDbContext> testContext) { _testContext = testContext; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs index e90f0e1b50..b57aebe571 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs @@ -15,12 +15,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Serialization { public sealed class SerializationTests - : IClassFixture, SerializationDbContext>> + : IClassFixture, SerializationDbContext>> { - private readonly IntegrationTestContext, SerializationDbContext> _testContext; + private readonly ExampleIntegrationTestContext, SerializationDbContext> _testContext; private readonly SerializationFakers _fakers = new SerializationFakers(); - public SerializationTests(IntegrationTestContext, SerializationDbContext> testContext) + public SerializationTests(ExampleIntegrationTestContext, SerializationDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs index 874b1626fd..af26d97e67 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs @@ -11,11 +11,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SoftDeletion { - public sealed class SoftDeletionTests : IClassFixture, SoftDeletionDbContext>> + public sealed class SoftDeletionTests : IClassFixture, SoftDeletionDbContext>> { - private readonly IntegrationTestContext, SoftDeletionDbContext> _testContext; + private readonly ExampleIntegrationTestContext, SoftDeletionDbContext> _testContext; - public SoftDeletionTests(IntegrationTestContext, SoftDeletionDbContext> testContext) + public SoftDeletionTests(ExampleIntegrationTestContext, SoftDeletionDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs index b0d60fed21..79b8b21629 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Sorting { - public sealed class SortTests : IClassFixture> + public sealed class SortTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private readonly ExampleFakers _fakers; - public SortTests(IntegrationTestContext testContext) + public SortTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs index 31ecebed2a..37db55b381 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs @@ -16,12 +16,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets { - public sealed class SparseFieldSetTests : IClassFixture> + public sealed class SparseFieldSetTests : IClassFixture> { - private readonly IntegrationTestContext _testContext; + private readonly ExampleIntegrationTestContext _testContext; private readonly ExampleFakers _fakers; - public SparseFieldSetTests(IntegrationTestContext testContext) + public SparseFieldSetTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs index 304d18b253..412a996f57 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs @@ -13,12 +13,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ZeroKeys { public sealed class EmptyGuidAsKeyTests - : IClassFixture, ZeroKeyDbContext>> + : IClassFixture, ZeroKeyDbContext>> { - private readonly IntegrationTestContext, ZeroKeyDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ZeroKeyDbContext> _testContext; private readonly ZeroKeyFakers _fakers = new ZeroKeyFakers(); - public EmptyGuidAsKeyTests(IntegrationTestContext, ZeroKeyDbContext> testContext) + public EmptyGuidAsKeyTests(ExampleIntegrationTestContext, ZeroKeyDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs index fa58257c4a..8d13766ab9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs @@ -12,12 +12,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ZeroKeys { public sealed class ZeroAsKeyTests - : IClassFixture, ZeroKeyDbContext>> + : IClassFixture, ZeroKeyDbContext>> { - private readonly IntegrationTestContext, ZeroKeyDbContext> _testContext; + private readonly ExampleIntegrationTestContext, ZeroKeyDbContext> _testContext; private readonly ZeroKeyFakers _fakers = new ZeroKeyFakers(); - public ZeroAsKeyTests(IntegrationTestContext, ZeroKeyDbContext> testContext) + public ZeroAsKeyTests(ExampleIntegrationTestContext, ZeroKeyDbContext> testContext) { _testContext = testContext; diff --git a/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs index 93321b9897..e8eb3fa54e 100644 --- a/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreExampleTests { public static class ServiceCollectionExtensions { - public static void AddControllersFromTestProject(this IServiceCollection services) + public static void AddControllersFromExampleProject(this IServiceCollection services) { var part = new AssemblyPart(typeof(EmptyStartup).Assembly); services.AddMvcCore().ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part)); diff --git a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs index b0d61c1e59..b7f7369820 100644 --- a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs +++ b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs @@ -15,7 +15,7 @@ namespace TestBuildingBlocks { /// - /// A test context that creates a new database and server instance before running tests and cleans up afterwards. + /// Base class for a test context that creates a new database and server instance before running tests and cleans up afterwards. /// You can either use this as a fixture on your tests class (init/cleanup runs once before/after all tests) or /// have your tests class inherit from it (init/cleanup runs once before/after each test). See /// for details on shared context usage. From d94235dc2e71f2792cad805a0448e73d4606cbc5 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 16:03:32 +0100 Subject: [PATCH 21/47] Adjusted test names for ModelStateValidationTests --- .../ModelStateValidationTests.cs | 36 +++++++++---------- .../NoModelStateValidationTests.cs | 4 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index 0d8176ff36..50a489f9d9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -19,7 +19,7 @@ public ModelStateValidationTests(ExampleIntegrationTestContext } [Fact] - public async Task When_posting_annotated_to_many_relationship_it_must_succeed() + public async Task Can_add_to_annotated_ToMany_relationship() { // Arrange var directory = new SystemDirectory @@ -320,7 +320,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_omitted_required_attribute_value_it_must_succeed() + public async Task Can_update_resource_with_omitted_required_attribute_value() { // Arrange var directory = new SystemDirectory @@ -360,7 +360,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_null_for_required_attribute_value_it_must_fail() + public async Task Cannot_update_resource_with_null_for_required_attribute_value() { // Arrange var directory = new SystemDirectory @@ -404,7 +404,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_invalid_attribute_value_it_must_fail() + public async Task Cannot_update_resource_with_invalid_attribute_value() { // Arrange var directory = new SystemDirectory @@ -448,7 +448,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_invalid_ID_it_must_fail() + public async Task Cannot_update_resource_with_invalid_ID() { // Arrange var directory = new SystemDirectory @@ -512,7 +512,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_valid_attribute_value_it_must_succeed() + public async Task Can_update_resource_with_valid_attribute_value() { // Arrange var directory = new SystemDirectory @@ -552,7 +552,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_annotated_relationships_it_must_succeed() + public async Task Can_update_resource_with_annotated_relationships() { // Arrange var directory = new SystemDirectory @@ -664,7 +664,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_multiple_self_references_it_must_succeed() + public async Task Can_update_resource_with_multiple_self_references() { // Arrange var directory = new SystemDirectory @@ -723,7 +723,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_resource_with_collection_of_self_references_it_must_succeed() + public async Task Can_update_resource_with_collection_of_self_references() { // Arrange var directory = new SystemDirectory @@ -777,7 +777,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_annotated_ToOne_relationship_it_must_succeed() + public async Task Can_replace_annotated_ToOne_relationship() { // Arrange var directory = new SystemDirectory @@ -824,7 +824,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_patching_annotated_ToMany_relationship_it_must_succeed() + public async Task Can_replace_annotated_ToMany_relationship() { // Arrange var directory = new SystemDirectory @@ -881,7 +881,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_deleting_annotated_to_many_relationship_it_must_succeed() + public async Task Can_remove_from_annotated_ToMany_relationship() { // Arrange var directory = new SystemDirectory diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs index 0923df55e1..ca5d155cb6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs @@ -17,7 +17,7 @@ public NoModelStateValidationTests(ExampleIntegrationTestContext Date: Thu, 4 Feb 2021 16:17:11 +0100 Subject: [PATCH 22/47] Adjusted test names for pagination --- .../PaginationWithoutTotalCountTests.cs | 12 ++++++------ .../Pagination/RangeValidationTests.cs | 14 +++++++------- .../RangeValidationWithMaximumTests.cs | 14 +++++++------- test/MultiDbContextTests/ResourceTests.cs | 18 +++++------------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs index 2f291af433..2892ec63e7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs @@ -33,7 +33,7 @@ public PaginationWithoutTotalCountTests(ExampleIntegrationTestContext(); @@ -56,7 +56,7 @@ public async Task When_page_size_is_unconstrained_it_should_not_render_paginatio } [Fact] - public async Task When_page_size_is_specified_in_query_string_with_no_data_it_should_render_pagination_links() + public async Task Renders_pagination_links_when_page_size_is_specified_in_query_string_with_no_data() { // Arrange var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); @@ -85,7 +85,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_page_number_is_specified_in_query_string_with_no_data_it_should_render_pagination_links() + public async Task Renders_pagination_links_when_page_number_is_specified_in_query_string_with_no_data() { // Arrange await _testContext.RunOnDatabaseAsync(async dbContext => @@ -111,7 +111,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_page_number_is_specified_in_query_string_with_partially_filled_page_it_should_render_pagination_links() + public async Task Renders_pagination_links_when_page_number_is_specified_in_query_string_with_partially_filled_page() { // Arrange var articles = _fakers.Article.Generate(12); @@ -143,7 +143,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_page_number_is_specified_in_query_string_with_full_page_it_should_render_pagination_links() + public async Task Renders_pagination_links_when_page_number_is_specified_in_query_string_with_full_page() { // Arrange var articles = _fakers.Article.Generate(_defaultPageSize * 3); @@ -175,7 +175,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_page_number_is_specified_in_query_string_with_full_page_on_secondary_endpoint_it_should_render_pagination_links() + public async Task Renders_pagination_links_when_page_number_is_specified_in_query_string_with_full_page_on_secondary_endpoint() { // Arrange var author = new Author diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs index e9974a3d29..4382215942 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs @@ -32,7 +32,7 @@ public RangeValidationTests(ExampleIntegrationTestContext } [Fact] - public async Task When_page_number_is_negative_it_must_fail() + public async Task Cannot_use_negative_page_number() { // Arrange var route = "/api/v1/todoItems?page[number]=-1"; @@ -51,7 +51,7 @@ public async Task When_page_number_is_negative_it_must_fail() } [Fact] - public async Task When_page_number_is_zero_it_must_fail() + public async Task Cannot_use_zero_page_number() { // Arrange var route = "/api/v1/todoItems?page[number]=0"; @@ -70,7 +70,7 @@ public async Task When_page_number_is_zero_it_must_fail() } [Fact] - public async Task When_page_number_is_positive_it_must_succeed() + public async Task Can_use_positive_page_number() { // Arrange var route = "/api/v1/todoItems?page[number]=20"; @@ -83,7 +83,7 @@ public async Task When_page_number_is_positive_it_must_succeed() } [Fact] - public async Task When_page_number_is_too_high_it_must_return_empty_set_of_resources() + public async Task Returns_empty_set_of_resources_when_page_number_is_too_high() { // Arrange var todoItems = _fakers.TodoItem.Generate(3); @@ -108,7 +108,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task When_page_size_is_negative_it_must_fail() + public async Task Cannot_use_negative_page_size() { // Arrange var route = "/api/v1/todoItems?page[size]=-1"; @@ -127,7 +127,7 @@ public async Task When_page_size_is_negative_it_must_fail() } [Fact] - public async Task When_page_size_is_zero_it_must_succeed() + public async Task Can_use_zero_page_size() { // Arrange var route = "/api/v1/todoItems?page[size]=0"; @@ -140,7 +140,7 @@ public async Task When_page_size_is_zero_it_must_succeed() } [Fact] - public async Task When_page_size_is_positive_it_must_succeed() + public async Task Can_use_positive_page_size() { // Arrange var route = "/api/v1/todoItems?page[size]=50"; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs index 62f773be25..6fe73f6df6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs @@ -29,7 +29,7 @@ public RangeValidationWithMaximumTests(ExampleIntegrationTestContext factory) } [Fact] - public async Task Can_Get_ResourceAs() + public async Task Can_get_ResourceAs() { // Arrange var client = _factory.CreateClient(); @@ -31,7 +32,7 @@ public async Task Can_Get_ResourceAs() var response = await client.SendAsync(request); // Assert - AssertStatusCode(HttpStatusCode.OK, response); + response.Should().HaveStatusCode(HttpStatusCode.OK); string responseBody = await response.Content.ReadAsStringAsync(); var document = JsonConvert.DeserializeObject(responseBody); @@ -41,7 +42,7 @@ public async Task Can_Get_ResourceAs() } [Fact] - public async Task Can_Get_ResourceBs() + public async Task Can_get_ResourceBs() { // Arrange var client = _factory.CreateClient(); @@ -52,7 +53,7 @@ public async Task Can_Get_ResourceBs() var response = await client.SendAsync(request); // Assert - AssertStatusCode(HttpStatusCode.OK, response); + response.Should().HaveStatusCode(HttpStatusCode.OK); string responseBody = await response.Content.ReadAsStringAsync(); var document = JsonConvert.DeserializeObject(responseBody); @@ -60,14 +61,5 @@ public async Task Can_Get_ResourceBs() document.ManyData.Should().HaveCount(1); document.ManyData[0].Attributes["nameB"].Should().Be("SampleB"); } - - private static void AssertStatusCode(HttpStatusCode expected, HttpResponseMessage response) - { - if (expected != response.StatusCode) - { - var responseBody = response.Content.ReadAsStringAsync().Result; - Assert.True(expected == response.StatusCode, $"Got {response.StatusCode} status code instead of {expected}. Response body: {responseBody}"); - } - } } } From cb983b9a6cc8973257822d23d6774f1dac5810e6 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 16:42:17 +0100 Subject: [PATCH 23/47] Adjusted test names for meta --- .../Models/TodoItem.cs | 7 ------ .../Meta/ResourceMetaTests.cs | 4 ++-- .../Meta/ResponseMetaTests.cs | 22 +---------------- .../IntegrationTests/Meta/TestResponseMeta.cs | 24 +++++++++++++++++++ .../Meta/TopLevelCountTests.cs | 14 +++++++---- 5 files changed, 37 insertions(+), 34 deletions(-) create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TestResponseMeta.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index 64afada036..abef810db1 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -15,13 +15,6 @@ public class TodoItem : Identifiable, IIsLockable [Attr] public long Ordinal { get; set; } - [Attr(Capabilities = AttrCapabilities.All & ~AttrCapabilities.AllowCreate)] - public string AlwaysChangingValue - { - get => Guid.NewGuid().ToString(); - set { } - } - [Attr] public DateTime CreatedDate { get; set; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs index 5fcd446864..6236b7fdef 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs @@ -21,7 +21,7 @@ public ResourceMetaTests(ExampleIntegrationTestContext te } [Fact] - public async Task ResourceDefinition_That_Implements_GetMeta_Contains_Resource_Meta() + public async Task Returns_resource_meta_from_ResourceDefinition() { // Arrange var todoItems = new[] @@ -54,7 +54,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task ResourceDefinition_That_Implements_GetMeta_Contains_Include_Meta() + public async Task Returns_resource_meta_from_ResourceDefinition_in_included_resources() { // Arrange var person = new Person diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs index f90f29223c..dd0464d5d6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResponseMetaTests.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using FluentAssertions; @@ -31,7 +30,7 @@ public ResponseMetaTests(ExampleIntegrationTestContext te } [Fact] - public async Task Registered_IResponseMeta_Adds_TopLevel_Meta() + public async Task Returns_top_level_meta() { // Arrange await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync(); }); @@ -63,23 +62,4 @@ public async Task Registered_IResponseMeta_Adds_TopLevel_Meta() }"); } } - - public sealed class TestResponseMeta : IResponseMeta - { - public IReadOnlyDictionary GetMeta() - { - return new Dictionary - { - ["license"] = "MIT", - ["projectUrl"] = "https://github.com/json-api-dotnet/JsonApiDotNetCore/", - ["versions"] = new[] - { - "v4.0.0", - "v3.1.0", - "v2.5.2", - "v1.3.1" - } - }; - } - } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TestResponseMeta.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TestResponseMeta.cs new file mode 100644 index 0000000000..913986f316 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TestResponseMeta.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Serialization; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Meta +{ + public sealed class TestResponseMeta : IResponseMeta + { + public IReadOnlyDictionary GetMeta() + { + return new Dictionary + { + ["license"] = "MIT", + ["projectUrl"] = "https://github.com/json-api-dotnet/JsonApiDotNetCore/", + ["versions"] = new[] + { + "v4.0.0", + "v3.1.0", + "v2.5.2", + "v1.3.1" + } + }; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs index 3b19ebb9fc..4b2823f6cc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExample; using JsonApiDotNetCoreExample.Data; @@ -20,12 +21,17 @@ public TopLevelCountTests(ExampleIntegrationTestContext t { _testContext = testContext; + testContext.ConfigureServicesAfterStartup(services => + { + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); + }); + var options = (JsonApiOptions) testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; } [Fact] - public async Task Total_Resource_Count_Included_For_Collection() + public async Task Renders_resource_count_for_collection() { // Arrange var todoItem = new TodoItem(); @@ -51,7 +57,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Total_Resource_Count_Included_For_Empty_Collection() + public async Task Renders_resource_count_for_empty_collection() { // Arrange await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync(); }); @@ -69,7 +75,7 @@ public async Task Total_Resource_Count_Included_For_Empty_Collection() } [Fact] - public async Task Total_Resource_Count_Excluded_From_POST_Response() + public async Task Hides_resource_count_in_create_resource_response() { // Arrange var requestBody = new @@ -96,7 +102,7 @@ public async Task Total_Resource_Count_Excluded_From_POST_Response() } [Fact] - public async Task Total_Resource_Count_Excluded_From_PATCH_Response() + public async Task Hides_resource_count_in_update_resource_response() { // Arrange var todoItem = new TodoItem(); From 5ad5c76cc89b364b0dd0b7e24a47d8c14e5ecb87 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 17:06:37 +0100 Subject: [PATCH 24/47] Fixed broken tests; simpler bootstrap setup --- test/MultiDbContextTests/ResourceTests.cs | 35 +++--- test/NoEntityFrameworkTests/WorkItemTests.cs | 38 ++++-- .../BaseIntegrationTestContext.cs | 107 +--------------- test/TestBuildingBlocks/IntegrationTest.cs | 114 ++++++++++++++++++ .../RemoteIntegrationTestContext.cs | 18 --- 5 files changed, 162 insertions(+), 150 deletions(-) create mode 100644 test/TestBuildingBlocks/IntegrationTest.cs delete mode 100644 test/TestBuildingBlocks/RemoteIntegrationTestContext.cs diff --git a/test/MultiDbContextTests/ResourceTests.cs b/test/MultiDbContextTests/ResourceTests.cs index f02e4d1e1c..91caa3d293 100644 --- a/test/MultiDbContextTests/ResourceTests.cs +++ b/test/MultiDbContextTests/ResourceTests.cs @@ -11,7 +11,7 @@ namespace MultiDbContextTests { - public sealed class ResourceTests : IClassFixture> + public sealed class ResourceTests : IntegrationTest, IClassFixture> { private readonly WebApplicationFactory _factory; @@ -24,42 +24,37 @@ public ResourceTests(WebApplicationFactory factory) public async Task Can_get_ResourceAs() { // Arrange - var client = _factory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/resourceAs"); + var route = "/resourceAs"; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); - - string responseBody = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseBody); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - document.ManyData.Should().HaveCount(1); - document.ManyData[0].Attributes["nameA"].Should().Be("SampleA"); + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Attributes["nameA"].Should().Be("SampleA"); } [Fact] public async Task Can_get_ResourceBs() { // Arrange - var client = _factory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Get, "/resourceBs"); + var route = "/resourceBs"; // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - string responseBody = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(responseBody); + responseDocument.ManyData.Should().HaveCount(1); + responseDocument.ManyData[0].Attributes["nameB"].Should().Be("SampleB"); + } - document.ManyData.Should().HaveCount(1); - document.ManyData[0].Attributes["nameB"].Should().Be("SampleB"); + protected override HttpClient CreateClient() + { + return _factory.CreateClient(); } } } diff --git a/test/NoEntityFrameworkTests/WorkItemTests.cs b/test/NoEntityFrameworkTests/WorkItemTests.cs index f616691af3..5b1ce11a44 100644 --- a/test/NoEntityFrameworkTests/WorkItemTests.cs +++ b/test/NoEntityFrameworkTests/WorkItemTests.cs @@ -1,8 +1,11 @@ using System; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; using NoEntityFrameworkExample; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; @@ -11,20 +14,20 @@ namespace NoEntityFrameworkTests { - public sealed class WorkItemTests : IClassFixture> + public sealed class WorkItemTests : IntegrationTest, IClassFixture> { - private readonly RemoteIntegrationTestContext _testContext; + private readonly WebApplicationFactory _factory; - public WorkItemTests(RemoteIntegrationTestContext testContext) + public WorkItemTests(WebApplicationFactory factory) { - _testContext = testContext; + _factory = factory; } [Fact] public async Task Can_get_WorkItems() { // Arrange - await _testContext.RunOnDatabaseAsync(async dbContext => + await RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(new WorkItem()); await dbContext.SaveChangesAsync(); @@ -33,7 +36,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => var route = "/api/v1/workItems"; // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + var (httpResponse, responseDocument) = await ExecuteGetAsync(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); @@ -47,7 +50,7 @@ public async Task Can_get_WorkItem_by_ID() // Arrange var workItem = new WorkItem(); - await _testContext.RunOnDatabaseAsync(async dbContext => + await RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); await dbContext.SaveChangesAsync(); @@ -56,7 +59,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => var route = "/api/v1/workItems/" + workItem.StringId; // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); + var (httpResponse, responseDocument) = await ExecuteGetAsync(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); @@ -87,7 +90,7 @@ public async Task Can_create_WorkItem() var route = "/api/v1/workItems/"; // Act - var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + var (httpResponse, responseDocument) = await ExecutePostAsync(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); @@ -102,7 +105,7 @@ public async Task Can_delete_WorkItem() // Arrange var workItem = new WorkItem(); - await _testContext.RunOnDatabaseAsync(async dbContext => + await RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); await dbContext.SaveChangesAsync(); @@ -111,12 +114,25 @@ await _testContext.RunOnDatabaseAsync(async dbContext => var route = "/api/v1/workItems/" + workItem.StringId; // Act - var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); + var (httpResponse, responseDocument) = await ExecuteDeleteAsync(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); } + + protected override HttpClient CreateClient() + { + return _factory.CreateClient(); + } + + private async Task RunOnDatabaseAsync(Func asyncAction) + { + using IServiceScope scope = _factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await asyncAction(dbContext); + } } } diff --git a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs index b7f7369820..d06439b823 100644 --- a/test/TestBuildingBlocks/BaseIntegrationTestContext.cs +++ b/test/TestBuildingBlocks/BaseIntegrationTestContext.cs @@ -1,16 +1,12 @@ using System; -using System.Collections.Generic; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; -using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; namespace TestBuildingBlocks { @@ -23,7 +19,7 @@ namespace TestBuildingBlocks /// The server Startup class, which can be defined in the test project. /// The base class for , which MUST be defined in the API project. /// The EF Core database context, which can be defined in the test project. - public abstract class BaseIntegrationTestContext : IDisposable + public abstract class BaseIntegrationTestContext : IntegrationTest, IDisposable where TStartup : class where TRemoteStartup : class where TDbContext : DbContext @@ -35,6 +31,11 @@ public abstract class BaseIntegrationTestContext Factory => _lazyFactory.Value; + protected override HttpClient CreateClient() + { + return Factory.CreateClient(); + } + protected BaseIntegrationTestContext() { _lazyFactory = new Lazy>(CreateFactory); @@ -103,102 +104,6 @@ public async Task RunOnDatabaseAsync(Func asyncAction) await asyncAction(dbContext); } - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteGetAsync(string requestUrl, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecutePostAsync(string requestUrl, object requestBody, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, - acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecutePatchAsync(string requestUrl, object requestBody, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, - acceptHeaders); - } - - public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteDeleteAsync(string requestUrl, object requestBody = null, - string contentType = HeaderConstants.MediaType, - IEnumerable acceptHeaders = null) - { - return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, - acceptHeaders); - } - - private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> - ExecuteRequestAsync(HttpMethod method, string requestUrl, object requestBody, - string contentType, IEnumerable acceptHeaders) - { - var request = new HttpRequestMessage(method, requestUrl); - string requestText = SerializeRequest(requestBody); - - if (!string.IsNullOrEmpty(requestText)) - { - request.Content = new StringContent(requestText); - - if (contentType != null) - { - request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); - } - } - - using HttpClient client = Factory.CreateClient(); - - if (acceptHeaders != null) - { - foreach (var acceptHeader in acceptHeaders) - { - client.DefaultRequestHeaders.Accept.Add(acceptHeader); - } - } - - HttpResponseMessage responseMessage = await client.SendAsync(request); - - string responseText = await responseMessage.Content.ReadAsStringAsync(); - var responseDocument = DeserializeResponse(responseText); - - return (responseMessage, responseDocument); - } - - private string SerializeRequest(object requestBody) - { - return requestBody == null - ? null - : requestBody is string stringRequestBody - ? stringRequestBody - : JsonConvert.SerializeObject(requestBody); - } - - private TResponseDocument DeserializeResponse(string responseText) - { - if (typeof(TResponseDocument) == typeof(string)) - { - return (TResponseDocument)(object)responseText; - } - - try - { - return JsonConvert.DeserializeObject(responseText, - IntegrationTestConfiguration.DeserializationSettings); - } - catch (JsonException exception) - { - throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); - } - } - private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory { private Action _loggingConfiguration; diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs new file mode 100644 index 0000000000..12329bd1c8 --- /dev/null +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using JsonApiDotNetCore.Middleware; +using Newtonsoft.Json; + +namespace TestBuildingBlocks +{ + /// + /// A base class for tests that conveniently enables to execute HTTP requests against json:api endpoints. + /// + public abstract class IntegrationTest + { + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteGetAsync(string requestUrl, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecutePostAsync(string requestUrl, object requestBody, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, + acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecutePatchAsync(string requestUrl, object requestBody, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, + acceptHeaders); + } + + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteDeleteAsync(string requestUrl, object requestBody = null, + string contentType = HeaderConstants.MediaType, + IEnumerable acceptHeaders = null) + { + return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, + acceptHeaders); + } + + private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> + ExecuteRequestAsync(HttpMethod method, string requestUrl, object requestBody, + string contentType, IEnumerable acceptHeaders) + { + var request = new HttpRequestMessage(method, requestUrl); + string requestText = SerializeRequest(requestBody); + + if (!string.IsNullOrEmpty(requestText)) + { + request.Content = new StringContent(requestText); + + if (contentType != null) + { + request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + } + + using HttpClient client = CreateClient(); + + if (acceptHeaders != null) + { + foreach (var acceptHeader in acceptHeaders) + { + client.DefaultRequestHeaders.Accept.Add(acceptHeader); + } + } + + HttpResponseMessage responseMessage = await client.SendAsync(request); + + string responseText = await responseMessage.Content.ReadAsStringAsync(); + var responseDocument = DeserializeResponse(responseText); + + return (responseMessage, responseDocument); + } + + private string SerializeRequest(object requestBody) + { + return requestBody == null + ? null + : requestBody is string stringRequestBody + ? stringRequestBody + : JsonConvert.SerializeObject(requestBody); + } + + protected abstract HttpClient CreateClient(); + + private TResponseDocument DeserializeResponse(string responseText) + { + if (typeof(TResponseDocument) == typeof(string)) + { + return (TResponseDocument)(object)responseText; + } + + try + { + return JsonConvert.DeserializeObject(responseText, + IntegrationTestConfiguration.DeserializationSettings); + } + catch (JsonException exception) + { + throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); + } + } + } +} diff --git a/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs b/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs deleted file mode 100644 index d9555737d9..0000000000 --- a/test/TestBuildingBlocks/RemoteIntegrationTestContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace TestBuildingBlocks -{ - /// - /// A test context that creates a new database and server instance before running tests and cleans up afterwards. - /// You can either use this as a fixture on your tests class (init/cleanup runs once before/after all tests) or - /// have your tests class inherit from it (init/cleanup runs once before/after each test). See - /// for details on shared context usage. - /// - /// The server Startup class, which MUST be defined in the API project. - /// The EF Core database context, which MUST be defined in the API project. - public class RemoteIntegrationTestContext : BaseIntegrationTestContext - where TStartup : class - where TDbContext : DbContext - { - } -} From 838a158f3161b22d837bb7219895536b1f1a2741 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 17:10:41 +0100 Subject: [PATCH 25/47] Adjusted test names for hooks --- .../ResourceHooks/ResourceHookTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index 96b042c7e4..a24996c3bc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -48,7 +48,7 @@ public ResourceHookTests(ExampleIntegrationTestContext Date: Thu, 4 Feb 2021 17:31:35 +0100 Subject: [PATCH 26/47] Enable concurrent testruns (makes running all tests 60% faster) --- .../JsonApiDotNetCoreExampleTests.csproj | 6 ------ test/JsonApiDotNetCoreExampleTests/xunit.runner.json | 5 ----- 2 files changed, 11 deletions(-) delete mode 100644 test/JsonApiDotNetCoreExampleTests/xunit.runner.json diff --git a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj index 77e604e150..8bf9f701f4 100644 --- a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj +++ b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj @@ -3,12 +3,6 @@ $(NetCoreAppVersion) - - - PreserveNewest - - - diff --git a/test/JsonApiDotNetCoreExampleTests/xunit.runner.json b/test/JsonApiDotNetCoreExampleTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/JsonApiDotNetCoreExampleTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} From 64c5b4acabaa99a5d2b4f653dda0bba886375350 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 17:37:43 +0100 Subject: [PATCH 27/47] Removed unused using --- test/MultiDbContextTests/ResourceTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/MultiDbContextTests/ResourceTests.cs b/test/MultiDbContextTests/ResourceTests.cs index 91caa3d293..eace446619 100644 --- a/test/MultiDbContextTests/ResourceTests.cs +++ b/test/MultiDbContextTests/ResourceTests.cs @@ -5,7 +5,6 @@ using JsonApiDotNetCore.Serialization.Objects; using Microsoft.AspNetCore.Mvc.Testing; using MultiDbContextExample; -using Newtonsoft.Json; using TestBuildingBlocks; using Xunit; From bcb73205c5a20563f6a21b8e4642eee7beeb4e7b Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 18:20:09 +0100 Subject: [PATCH 28/47] Cleanup tests for hooks --- .../ResourceHooks/ResourceHookTests.cs | 395 ++++++------------ 1 file changed, 124 insertions(+), 271 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index 93916091fc..506d13152d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -2,18 +2,16 @@ using System.Linq; using System.Linq.Expressions; using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Client.Internal; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Definitions; using JsonApiDotNetCoreExample.Models; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using TestBuildingBlocks; @@ -54,36 +52,29 @@ public async Task Can_create_user_with_password() var user = _fakers.User.Generate(); var serializer = GetRequestSerializer(p => new {p.Password, p.UserName}); + string requestBody = serializer.Serialize(user); var route = "/api/v1/users"; - var request = new HttpRequestMessage(HttpMethod.Post, route) - { - Content = new StringContent(serializer.Serialize(user)) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Created); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); - var body = await response.Content.ReadAsStringAsync(); - var returnedUser = GetResponseDeserializer().DeserializeSingle(body).Data; - var document = JsonConvert.DeserializeObject(body); + var responseUser = GetResponseDeserializer().DeserializeSingle(responseDocument).Data; + var document = JsonConvert.DeserializeObject(responseDocument); document.SingleData.Attributes.Should().NotContainKey("password"); document.SingleData.Attributes["userName"].Should().Be(user.UserName); - using var scope = _testContext.Factory.Services.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - var dbUser = await dbContext.Users.FindAsync(returnedUser.Id); + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var userInDatabase = await dbContext.Users.FirstAsync(u => u.Id == responseUser.Id); - dbUser.UserName.Should().Be(user.UserName); - dbUser.Password.Should().Be(user.Password); + userInDatabase.UserName.Should().Be(user.UserName); + userInDatabase.Password.Should().Be(user.Password); + }); } [Fact] @@ -92,47 +83,34 @@ public async Task Can_update_user_password() // Arrange var user = _fakers.User.Generate(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Users.Add(user); await dbContext.SaveChangesAsync(); - } + }); user.Password = _fakers.User.Generate().Password; var serializer = GetRequestSerializer(p => new {p.Password}); + string requestBody = serializer.Serialize(user); var route = $"/api/v1/users/{user.Id}"; - var request = new HttpRequestMessage(HttpMethod.Patch, route) - { - Content = new StringContent(serializer.Serialize(user)) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); + responseDocument.SingleData.Attributes.Should().NotContainKey("password"); + responseDocument.SingleData.Attributes["userName"].Should().Be(user.UserName); - document.SingleData.Attributes.Should().NotContainKey("password"); - document.SingleData.Attributes["userName"].Should().Be(user.UserName); - - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var dbUser = dbContext.Users.Single(u => u.Id == user.Id); + var userInDatabase = await dbContext.Users.FirstAsync(u => u.Id == user.Id); - dbUser.Password.Should().Be(user.Password); - } + userInDatabase.Password.Should().Be(user.Password); + }); } [Fact] @@ -141,21 +119,16 @@ public async Task Unauthorized_TodoItem() // Arrange var route = "/api/v1/todoItems/1337"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update the author of todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update the author of todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -164,21 +137,16 @@ public async Task Unauthorized_Passport() // Arrange var route = "/api/v1/people/1?include=passport"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); - - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to include passports on individual persons."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to include passports on individual persons."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -188,31 +156,24 @@ public async Task Unauthorized_Article() var article = _fakers.Article.Generate(); article.Caption = "Classified"; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Articles.Add(article); await dbContext.SaveChangesAsync(); - } + }); var route = $"/api/v1/articles/{article.Id}"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to see this article."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to see this article."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -223,26 +184,21 @@ public async Task Article_is_hidden() string toBeExcluded = "This should not be included"; articles[0].Caption = toBeExcluded; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Articles.AddRange(articles); await dbContext.SaveChangesAsync(); - } + }); var route = "/api/v1/articles"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - var body = await response.Content.ReadAsStringAsync(); - body.Should().NotContain(toBeExcluded); + responseDocument.Should().NotContain(toBeExcluded); } [Fact] @@ -256,26 +212,21 @@ public async Task Article_through_secondary_endpoint_is_hidden() var author = _fakers.Author.Generate(); author.Articles = articles; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.AuthorDifferentDbContextName.Add(author); await dbContext.SaveChangesAsync(); - } + }); var route = $"/api/v1/authors/{author.Id}/articles"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - var body = await response.Content.ReadAsStringAsync(); - body.Should().NotContain(toBeExcluded); + responseDocument.Should().NotContain(toBeExcluded); } [Fact] @@ -284,33 +235,22 @@ public async Task Passport_Through_Secondary_Endpoint_Is_Hidden() // Arrange var person = _fakers.Person.Generate(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - - person.Passport = new Passport(dbContext) - { - IsLocked = true - }; - + person.Passport = new Passport(dbContext) {IsLocked = true}; dbContext.People.Add(person); await dbContext.SaveChangesAsync(); - } + }); var route = $"/api/v1/people/{person.Id}/passport"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); - - var body = await response.Content.ReadAsStringAsync(); - var document = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - document.Data.Should().BeNull(); + responseDocument.Data.Should().BeNull(); } [Fact] @@ -336,13 +276,11 @@ public async Task Tag_is_hidden() } }; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.ArticleTags.AddRange(articleTags); await dbContext.SaveChangesAsync(); - } + }); // Workaround for https://github.com/dotnet/efcore/issues/21026 var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); @@ -351,16 +289,13 @@ public async Task Tag_is_hidden() var route = "/api/v1/articles?include=tags"; - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.GetAsync(route); + var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.OK); + httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); - var body = await response.Content.ReadAsStringAsync(); - body.Should().NotContain(toBeExcluded); + responseDocument.Should().NotContain(toBeExcluded); } [Fact] @@ -374,16 +309,13 @@ public async Task Cascade_permission_error_create_ToOne_relationship() var lockedPerson = _fakers.Person.Generate(); lockedPerson.IsLocked = true; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var passport = new Passport(dbContext); lockedPerson.Passport = passport; - dbContext.People.Add(lockedPerson); await dbContext.SaveChangesAsync(); - } + }); var requestBody = new { @@ -406,27 +338,16 @@ public async Task Cascade_permission_error_create_ToOne_relationship() var route = "/api/v1/people"; - var request = new HttpRequestMessage(HttpMethod.Post, route); - - string requestText = JsonConvert.SerializeObject(requestBody); - request.Content = new StringContent(requestText); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -434,19 +355,18 @@ public async Task Cascade_permission_error_updating_ToOne_relationship() { // Arrange var person = _fakers.Person.Generate(); - Passport newPassport; + Passport newPassport = null; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var passport = new Passport(dbContext) {IsLocked = true}; person.Passport = passport; dbContext.People.Add(person); + newPassport = new Passport(dbContext); dbContext.Passports.Add(newPassport); await dbContext.SaveChangesAsync(); - } + }); var requestBody = new { @@ -470,27 +390,16 @@ public async Task Cascade_permission_error_updating_ToOne_relationship() var route = $"/api/v1/people/{person.Id}"; - var request = new HttpRequestMessage(HttpMethod.Patch, route); - - string requestText = JsonConvert.SerializeObject(requestBody); - request.Content = new StringContent(requestText); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); - - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); - errorDocument.Errors[0].Detail.Should().BeNull(); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); + + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -499,17 +408,16 @@ public async Task Cascade_permission_error_updating_ToOne_relationship_deletion( // Arrange var person = _fakers.Person.Generate(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var passport = new Passport(dbContext) {IsLocked = true}; person.Passport = passport; dbContext.People.Add(person); + var newPassport = new Passport(dbContext); dbContext.Passports.Add(newPassport); await dbContext.SaveChangesAsync(); - } + }); var requestBody = new { @@ -529,27 +437,16 @@ public async Task Cascade_permission_error_updating_ToOne_relationship_deletion( var route = $"/api/v1/people/{person.Id}"; - var request = new HttpRequestMessage(HttpMethod.Patch, route); - - string requestText = JsonConvert.SerializeObject(requestBody); - request.Content = new StringContent(requestText); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked persons."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -559,35 +456,26 @@ public async Task Cascade_permission_error_delete_ToOne_relationship() var lockedPerson = _fakers.Person.Generate(); lockedPerson.IsLocked = true; - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var passport = new Passport(dbContext); lockedPerson.Passport = passport; dbContext.People.Add(lockedPerson); await dbContext.SaveChangesAsync(); - } + }); var route = $"/api/v1/passports/{lockedPerson.Passport.StringId}"; - var request = new HttpRequestMessage(HttpMethod.Delete, route); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -599,13 +487,11 @@ public async Task Cascade_permission_error_create_ToMany_relationship() lockedTodo.IsLocked = true; lockedTodo.StakeHolders = persons.ToHashSet(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.TodoItems.Add(lockedTodo); await dbContext.SaveChangesAsync(); - } + }); var requestBody = new { @@ -636,27 +522,16 @@ public async Task Cascade_permission_error_create_ToMany_relationship() var route = "/api/v1/todoItems"; - var request = new HttpRequestMessage(HttpMethod.Post, route); - - string requestText = JsonConvert.SerializeObject(requestBody); - request.Content = new StringContent(requestText); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); - - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -671,13 +546,11 @@ public async Task Cascade_permission_error_updating_ToMany_relationship() var unlockedTodo = _fakers.TodoItem.Generate(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.TodoItems.AddRange(lockedTodo, unlockedTodo); await dbContext.SaveChangesAsync(); - } + }); var requestBody = new { @@ -709,27 +582,16 @@ public async Task Cascade_permission_error_updating_ToMany_relationship() var route = $"/api/v1/todoItems/{unlockedTodo.Id}"; - var request = new HttpRequestMessage(HttpMethod.Patch, route); - - string requestText = JsonConvert.SerializeObject(requestBody); - request.Content = new StringContent(requestText); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } [Fact] @@ -741,33 +603,24 @@ public async Task Cascade_permission_error_delete_ToMany_relationship() lockedTodo.IsLocked = true; lockedTodo.StakeHolders = persons.ToHashSet(); - using (var scope = _testContext.Factory.Services.CreateScope()) + await _testContext.RunOnDatabaseAsync(async dbContext => { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.TodoItems.Add(lockedTodo); await dbContext.SaveChangesAsync(); - } + }); var route = $"/api/v1/people/{persons[0].Id}"; - var request = new HttpRequestMessage(HttpMethod.Delete, route); - - using var client = _testContext.Factory.CreateClient(); - // Act - var response = await client.SendAsync(request); + var (httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync(route); // Assert - response.Should().HaveStatusCode(HttpStatusCode.Forbidden); - - var body = await response.Content.ReadAsStringAsync(); - var errorDocument = JsonConvert.DeserializeObject(body); + httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); - errorDocument.Errors.Should().HaveCount(1); - errorDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); - errorDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); - errorDocument.Errors[0].Detail.Should().BeNull(); + responseDocument.Errors.Should().HaveCount(1); + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.Forbidden); + responseDocument.Errors[0].Title.Should().Be("You are not allowed to update fields or relationships of locked todo items."); + responseDocument.Errors[0].Detail.Should().BeNull(); } private IRequestSerializer GetRequestSerializer(Expression> attributes = null, From 60ac85608c216e6abdd7ed032c691b0178bda19a Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:09:00 +0100 Subject: [PATCH 29/47] Cleanup example models --- .../Data/AppDbContext.cs | 12 +---- .../Models/Address.cs | 3 -- .../JsonApiDotNetCoreExample/Models/Gender.cs | 9 ---- .../Models/Passport.cs | 44 ---------------- .../JsonApiDotNetCoreExample/Models/Person.cs | 6 --- .../Models/SuperUser.cs | 10 ++++ .../Models/TodoItem.cs | 7 --- .../JsonApiDotNetCoreExample/Models/User.cs | 37 +------------ .../ExampleFakers.cs | 35 +++---------- .../PaginationWithTotalCountTests.cs | 4 +- .../PaginationWithoutTotalCountTests.cs | 4 +- .../Pagination/RangeValidationTests.cs | 4 +- .../ResourceHooks/ResourceHookTests.cs | 52 ++++++++----------- .../ResourceHooks/ResourceHooksStartup.cs | 4 -- .../IntegrationTests/Sorting/SortTests.cs | 4 +- .../SparseFieldSets/SparseFieldSetTests.cs | 4 +- .../EntityFrameworkCoreRepositoryTests.cs | 3 +- .../IServiceCollectionExtensionsTests.cs | 4 -- .../ResourceConstructionExpressionTests.cs | 32 ------------ .../Models/ResourceConstructionTests.cs | 37 ------------- .../ResourceHooks/ResourceHooksTestsSetup.cs | 13 ++--- 21 files changed, 51 insertions(+), 277 deletions(-) delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/Gender.cs create mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/SuperUser.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 6107186144..2f3e590dac 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -1,26 +1,19 @@ -using System; using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; namespace JsonApiDotNetCoreExample.Data { public sealed class AppDbContext : DbContext { - public ISystemClock SystemClock { get; } - public DbSet TodoItems { get; set; } - public DbSet Passports { get; set; } public DbSet People { get; set; } public DbSet
Articles { get; set; } public DbSet AuthorDifferentDbContextName { get; set; } public DbSet Users { get; set; } - public DbSet ArticleTags { get; set; } public DbSet Blogs { get; set; } - public AppDbContext(DbContextOptions options, ISystemClock systemClock) : base(options) + public AppDbContext(DbContextOptions options) : base(options) { - SystemClock = systemClock ?? throw new ArgumentNullException(nameof(systemClock)); } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -49,9 +42,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(t => t.StakeHolders) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(t => t.DependentOnTodo); - modelBuilder.Entity() .HasMany(t => t.ChildrenTodos) .WithOne(t => t.ParentTodo); diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Address.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Address.cs index a84436df31..2388a96729 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Address.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Address.cs @@ -8,9 +8,6 @@ public sealed class Address : Identifiable [Attr] public string Street { get; set; } - [Attr] - public string ZipCode { get; set; } - [HasOne] public Country Country { get; set; } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Gender.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Gender.cs deleted file mode 100644 index 4990932a0a..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Gender.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace JsonApiDotNetCoreExample.Models -{ - public enum Gender - { - Unknown, - Male, - Female - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Passport.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Passport.cs index 16daf865f5..5b09f0398c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Passport.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Passport.cs @@ -1,58 +1,14 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -using JsonApiDotNetCoreExample.Data; -using Microsoft.AspNetCore.Authentication; namespace JsonApiDotNetCoreExample.Models { public class Passport : Identifiable { - private readonly ISystemClock _systemClock; - private int? _socialSecurityNumber; - - [Attr] - public int? SocialSecurityNumber - { - get => _socialSecurityNumber; - set - { - if (value != _socialSecurityNumber) - { - LastSocialSecurityNumberChange = _systemClock.UtcNow.LocalDateTime; - _socialSecurityNumber = value; - } - } - } - - [Attr] - public DateTime LastSocialSecurityNumberChange { get; set; } - [Attr] public bool IsLocked { get; set; } [HasOne] public Person Person { get; set; } - - [Attr] - [NotMapped] - public string BirthCountryName - { - get => BirthCountry?.Name; - set - { - BirthCountry ??= new Country(); - BirthCountry.Name = value; - } - } - - [EagerLoad] - public Country BirthCountry { get; set; } - - public Passport(AppDbContext appDbContext) - { - _systemClock = appDbContext.SystemClock; - } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs index b5d67fb5a0..46ca00f02b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs @@ -40,12 +40,6 @@ public string FirstName [Attr(PublicName = "the-Age")] public int Age { get; set; } - [Attr] - public Gender Gender { get; set; } - - [Attr] - public string Category { get; set; } - [HasMany] public ISet TodoItems { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/SuperUser.cs b/src/Examples/JsonApiDotNetCoreExample/Models/SuperUser.cs new file mode 100644 index 0000000000..660ddd3064 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/SuperUser.cs @@ -0,0 +1,10 @@ +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExample.Models +{ + public sealed class SuperUser : User + { + [Attr] + public int SecurityLevel { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index abef810db1..afa4436d8f 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -24,9 +24,6 @@ public class TodoItem : Identifiable, IIsLockable [Attr(Capabilities = AttrCapabilities.All & ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] public string CalculatedValue => "calculated"; - [Attr(Capabilities = AttrCapabilities.All & ~AttrCapabilities.AllowChange)] - public DateTimeOffset? OffsetDate { get; set; } - [HasOne] public Person Owner { get; set; } @@ -42,10 +39,6 @@ public class TodoItem : Identifiable, IIsLockable [HasOne] public TodoItemCollection Collection { get; set; } - // cyclical to-one structure - [HasOne] - public TodoItem DependentOnTodo { get; set; } - // cyclical to-many structure [HasOne] public TodoItem ParentTodo { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs index dea3f26be5..f61cf6e7ef 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs @@ -1,49 +1,14 @@ -using System; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -using JsonApiDotNetCoreExample.Data; -using Microsoft.AspNetCore.Authentication; namespace JsonApiDotNetCoreExample.Models { public class User : Identifiable { - private readonly ISystemClock _systemClock; - private string _password; - [Attr] public string UserName { get; set; } [Attr(Capabilities = AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange)] - public string Password - { - get => _password; - set - { - if (value != _password) - { - _password = value; - LastPasswordChange = _systemClock.UtcNow.LocalDateTime; - } - } - } - - [Attr] - public DateTime LastPasswordChange { get; set; } - - public User(AppDbContext appDbContext) - { - _systemClock = appDbContext.SystemClock; - } - } - - public sealed class SuperUser : User - { - [Attr] - public int SecurityLevel { get; set; } - - public SuperUser(AppDbContext appDbContext) : base(appDbContext) - { - } + public string Password { get; set; } } } diff --git a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs index a8908ad93e..b1dfec3fe3 100644 --- a/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs +++ b/test/JsonApiDotNetCoreExampleTests/ExampleFakers.cs @@ -1,8 +1,6 @@ using System; using Bogus; -using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Person = JsonApiDotNetCoreExample.Models.Person; @@ -10,8 +8,6 @@ namespace JsonApiDotNetCoreExampleTests { internal sealed class ExampleFakers : FakerContainer { - private readonly IServiceProvider _serviceProvider; - private readonly Lazy> _lazyAuthorFaker = new Lazy>(() => new Faker() .UseSeed(GetFakerSeed()) @@ -26,7 +22,11 @@ internal sealed class ExampleFakers : FakerContainer .RuleFor(article => article.Caption, f => f.Lorem.Word()) .RuleFor(article => article.Url, f => f.Internet.Url())); - private readonly Lazy> _lazyUserFaker; + private readonly Lazy> _lazyUserFaker = new Lazy>(() => + new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(user => user.UserName, f => f.Person.UserName) + .RuleFor(user => user.Password, f => f.Internet.Password())); private readonly Lazy> _lazyTodoItemFaker = new Lazy>(() => new Faker() @@ -34,17 +34,14 @@ internal sealed class ExampleFakers : FakerContainer .RuleFor(todoItem => todoItem.Description, f => f.Random.Words()) .RuleFor(todoItem => todoItem.Ordinal, f => f.Random.Long(1, 999999)) .RuleFor(todoItem => todoItem.CreatedDate, f => f.Date.Past()) - .RuleFor(todoItem => todoItem.AchievedDate, f => f.Date.Past()) - .RuleFor(todoItem => todoItem.OffsetDate, f => f.Date.FutureOffset())); + .RuleFor(todoItem => todoItem.AchievedDate, f => f.Date.Past())); private readonly Lazy> _lazyPersonFaker = new Lazy>(() => new Faker() .UseSeed(GetFakerSeed()) .RuleFor(person => person.FirstName, f => f.Person.FirstName) .RuleFor(person => person.LastName, f => f.Person.LastName) - .RuleFor(person => person.Age, f => f.Random.Int(25, 50)) - .RuleFor(person => person.Gender, f => f.PickRandom()) - .RuleFor(person => person.Category, f => f.Lorem.Word())); + .RuleFor(person => person.Age, f => f.Random.Int(25, 50))); private readonly Lazy> _lazyTagFaker = new Lazy>(() => new Faker() @@ -58,23 +55,5 @@ internal sealed class ExampleFakers : FakerContainer public Faker TodoItem => _lazyTodoItemFaker.Value; public Faker Person => _lazyPersonFaker.Value; public Faker Tag => _lazyTagFaker.Value; - - public ExampleFakers(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - - _lazyUserFaker = new Lazy>(() => - new Faker() - .UseSeed(GetFakerSeed()) - .CustomInstantiator(f => new User(ResolveDbContext())) - .RuleFor(user => user.UserName, f => f.Person.UserName) - .RuleFor(user => user.Password, f => f.Internet.Password())); - } - - private AppDbContext ResolveDbContext() - { - using var scope = _serviceProvider.CreateScope(); - return scope.ServiceProvider.GetRequiredService(); - } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs index fb9d31449b..70147ee66a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs @@ -20,7 +20,7 @@ public sealed class PaginationWithTotalCountTests : IClassFixture _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); public PaginationWithTotalCountTests(ExampleIntegrationTestContext testContext) { @@ -35,8 +35,6 @@ public PaginationWithTotalCountTests(ExampleIntegrationTestContext _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); public PaginationWithoutTotalCountTests(ExampleIntegrationTestContext testContext) { @@ -28,8 +28,6 @@ public PaginationWithoutTotalCountTests(ExampleIntegrationTestContext> { private readonly ExampleIntegrationTestContext _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); private const int _defaultPageSize = 5; @@ -27,8 +27,6 @@ public RangeValidationTests(ExampleIntegrationTestContext options.DefaultPageSize = new PageSize(_defaultPageSize); options.MaximumPageSize = null; options.MaximumPageNumber = null; - - _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs index 506d13152d..f7b744e9bb 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHookTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Net; @@ -23,7 +24,7 @@ public sealed class ResourceHookTests : IClassFixture, AppDbContext>> { private readonly ExampleIntegrationTestContext, AppDbContext> _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); public ResourceHookTests(ExampleIntegrationTestContext, AppDbContext> testContext) { @@ -36,13 +37,13 @@ public ResourceHookTests(ExampleIntegrationTestContext, PersonHooksDefinition>(); services.AddScoped, TagHooksDefinition>(); services.AddScoped, TodoItemHooksDefinition>(); + + services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); }); var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.DisableTopPagination = false; options.DisableChildrenPagination = false; - - _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] @@ -205,12 +206,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Article_through_secondary_endpoint_is_hidden() { // Arrange - var articles = _fakers.Article.Generate(3); string toBeExcluded = "This should not be included"; - articles[0].Caption = toBeExcluded; var author = _fakers.Author.Generate(); - author.Articles = articles; + author.Articles = _fakers.Article.Generate(3); + author.Articles[0].Caption = toBeExcluded; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -234,10 +234,10 @@ public async Task Passport_Through_Secondary_Endpoint_Is_Hidden() { // Arrange var person = _fakers.Person.Generate(); + person.Passport = new Passport {IsLocked = true}; await _testContext.RunOnDatabaseAsync(async dbContext => { - person.Passport = new Passport(dbContext) {IsLocked = true}; dbContext.People.Add(person); await dbContext.SaveChangesAsync(); }); @@ -257,28 +257,27 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Tag_is_hidden() { // Arrange - var article = _fakers.Article.Generate(); - var tags = _fakers.Tag.Generate(2); string toBeExcluded = "This should not be included"; + + var tags = _fakers.Tag.Generate(2); tags[0].Name = toBeExcluded; - var articleTags = new[] + var article = _fakers.Article.Generate(); + article.ArticleTags = new HashSet { new ArticleTag { - Article = article, Tag = tags[0] }, new ArticleTag { - Article = article, Tag = tags[1] } }; await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.ArticleTags.AddRange(articleTags); + dbContext.Articles.Add(article); await dbContext.SaveChangesAsync(); }); @@ -308,11 +307,10 @@ public async Task Cascade_permission_error_create_ToOne_relationship() // Arrange var lockedPerson = _fakers.Person.Generate(); lockedPerson.IsLocked = true; + lockedPerson.Passport = new Passport(); await _testContext.RunOnDatabaseAsync(async dbContext => { - var passport = new Passport(dbContext); - lockedPerson.Passport = passport; dbContext.People.Add(lockedPerson); await dbContext.SaveChangesAsync(); }); @@ -355,16 +353,13 @@ public async Task Cascade_permission_error_updating_ToOne_relationship() { // Arrange var person = _fakers.Person.Generate(); - Passport newPassport = null; + person.Passport = new Passport {IsLocked = true}; + + var newPassport = new Passport(); await _testContext.RunOnDatabaseAsync(async dbContext => { - var passport = new Passport(dbContext) {IsLocked = true}; - person.Passport = passport; - dbContext.People.Add(person); - - newPassport = new Passport(dbContext); - dbContext.Passports.Add(newPassport); + dbContext.AddRange(person, newPassport); await dbContext.SaveChangesAsync(); }); @@ -407,15 +402,13 @@ public async Task Cascade_permission_error_updating_ToOne_relationship_deletion( { // Arrange var person = _fakers.Person.Generate(); + person.Passport = new Passport {IsLocked = true}; + + var newPassport = new Passport(); await _testContext.RunOnDatabaseAsync(async dbContext => { - var passport = new Passport(dbContext) {IsLocked = true}; - person.Passport = passport; - dbContext.People.Add(person); - - var newPassport = new Passport(dbContext); - dbContext.Passports.Add(newPassport); + dbContext.AddRange(person, newPassport); await dbContext.SaveChangesAsync(); }); @@ -455,11 +448,10 @@ public async Task Cascade_permission_error_delete_ToOne_relationship() // Arrange var lockedPerson = _fakers.Person.Generate(); lockedPerson.IsLocked = true; + lockedPerson.Passport = new Passport(); await _testContext.RunOnDatabaseAsync(async dbContext => { - var passport = new Passport(dbContext); - lockedPerson.Passport = passport; dbContext.People.Add(lockedPerson); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs index af60191944..fdc523810d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -1,9 +1,7 @@ using JsonApiDotNetCore.Configuration; -using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using TestBuildingBlocks; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks { @@ -17,8 +15,6 @@ public ResourceHooksStartup(IConfiguration configuration) public override void ConfigureServices(IServiceCollection services) { - services.AddSingleton(); - base.ConfigureServices(services); services.AddControllersFromExampleProject(); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs index 79b8b21629..bf9b98f424 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs @@ -16,13 +16,11 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Sorting public sealed class SortTests : IClassFixture> { private readonly ExampleIntegrationTestContext _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); public SortTests(ExampleIntegrationTestContext testContext) { _testContext = testContext; - - _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs index 37db55b381..e478129b84 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs @@ -19,7 +19,7 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets public sealed class SparseFieldSetTests : IClassFixture> { private readonly ExampleIntegrationTestContext _testContext; - private readonly ExampleFakers _fakers; + private readonly ExampleFakers _fakers = new ExampleFakers(); public SparseFieldSetTests(ExampleIntegrationTestContext testContext) { @@ -36,8 +36,6 @@ public SparseFieldSetTests(ExampleIntegrationTestContext services.AddScoped, JsonApiResourceService
>(); }); - - _fakers = new ExampleFakers(testContext.Factory.Services); } [Fact] diff --git a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs index 0d8b177ddc..27acbf0439 100644 --- a/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs +++ b/test/UnitTests/Data/EntityFrameworkCoreRepositoryTests.cs @@ -15,7 +15,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using Moq; -using TestBuildingBlocks; using Xunit; namespace UnitTests.Data @@ -102,7 +101,7 @@ private AppDbContext GetDbContext(Guid? seed = null) var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: $"IntegrationDatabaseRepository{actualSeed}") .Options; - var context = new AppDbContext(options, new FrozenSystemClock()); + var context = new AppDbContext(options); context.RemoveRange(context.TodoItems); return context; diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index bc6318b774..533e69b5a0 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -14,11 +14,9 @@ using JsonApiDotNetCore.Services; using JsonApiDotNetCoreExample.Data; using JsonApiDotNetCoreExample.Models; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using TestBuildingBlocks; using Xunit; namespace UnitTests.Extensions @@ -31,7 +29,6 @@ public void AddJsonApiInternals_Adds_All_Required_Services() // Arrange var services = new ServiceCollection(); services.AddLogging(); - services.AddSingleton(); services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb")); services.AddJsonApi(); @@ -66,7 +63,6 @@ public void RegisterResource_DeviatingDbContextPropertyName_RegistersCorrectly() // Arrange var services = new ServiceCollection(); services.AddLogging(); - services.AddSingleton(); services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb")); services.AddJsonApi(); diff --git a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs index 71a4f9642c..45f5125cb7 100644 --- a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs @@ -2,10 +2,6 @@ using System.ComponentModel.Design; using System.Linq.Expressions; using JsonApiDotNetCore.Resources; -using JsonApiDotNetCoreExample.Data; -using Microsoft.AspNetCore.Authentication; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; using Xunit; namespace UnitTests.Models @@ -30,34 +26,6 @@ public void When_resource_has_default_constructor_it_must_succeed() Assert.NotNull(resource); } - [Fact] - public void When_resource_has_constructor_with_injectable_parameter_it_must_succeed() - { - // Arrange - var contextOptions = new DbContextOptionsBuilder().Options; - var systemClock = new FrozenSystemClock(); - var appDbContext = new AppDbContext(contextOptions, systemClock); - - using var serviceContainer = new ServiceContainer(); - serviceContainer.AddService(typeof(DbContextOptions), contextOptions); - serviceContainer.AddService(typeof(ISystemClock), systemClock); - serviceContainer.AddService(typeof(AppDbContext), appDbContext); - - var factory = new ResourceFactory(serviceContainer); - - // Act - NewExpression newExpression = factory.CreateNewExpression(typeof(ResourceWithDbContextConstructor)); - - // Assert - var function = Expression - .Lambda>(newExpression) - .Compile(); - - ResourceWithDbContextConstructor resource = function(); - Assert.NotNull(resource); - Assert.Equal(appDbContext, resource.AppDbContext); - } - [Fact] public void When_resource_has_constructor_with_string_parameter_it_must_fail() { diff --git a/test/UnitTests/Models/ResourceConstructionTests.cs b/test/UnitTests/Models/ResourceConstructionTests.cs index bfae197a83..7ec46bd674 100644 --- a/test/UnitTests/Models/ResourceConstructionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionTests.cs @@ -6,11 +6,9 @@ using JsonApiDotNetCore.Serialization; using JsonApiDotNetCoreExample.Data; using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Newtonsoft.Json; -using TestBuildingBlocks; using Xunit; namespace UnitTests.Models @@ -88,41 +86,6 @@ public void When_resource_has_default_constructor_that_throws_it_must_fail() exception.Message); } - [Fact] - public void When_resource_has_constructor_with_injectable_parameter_it_must_succeed() - { - // Arrange - var graph = new ResourceGraphBuilder(new JsonApiOptions(), NullLoggerFactory.Instance) - .Add() - .Build(); - - var appDbContext = new AppDbContext(new DbContextOptionsBuilder().Options, new FrozenSystemClock()); - - var serviceContainer = new ServiceContainer(); - serviceContainer.AddService(typeof(AppDbContext), appDbContext); - - var serializer = new RequestDeserializer(graph, new ResourceFactory(serviceContainer), new TargetedFields(), _mockHttpContextAccessor.Object, _requestMock.Object); - - var body = new - { - data = new - { - id = "1", - type = "resourceWithDbContextConstructors" - } - }; - - string content = JsonConvert.SerializeObject(body); - - // Act - object result = serializer.Deserialize(content); - - // Assert - Assert.NotNull(result); - Assert.Equal(typeof(ResourceWithDbContextConstructor), result.GetType()); - Assert.Equal(appDbContext, ((ResourceWithDbContextConstructor)result).AppDbContext); - } - [Fact] public void When_resource_has_constructor_with_string_parameter_it_must_fail() { diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index bcfdba7cf1..cb0c98ae8f 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -19,7 +19,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using Moq; -using TestBuildingBlocks; using Person = JsonApiDotNetCoreExample.Models.Person; namespace UnitTests.ResourceHooks @@ -40,8 +39,6 @@ public class HooksDummyData public HooksDummyData() { - var appDbContext = new AppDbContext(new DbContextOptionsBuilder().Options, new FrozenSystemClock()); - _resourceGraph = new ResourceGraphBuilder(new JsonApiOptions(), NullLoggerFactory.Instance) .Add() .Add() @@ -56,14 +53,12 @@ public HooksDummyData() _personFaker = new Faker().Rules((f, i) => i.Id = f.UniqueIndex + 1); _articleFaker = new Faker
().Rules((f, i) => i.Id = f.UniqueIndex + 1); - _articleTagFaker = new Faker().CustomInstantiator(f => new ArticleTag()); + _articleTagFaker = new Faker(); _identifiableArticleTagFaker = new Faker().Rules((f, i) => i.Id = f.UniqueIndex + 1); _tagFaker = new Faker() - .CustomInstantiator(f => new Tag()) .Rules((f, i) => i.Id = f.UniqueIndex + 1); _passportFaker = new Faker() - .CustomInstantiator(f => new Passport(appDbContext)) .Rules((f, i) => i.Id = f.UniqueIndex + 1); } @@ -194,7 +189,7 @@ public class HooksTestsSetup : HooksDummyData // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, constraintsMock, gpfMock, options) = CreateMocks(); - var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions, new FrozenSystemClock()) : null; + var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; var resourceGraph = new ResourceGraphBuilder(new JsonApiOptions(), NullLoggerFactory.Instance) .Add() @@ -230,7 +225,7 @@ public class HooksTestsSetup : HooksDummyData // mocking the genericServiceFactory and JsonApiContext and wiring them up. var (ufMock, constraintsMock, gpfMock, options) = CreateMocks(); - var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions, new FrozenSystemClock()) : null; + var dbContext = repoDbContextOptions != null ? new AppDbContext(repoDbContextOptions) : null; var resourceGraph = new ResourceGraphBuilder(new JsonApiOptions(), NullLoggerFactory.Instance) .Add() @@ -281,7 +276,7 @@ protected DbContextOptions InitInMemoryDb(Action seeder .UseInMemoryDatabase(databaseName: "repository_mock") .Options; - using (var context = new AppDbContext(options, new FrozenSystemClock())) + using (var context = new AppDbContext(options)) { seeder(context); ResolveInverseRelationships(context); From de40fb71f773883486c58a8cc61466e50440bebe Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:11:57 +0100 Subject: [PATCH 30/47] Revert "Enable concurrent testruns (makes running all tests 60% faster)" to investigate why cibuild hangs This reverts commit d3ff09a1a1ca2ed8c6bd8a7e47c6b7733cecbd6f. --- .../JsonApiDotNetCoreExampleTests.csproj | 6 ++++++ test/JsonApiDotNetCoreExampleTests/xunit.runner.json | 5 +++++ 2 files changed, 11 insertions(+) create mode 100644 test/JsonApiDotNetCoreExampleTests/xunit.runner.json diff --git a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj index 8bf9f701f4..77e604e150 100644 --- a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj +++ b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj @@ -3,6 +3,12 @@ $(NetCoreAppVersion) + + + PreserveNewest + + + diff --git a/test/JsonApiDotNetCoreExampleTests/xunit.runner.json b/test/JsonApiDotNetCoreExampleTests/xunit.runner.json new file mode 100644 index 0000000000..8f5f10571b --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} From bd11b521cb8b2cf51f3eca88831fa6a49d9f26ed Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:32:03 +0100 Subject: [PATCH 31/47] fixed failing testrunner --- test/TestBuildingBlocks/TestBuildingBlocks.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 42f159dfa1..6f81675106 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -8,6 +8,7 @@ + From b0ac024e3b6180c2e45bce3f1c0b491ce0d46f29 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:43:57 +0100 Subject: [PATCH 32/47] Enable running integration tests in parallel --- test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj | 6 ------ test/NoEntityFrameworkTests/xunit.runner.json | 4 ---- 2 files changed, 10 deletions(-) delete mode 100644 test/NoEntityFrameworkTests/xunit.runner.json diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index 42964abb58..91b2c655e7 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -3,12 +3,6 @@ $(NetCoreAppVersion) - - - PreserveNewest - - - diff --git a/test/NoEntityFrameworkTests/xunit.runner.json b/test/NoEntityFrameworkTests/xunit.runner.json deleted file mode 100644 index 9db029ba52..0000000000 --- a/test/NoEntityFrameworkTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -} From f343d084fb29b8ce83ba819e3ef123dbaf24400d Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:50:38 +0100 Subject: [PATCH 33/47] test From 38c127acf5d8b5cf7ed0b83ef65de5b25761c0e2 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 19:57:21 +0100 Subject: [PATCH 34/47] Disable duplicate builds --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index a3aca5fccc..ba94dfde6b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,9 @@ version: '{build}' os: Visual Studio 2019 +# Do not build feature branch with open Pull Requests +skip_branch_with_pr: true + environment: PGUSER: postgres PGPASSWORD: Password12! From 9ee3fc59d03df16771ed126cb1be6ce39756bddf Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 4 Feb 2021 20:01:44 +0100 Subject: [PATCH 35/47] Revert "Disable duplicate builds" This reverts commit 38c127acf5d8b5cf7ed0b83ef65de5b25761c0e2. --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ba94dfde6b..a3aca5fccc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,6 @@ version: '{build}' os: Visual Studio 2019 -# Do not build feature branch with open Pull Requests -skip_branch_with_pr: true - environment: PGUSER: postgres PGPASSWORD: Password12! From 59fccd81e08248476004dcc0d06813d78a3cd576 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 5 Feb 2021 10:06:20 +0100 Subject: [PATCH 36/47] Addressed review feedback --- .../ExceptionHandling/ConsumerArticleService.cs | 4 +++- .../ExceptionHandling/ExceptionHandlerTests.cs | 2 +- .../IntegrationTests/NamingConventions/KebabCasingTests.cs | 4 ++-- .../IntegrationTests/RestrictedControllers/Bed.cs | 3 +++ .../IntegrationTests/RestrictedControllers/Chair.cs | 4 +++- .../IntegrationTests/RestrictedControllers/Sofa.cs | 3 +++ .../IntegrationTests/RestrictedControllers/Table.cs | 4 +++- 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs index 17daa6233a..2d867ea0f7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ConsumerArticleService.cs @@ -13,6 +13,8 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ExceptionHandling { public sealed class ConsumerArticleService : JsonApiResourceService { + public const string UnavailableArticlePrefix = "X"; + private const string _supportEmailAddress = "company@email.com"; public ConsumerArticleService(IResourceRepositoryAccessor repositoryAccessor, IQueryLayerComposer queryLayerComposer, @@ -28,7 +30,7 @@ public override async Task GetAsync(int id, CancellationToken c { var consumerArticle = await base.GetAsync(id, cancellationToken); - if (consumerArticle.Code.StartsWith("X")) + if (consumerArticle.Code.StartsWith(UnavailableArticlePrefix)) { throw new ConsumerArticleIsNoLongerAvailableException(consumerArticle.Code, _supportEmailAddress); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs index a03f9060aa..09e946dc11 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -57,7 +57,7 @@ public async Task Logs_and_produces_error_response_for_custom_exception() var consumerArticle = new ConsumerArticle { - Code = "X123" + Code = ConsumerArticleService.UnavailableArticlePrefix + "123" }; await _testContext.RunOnDatabaseAsync(async dbContext => diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs index a19dee6ab3..08d4d28f3f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -135,7 +135,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Cannot_create_resource_for_invalid_request_body() + public async Task Applies_casing_convention_on_error_stack_trace() { // Arrange var requestBody = "{ \"data\": {"; @@ -155,7 +155,7 @@ public async Task Cannot_create_resource_for_invalid_request_body() } [Fact] - public async Task Cannot_update_resource_for_invalid_attribute() + public async Task Applies_casing_convention_on_source_pointer_from_ModelState() { // Arrange var existingBoard = _fakers.DivingBoard.Generate(); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs index 3aa4747c9c..8c84293143 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Bed.cs @@ -1,8 +1,11 @@ using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class Bed : Identifiable { + [Attr] + public bool IsDouble { get; set; } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs index e80794f0e7..1948563f01 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Chair.cs @@ -1,9 +1,11 @@ using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class Chair : Identifiable { - + [Attr] + public int LegCount { get; set; } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs index 4c5c84ce20..c812e24b01 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Sofa.cs @@ -1,8 +1,11 @@ using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class Sofa : Identifiable { + [Attr] + public int SeatCount { get; set; } } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs index af649cc581..6d07ba65d6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/Table.cs @@ -1,9 +1,11 @@ using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.RestrictedControllers { public sealed class Table : Identifiable { - + [Attr] + public int LegCount { get; set; } } } From 21b94c5f30738dd83905ffe1d3b377f575098364 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 5 Feb 2021 10:52:44 +0100 Subject: [PATCH 37/47] Moved integration tests for filter/include/page/sort/fields into QueryStrings subfolder and auto-adjusted namespaces. --- .../IntegrationTests/CompositeKeys/CompositeKeyTests.cs | 1 + .../IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs | 1 + .../ContentNegotiation/ContentTypeHeaderTests.cs | 1 + .../ControllerActionResults/ActionResultTests.cs | 1 + .../CustomRoutes/ApiControllerAttributeTests.cs | 1 + .../IntegrationTests/CustomRoutes/CustomRouteTests.cs | 1 + .../IntegrationTests/EagerLoading/EagerLoadingTests.cs | 1 + .../ExceptionHandling/ExceptionHandlerTests.cs | 1 + .../IntegrationTests/IdObfuscation/IdObfuscationTests.cs | 1 + .../ModelStateValidation/NoModelStateValidationTests.cs | 1 + .../NamingConventions/KebabCasingConventionStartup.cs | 1 + .../{ => QueryStrings}/Filtering/FilterDataTypeTests.cs | 3 ++- .../{ => QueryStrings}/Filtering/FilterDbContext.cs | 2 +- .../{ => QueryStrings}/Filtering/FilterDepthTests.cs | 2 +- .../{ => QueryStrings}/Filtering/FilterOperatorTests.cs | 3 ++- .../{ => QueryStrings}/Filtering/FilterTests.cs | 2 +- .../{ => QueryStrings}/Filtering/FilterableResource.cs | 2 +- .../Filtering/FilterableResourcesController.cs | 2 +- .../{ => QueryStrings}/Includes/IncludeTests.cs | 2 +- .../Pagination/PaginationWithTotalCountTests.cs | 2 +- .../Pagination/PaginationWithoutTotalCountTests.cs | 2 +- .../{ => QueryStrings}/Pagination/RangeValidationTests.cs | 2 +- .../Pagination/RangeValidationWithMaximumTests.cs | 2 +- .../IntegrationTests/QueryStrings/QueryStringTests.cs | 2 +- .../QueryStrings/SerializerDefaultValueHandlingTests.cs | 1 + .../QueryStrings/SerializerNullValueHandlingTests.cs | 1 + .../IntegrationTests/{ => QueryStrings}/Sorting/SortTests.cs | 2 +- .../{ => QueryStrings}/SparseFieldSets/ResourceCaptureStore.cs | 2 +- .../SparseFieldSets/ResultCapturingRepository.cs | 2 +- .../{ => QueryStrings}/SparseFieldSets/SparseFieldSetTests.cs | 2 +- .../IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs | 1 + .../Creating/CreateResourceWithClientGeneratedIdTests.cs | 1 + .../Creating/CreateResourceWithToManyRelationshipTests.cs | 1 + .../Creating/CreateResourceWithToOneRelationshipTests.cs | 1 + .../IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs | 1 + .../ReadWrite/Fetching/FetchRelationshipTests.cs | 1 + .../IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs | 1 + .../Updating/Relationships/AddToToManyRelationshipTests.cs | 1 + .../Relationships/RemoveFromToManyRelationshipTests.cs | 1 + .../Updating/Relationships/ReplaceToManyRelationshipTests.cs | 1 + .../Updating/Relationships/UpdateToOneRelationshipTests.cs | 1 + .../Updating/Resources/ReplaceToManyRelationshipTests.cs | 1 + .../ReadWrite/Updating/Resources/UpdateResourceTests.cs | 1 + .../Updating/Resources/UpdateToOneRelationshipTests.cs | 1 + .../RequiredRelationships/DefaultBehaviorTests.cs | 1 + .../ResourceConstructorInjection/ResourceInjectionTests.cs | 1 + .../ResourceDefinitionQueryCallbackTests.cs | 1 + .../IntegrationTests/ResourceHooks/ResourceHooksStartup.cs | 1 + .../IntegrationTests/ResourceInheritance/InheritanceTests.cs | 1 + .../RestrictedControllers/DisableQueryStringTests.cs | 1 + .../RestrictedControllers/HttpReadOnlyTests.cs | 1 + .../RestrictedControllers/NoHttpDeleteTests.cs | 1 + .../IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs | 1 + .../IntegrationTests/RestrictedControllers/NoHttpPostTests.cs | 1 + .../IntegrationTests/Serialization/SerializationTests.cs | 1 + .../IntegrationTests/SoftDeletion/SoftDeletionTests.cs | 1 + .../IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs | 1 + .../IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs | 1 + test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs | 2 +- 59 files changed, 61 insertions(+), 18 deletions(-) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterDataTypeTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterDbContext.cs (78%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterDepthTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterOperatorTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterTests.cs (98%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterableResource.cs (95%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Filtering/FilterableResourcesController.cs (86%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Includes/IncludeTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Pagination/PaginationWithTotalCountTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Pagination/PaginationWithoutTotalCountTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Pagination/RangeValidationTests.cs (98%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Pagination/RangeValidationWithMaximumTests.cs (98%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/Sorting/SortTests.cs (99%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/SparseFieldSets/ResourceCaptureStore.cs (83%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/SparseFieldSets/ResultCapturingRepository.cs (94%) rename test/JsonApiDotNetCoreExampleTests/IntegrationTests/{ => QueryStrings}/SparseFieldSets/SparseFieldSetTests.cs (99%) diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs index c177137cd7..ad647062c3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs index 0169c316b5..4eafc2e40a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs index 14367717ad..790370f12e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs index fa6ccebf05..3b346fb180 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ActionResultTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs index 4a3237ee9e..f124fe6ae6 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/ApiControllerAttributeTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs index 5028008eab..5978ae3b4c 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs index 1dbe44d277..76d7d9a972 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/EagerLoading/EagerLoadingTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs index 09e946dc11..6b1dd941ed 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ExceptionHandling/ExceptionHandlerTests.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs index ae8ab26b42..9b5bde276a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/IdObfuscation/IdObfuscationTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs index ca5d155cb6..f24da7a5d1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/NoModelStateValidationTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs index 44fd3af114..d871dac25e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs @@ -1,4 +1,5 @@ using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Newtonsoft.Json.Serialization; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs index 805fd8b513..e2b77af026 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs @@ -7,11 +7,12 @@ using Humanizer; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterDataTypeTests : IClassFixture, FilterDbContext>> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs similarity index 78% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDbContext.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 86db753075..ec0e971b39 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterDbContext : DbContext { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs index a0748bf943..c97d216000 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterDepthTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs @@ -13,7 +13,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterDepthTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs index 71a6f54f21..dbd6773f50 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs @@ -8,11 +8,12 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterOperatorTests : IClassFixture, FilterDbContext>> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs similarity index 98% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs index c60671a5f3..60da47cce0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs @@ -10,7 +10,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResource.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs similarity index 95% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResource.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs index be66eec1de..4be6b98e02 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResource.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterableResource : Identifiable { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResourcesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs similarity index 86% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResourcesController.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs index 7314d155d0..36f23a6c7b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Filtering/FilterableResourcesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.Services; using Microsoft.Extensions.Logging; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Filtering +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Filtering { public sealed class FilterableResourcesController : JsonApiController { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index 6afd1fd707..de09d47d3d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -14,7 +14,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Includes +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Includes { public sealed class IncludeTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs index 70147ee66a..3802fc76b0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs @@ -13,7 +13,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Pagination { public sealed class PaginationWithTotalCountTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithoutTotalCountTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithoutTotalCountTests.cs index 453494acae..242378fe02 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/PaginationWithoutTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithoutTotalCountTests.cs @@ -10,7 +10,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Pagination { public sealed class PaginationWithoutTotalCountTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs similarity index 98% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs index 6e76dae7c5..fa961ce523 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs @@ -10,7 +10,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Pagination { public sealed class RangeValidationTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationWithMaximumTests.cs similarity index 98% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationWithMaximumTests.cs index 6fe73f6df6..ea13c1643d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Pagination/RangeValidationWithMaximumTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/RangeValidationWithMaximumTests.cs @@ -9,7 +9,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Pagination +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Pagination { public sealed class RangeValidationWithMaximumTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs index b3a5db84c8..1fc4c975d0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/QueryStringTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; @@ -13,7 +14,6 @@ public sealed class QueryStringTests : IClassFixture, QueryStringDbContext>> { private readonly ExampleIntegrationTestContext, QueryStringDbContext> _testContext; - private readonly QueryStringFakers _fakers = new QueryStringFakers(); public QueryStringTests(ExampleIntegrationTestContext, QueryStringDbContext> testContext) { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs index 829cd5de4d..898b6b7ad4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerDefaultValueHandlingTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs index aaa2029c6b..5ee4f490c8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SerializerNullValueHandlingTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs index bf9b98f424..efb0219211 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs @@ -11,7 +11,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Sorting +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.Sorting { public sealed class SortTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResourceCaptureStore.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResourceCaptureStore.cs similarity index 83% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResourceCaptureStore.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResourceCaptureStore.cs index a4cef25a9f..2082031663 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResourceCaptureStore.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResourceCaptureStore.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using JsonApiDotNetCore.Resources; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.SparseFieldSets { public sealed class ResourceCaptureStore { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResultCapturingRepository.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs similarity index 94% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResultCapturingRepository.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs index 4387296f9f..1b0f8b7efc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/ResultCapturingRepository.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs @@ -7,7 +7,7 @@ using JsonApiDotNetCore.Resources; using Microsoft.Extensions.Logging; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.SparseFieldSets { /// /// Enables sparse fieldset tests to verify which fields were (not) retrieved from the database. diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs similarity index 99% rename from test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs rename to test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index e478129b84..6f412dc994 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -14,7 +14,7 @@ using TestBuildingBlocks; using Xunit; -namespace JsonApiDotNetCoreExampleTests.IntegrationTests.SparseFieldSets +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings.SparseFieldSets { public sealed class SparseFieldSetTests : IClassFixture> { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index 9c5d5e775b..4e961c5e17 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index 86ad8fd335..a82338667a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index 558c375149..652ce88661 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index ad8865d712..e9a00616fc 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs index 05f1ac71b7..3b49693fc3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs index fc34b593d2..252539df21 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index 57c49860c3..6042b9ded4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index c8ac2c66ff..1d43cb3a59 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index 7dbadd7717..550f020e73 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index bfdd768bd6..5140bbfeba 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index e0f202b068..b86ec9b952 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index 163d6b9d5e..310d833ed5 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index bc9f15e423..f4017c2492 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -6,6 +6,7 @@ using FluentAssertions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index 7c4eb34229..87c84d4327 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs index 0f8d4fdda9..9fda109f2e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RequiredRelationships/DefaultBehaviorTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs index e381cdece2..92b0783b89 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -4,6 +4,7 @@ using FluentAssertions.Common; using FluentAssertions.Extensions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs index 41e010ad5a..5c54c0e658 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/ResourceDefinitionQueryCallbackTests.cs @@ -6,6 +6,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs index fdc523810d..64685ba34e 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -1,4 +1,5 @@ using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs index 0ce3721116..602600b2b4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceInheritance/InheritanceTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceInheritance.Models; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs index 6f73c145da..323576c49d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using JsonApiDotNetCore.QueryStrings; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs index 0895e7d8b4..b2725a9d18 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/HttpReadOnlyTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs index 699d1fc7df..26a8036d80 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpDeleteTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs index 9b2d7a3ea3..b3acd90b77 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPatchTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs index 837a0a1eeb..4486093ca0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/RestrictedControllers/NoHttpPostTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs index b57aebe571..76d0ccf298 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Serialization/SerializationTests.cs @@ -6,6 +6,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs index af26d97e67..2dcd98ea40 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/SoftDeletion/SoftDeletionTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs index 412a996f57..a4193f46b0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/EmptyGuidAsKeyTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs index 8d13766ab9..a50dec9bc0 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ZeroKeys/ZeroAsKeyTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCoreExampleTests.Startups; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs index aaba661a65..3d07ea8187 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs @@ -8,7 +8,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace JsonApiDotNetCoreExampleTests +namespace JsonApiDotNetCoreExampleTests.Startups { public class TestableStartup : EmptyStartup where TDbContext : DbContext From a044cf0f63515ddea94bde85fc3bada321668b5e Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 5 Feb 2021 10:54:04 +0100 Subject: [PATCH 38/47] Move types into separate files --- .../BaseToothbrushesController.cs | 63 +++++++++++++++++++ .../ToothbrushesController.cs | 54 ---------------- .../CallableResourceDefinition.cs | 5 -- .../ResourceDefinitions/IUserRolesService.cs | 7 +++ 4 files changed, 70 insertions(+), 59 deletions(-) create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/BaseToothbrushesController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/IUserRolesService.cs diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/BaseToothbrushesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/BaseToothbrushesController.cs new file mode 100644 index 0000000000..4c7338405f --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/BaseToothbrushesController.cs @@ -0,0 +1,63 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ControllerActionResults +{ + public abstract class BaseToothbrushesController : BaseJsonApiController + { + protected BaseToothbrushesController(IJsonApiOptions options, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + + public override async Task GetAsync(int id, CancellationToken cancellationToken) + { + if (id == 11111111) + { + return NotFound(); + } + + if (id == 22222222) + { + return NotFound(new Error(HttpStatusCode.NotFound) + { + Title = "No toothbrush with that ID exists." + }); + } + + if (id == 33333333) + { + return Conflict("Something went wrong."); + } + + if (id == 44444444) + { + return Error(new Error(HttpStatusCode.BadGateway)); + } + + if (id == 55555555) + { + var errors = new[] + { + new Error(HttpStatusCode.PreconditionFailed), + new Error(HttpStatusCode.Unauthorized), + new Error(HttpStatusCode.ExpectationFailed) + { + Title = "This is not a very great request." + } + }; + return Error(errors); + } + + return await base.GetAsync(id, cancellationToken); + } + } +} \ No newline at end of file diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs index 8155d1ba3b..8cd4e1d646 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs @@ -1,9 +1,6 @@ -using System.Net; using System.Threading; using System.Threading.Tasks; using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -24,55 +21,4 @@ public override Task GetAsync(int id, CancellationToken cancellat return base.GetAsync(id, cancellationToken); } } - - public abstract class BaseToothbrushesController : BaseJsonApiController - { - protected BaseToothbrushesController(IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, loggerFactory, resourceService) - { - } - - public override async Task GetAsync(int id, CancellationToken cancellationToken) - { - if (id == 11111111) - { - return NotFound(); - } - - if (id == 22222222) - { - return NotFound(new Error(HttpStatusCode.NotFound) - { - Title = "No toothbrush with that ID exists." - }); - } - - if (id == 33333333) - { - return Conflict("Something went wrong."); - } - - if (id == 44444444) - { - return Error(new Error(HttpStatusCode.BadGateway)); - } - - if (id == 55555555) - { - var errors = new[] - { - new Error(HttpStatusCode.PreconditionFailed), - new Error(HttpStatusCode.Unauthorized), - new Error(HttpStatusCode.ExpectationFailed) - { - Title = "This is not a very great request." - } - }; - return Error(errors); - } - - return await base.GetAsync(id, cancellationToken); - } - } } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/CallableResourceDefinition.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/CallableResourceDefinition.cs index a3de6f4b0d..7cfb920377 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/CallableResourceDefinition.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/CallableResourceDefinition.cs @@ -11,11 +11,6 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceDefinitions { - public interface IUserRolesService - { - bool AllowIncludeOwner { get; } - } - public sealed class CallableResourceDefinition : JsonApiResourceDefinition { private readonly IUserRolesService _userRolesService; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/IUserRolesService.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/IUserRolesService.cs new file mode 100644 index 0000000000..1343637bae --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceDefinitions/IUserRolesService.cs @@ -0,0 +1,7 @@ +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceDefinitions +{ + public interface IUserRolesService + { + bool AllowIncludeOwner { get; } + } +} \ No newline at end of file From 78bc0c392d2157844d5f420bebe997704b65ea6c Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 5 Feb 2021 11:23:02 +0100 Subject: [PATCH 39/47] Moved unit tests for query string parameters into ExampleTests project, so they can share private models with integration tests for query strings. They are likely to evolve simultaneously, so I prefer not to make a copy of them. Renamed Blog to LegacyBlog in preparation for refactoring to make query string tests run against private models. LegacyBlog is going away at the end. --- ...Controller.cs => LegacyBlogsController.cs} | 6 +- .../Data/AppDbContext.cs | 2 +- .../Models/Article.cs | 2 +- .../Models/{Blog.cs => LegacyBlog.cs} | 2 +- .../Filtering/FilterDepthTests.cs | 40 ++++++------- .../QueryStrings/Includes/IncludeTests.cs | 18 +++--- .../PaginationWithTotalCountTests.cs | 58 +++++++++---------- .../QueryStrings/Sorting/SortTests.cs | 38 ++++++------ .../SparseFieldSets/SparseFieldSetTests.cs | 26 ++++----- .../QueryStringParameters/BaseParseTests.cs | 6 +- .../DefaultsParseTests.cs | 2 +- .../QueryStringParameters/FilterParseTests.cs | 10 ++-- .../IncludeParseTests.cs | 4 +- .../LegacyFilterParseTests.cs | 2 +- .../QueryStringParameters/NullsParseTests.cs | 2 +- .../PaginationParseTests.cs | 6 +- .../QueryStringParameters/SortParseTests.cs | 10 ++-- .../SparseFieldSetParseTests.cs | 2 +- 18 files changed, 118 insertions(+), 118 deletions(-) rename src/Examples/JsonApiDotNetCoreExample/Controllers/{BlogsController.cs => LegacyBlogsController.cs} (69%) rename src/Examples/JsonApiDotNetCoreExample/Models/{Blog.cs => LegacyBlog.cs} (89%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/BaseParseTests.cs (88%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/DefaultsParseTests.cs (98%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/FilterParseTests.cs (96%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/IncludeParseTests.cs (97%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs (98%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/NullsParseTests.cs (98%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/PaginationParseTests.cs (98%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/SortParseTests.cs (95%) rename test/{ => JsonApiDotNetCoreExampleTests}/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs (98%) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/BlogsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/LegacyBlogsController.cs similarity index 69% rename from src/Examples/JsonApiDotNetCoreExample/Controllers/BlogsController.cs rename to src/Examples/JsonApiDotNetCoreExample/Controllers/LegacyBlogsController.cs index 824b7a30a6..be3dd80540 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/BlogsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/LegacyBlogsController.cs @@ -6,12 +6,12 @@ namespace JsonApiDotNetCoreExample.Controllers { - public sealed class BlogsController : JsonApiController + public sealed class LegacyBlogsController : JsonApiController { - public BlogsController( + public LegacyBlogsController( IJsonApiOptions options, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, loggerFactory, resourceService) { } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 2f3e590dac..08b6442593 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -10,7 +10,7 @@ public sealed class AppDbContext : DbContext public DbSet
Articles { get; set; } public DbSet AuthorDifferentDbContextName { get; set; } public DbSet Users { get; set; } - public DbSet Blogs { get; set; } + public DbSet Blogs { get; set; } public AppDbContext(DbContextOptions options) : base(options) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs index 455ed51039..0282f50fff 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs @@ -30,6 +30,6 @@ public sealed class Article : Identifiable public ICollection Revisions { get; set; } [HasOne] - public Blog Blog { get; set; } + public LegacyBlog Blog { get; set; } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Blog.cs b/src/Examples/JsonApiDotNetCoreExample/Models/LegacyBlog.cs similarity index 89% rename from src/Examples/JsonApiDotNetCoreExample/Models/Blog.cs rename to src/Examples/JsonApiDotNetCoreExample/Models/LegacyBlog.cs index 0330150ca3..8e38b9db3e 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Blog.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/LegacyBlog.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCoreExample.Models { - public sealed class Blog : Identifiable + public sealed class LegacyBlog : Identifiable { [Attr] public string Title { get; set; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs index c97d216000..c95d32a1a4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs @@ -101,7 +101,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_in_secondary_resources() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -123,7 +123,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?filter=equals(caption,'Two')"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?filter=equals(caption,'Two')"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -216,10 +216,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_on_HasMany_relationship() { // Arrange - var blogs = new List + var blogs = new List { - new Blog(), - new Blog + new LegacyBlog(), + new LegacyBlog { Articles = new List
{ @@ -233,13 +233,13 @@ public async Task Can_filter_on_HasMany_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?filter=greaterThan(count(articles),'0')"; + var route = "/api/v1/legacyBlogs?filter=greaterThan(count(articles),'0')"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -301,7 +301,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_in_scope_of_HasMany_relationship() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -318,13 +318,13 @@ public async Task Can_filter_in_scope_of_HasMany_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.Add(blog); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=articles&filter[articles]=equals(caption,'Two')"; + var route = "/api/v1/legacyBlogs?include=articles&filter[articles]=equals(caption,'Two')"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -342,7 +342,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_in_scope_of_HasMany_relationship_on_secondary_resource() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -368,7 +368,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/owner?include=articles&filter[articles]=equals(caption,'Two')"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/owner?include=articles&filter[articles]=equals(caption,'Two')"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -449,7 +449,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_in_scope_of_relationship_chain() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -470,13 +470,13 @@ public async Task Can_filter_in_scope_of_relationship_chain() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.Add(blog); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=owner.articles&filter[owner.articles]=equals(caption,'Two')"; + var route = "/api/v1/legacyBlogs?include=owner.articles&filter[owner.articles]=equals(caption,'Two')"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -595,10 +595,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_filter_in_multiple_scopes() { // Arrange - var blogs = new List + var blogs = new List { - new Blog(), - new Blog + new LegacyBlog(), + new LegacyBlog { Title = "Technology", Owner = new Author @@ -632,13 +632,13 @@ public async Task Can_filter_in_multiple_scopes() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=owner.articles.revisions&" + + var route = "/api/v1/legacyBlogs?include=owner.articles.revisions&" + "filter=and(equals(title,'Technology'),has(owner.articles),equals(owner.lastName,'Smith'))&" + "filter[owner.articles]=equals(caption,'Two')&" + "filter[owner.articles.revisions]=greaterThan(publishTime,'2005-05-05')"; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index de09d47d3d..3fdabbaa06 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -114,7 +114,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_include_in_secondary_resource() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -136,7 +136,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/owner?include=articles"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/owner?include=articles"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -158,7 +158,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_include_in_secondary_resources() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -180,7 +180,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?include=author"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?include=author"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -434,7 +434,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_include_chain_of_HasMany_relationships() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Title = "Some", Articles = new List
@@ -460,7 +460,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}?include=articles.revisions"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}?include=articles.revisions"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -843,7 +843,7 @@ public async Task Can_include_at_configured_maximum_inclusion_depth() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.MaximumIncludeDepth = 1; - var blog = new Blog(); + var blog = new LegacyBlog(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -852,7 +852,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?include=author,revisions"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?include=author,revisions"; // Act var (httpResponse, _) = await _testContext.ExecuteGetAsync(route); @@ -868,7 +868,7 @@ public async Task Cannot_exceed_configured_maximum_inclusion_depth() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.MaximumIncludeDepth = 1; - var route = "/api/v1/blogs/123/owner?include=articles.revisions"; + var route = "/api/v1/legacyBlogs/123/owner?include=articles.revisions"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs index 3802fc76b0..903be6f55a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs @@ -115,7 +115,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_paginate_in_secondary_resources() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -137,7 +137,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?page[number]=2&page[size]=1"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?page[number]=2&page[size]=1"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -150,10 +150,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be("http://localhost" + route); - responseDocument.Links.First.Should().Be($"http://localhost/api/v1/blogs/{blog.StringId}/articles?page[size]=1"); + responseDocument.Links.First.Should().Be($"http://localhost/api/v1/legacyBlogs/{blog.StringId}/articles?page[size]=1"); responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().Be(responseDocument.Links.First); - responseDocument.Links.Next.Should().Be($"http://localhost/api/v1/blogs/{blog.StringId}/articles?page[number]=3&page[size]=1"); + responseDocument.Links.Next.Should().Be($"http://localhost/api/v1/legacyBlogs/{blog.StringId}/articles?page[number]=3&page[size]=1"); } [Fact] @@ -191,9 +191,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_paginate_in_scope_of_HasMany_relationship() { // Arrange - var blogs = new List + var blogs = new List { - new Blog + new LegacyBlog { Articles = new List
{ @@ -207,7 +207,7 @@ public async Task Can_paginate_in_scope_of_HasMany_relationship() } } }, - new Blog + new LegacyBlog { Articles = new List
{ @@ -221,18 +221,18 @@ public async Task Can_paginate_in_scope_of_HasMany_relationship() } } }, - new Blog() + new LegacyBlog() }; await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=articles&page[number]=articles:2&page[size]=2,articles:1"; + var route = "/api/v1/legacyBlogs?include=articles&page[number]=articles:2&page[size]=2,articles:1"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -248,8 +248,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be("http://localhost" + route); - responseDocument.Links.First.Should().Be("http://localhost/api/v1/blogs?include=articles&page[size]=2,articles:1"); - responseDocument.Links.Last.Should().Be("http://localhost/api/v1/blogs?include=articles&page[number]=2&page[size]=2,articles:1"); + responseDocument.Links.First.Should().Be("http://localhost/api/v1/legacyBlogs?include=articles&page[size]=2,articles:1"); + responseDocument.Links.Last.Should().Be("http://localhost/api/v1/legacyBlogs?include=articles&page[number]=2&page[size]=2,articles:1"); responseDocument.Links.Prev.Should().BeNull(); responseDocument.Links.Next.Should().Be(responseDocument.Links.Last); } @@ -258,7 +258,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_paginate_in_scope_of_HasMany_relationship_on_secondary_resource() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -284,7 +284,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/owner?include=articles&page[number]=articles:2&page[size]=articles:1"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/owner?include=articles&page[number]=articles:2&page[size]=articles:1"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -308,7 +308,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_paginate_HasMany_relationship_on_relationship_endpoint() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -330,7 +330,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/relationships/articles?page[number]=2&page[size]=1"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/relationships/articles?page[number]=2&page[size]=1"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -343,7 +343,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be("http://localhost" + route); - responseDocument.Links.First.Should().Be($"http://localhost/api/v1/blogs/{blog.StringId}/relationships/articles?page[size]=1"); + responseDocument.Links.First.Should().Be($"http://localhost/api/v1/legacyBlogs/{blog.StringId}/relationships/articles?page[size]=1"); responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().Be(responseDocument.Links.First); responseDocument.Links.Next.Should().BeNull(); @@ -491,13 +491,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_paginate_in_multiple_scopes() { // Arrange - var blogs = new List + var blogs = new List { - new Blog + new LegacyBlog { Title = "Cooking" }, - new Blog + new LegacyBlog { Title = "Technology", Owner = new Author @@ -531,13 +531,13 @@ public async Task Can_paginate_in_multiple_scopes() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=owner.articles.revisions&" + + var route = "/api/v1/legacyBlogs?include=owner.articles.revisions&" + "page[size]=1,owner.articles:1,owner.articles.revisions:1&" + "page[number]=2,owner.articles:2,owner.articles.revisions:2"; @@ -557,8 +557,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be("http://localhost" + route); - responseDocument.Links.First.Should().Be("http://localhost/api/v1/blogs?include=owner.articles.revisions&page[size]=1,owner.articles:1,owner.articles.revisions:1"); - responseDocument.Links.Last.Should().Be("http://localhost/api/v1/blogs?include=owner.articles.revisions&page[size]=1,owner.articles:1,owner.articles.revisions:1&page[number]=2"); + responseDocument.Links.First.Should().Be("http://localhost/api/v1/legacyBlogs?include=owner.articles.revisions&page[size]=1,owner.articles:1,owner.articles.revisions:1"); + responseDocument.Links.Last.Should().Be("http://localhost/api/v1/legacyBlogs?include=owner.articles.revisions&page[size]=1,owner.articles:1,owner.articles.revisions:1&page[number]=2"); responseDocument.Links.Prev.Should().Be(responseDocument.Links.First); responseDocument.Links.Next.Should().BeNull(); } @@ -608,7 +608,7 @@ public async Task Uses_default_page_number_and_size() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.DefaultPageSize = new PageSize(2); - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -634,7 +634,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -651,7 +651,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.First.Should().Be(responseDocument.Links.Self); responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().BeNull(); - responseDocument.Links.Next.Should().Be($"http://localhost/api/v1/blogs/{blog.StringId}/articles?page[number]=2"); + responseDocument.Links.Next.Should().Be($"http://localhost/api/v1/legacyBlogs/{blog.StringId}/articles?page[number]=2"); } [Fact] @@ -661,7 +661,7 @@ public async Task Returns_all_resources_when_paging_is_disabled() var options = (JsonApiOptions) _testContext.Factory.Services.GetRequiredService(); options.DefaultPageSize = null; - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
() }; @@ -681,7 +681,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs index efb0219211..594da043b3 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs @@ -91,7 +91,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_in_secondary_resources() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Articles = new List
{ @@ -108,7 +108,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?sort=caption"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?sort=caption"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -157,9 +157,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_on_HasMany_relationship() { // Arrange - var blogs = new List + var blogs = new List { - new Blog + new LegacyBlog { Articles = new List
{ @@ -173,7 +173,7 @@ public async Task Can_sort_on_HasMany_relationship() } } }, - new Blog + new LegacyBlog { Articles = new List
{ @@ -187,13 +187,13 @@ public async Task Can_sort_on_HasMany_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?sort=count(articles)"; + var route = "/api/v1/legacyBlogs?sort=count(articles)"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -310,7 +310,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_in_scope_of_HasMany_relationship_on_secondary_resource() { // Arrange - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -331,7 +331,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/owner?include=articles&sort[articles]=caption"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/owner?include=articles&sort[articles]=caption"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -406,9 +406,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_on_multiple_fields_in_multiple_scopes() { // Arrange - var blogs = new List + var blogs = new List { - new Blog + new LegacyBlog { Title = "Z", Articles = new List
@@ -448,7 +448,7 @@ public async Task Can_sort_on_multiple_fields_in_multiple_scopes() } } }, - new Blog + new LegacyBlog { Title = "Y" } @@ -456,13 +456,13 @@ public async Task Can_sort_on_multiple_fields_in_multiple_scopes() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=articles.revisions&sort=title&sort[articles]=caption,url&sort[articles.revisions]=-publishTime"; + var route = "/api/v1/legacyBlogs?include=articles.revisions&sort=title&sort[articles]=caption,url&sort[articles.revisions]=-publishTime"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -547,13 +547,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_sort_in_multiple_scopes() { // Arrange - var blogs = new List + var blogs = new List { - new Blog + new LegacyBlog { Title = "Cooking" }, - new Blog + new LegacyBlog { Title = "Technology", Owner = new Author @@ -587,13 +587,13 @@ public async Task Can_sort_in_multiple_scopes() await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Blogs.AddRange(blogs); await dbContext.SaveChangesAsync(); }); - var route = "/api/v1/blogs?include=owner.articles.revisions&" + + var route = "/api/v1/legacyBlogs?include=owner.articles.revisions&" + "sort=-title&" + "sort[owner.articles]=-caption&" + "sort[owner.articles.revisions]=-publishTime"; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index 6f412dc994..092deca1f7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -29,7 +29,7 @@ public SparseFieldSetTests(ExampleIntegrationTestContext { services.AddSingleton(); - services.AddResourceRepository>(); + services.AddResourceRepository>(); services.AddResourceRepository>(); services.AddResourceRepository>(); services.AddResourceRepository>(); @@ -212,7 +212,7 @@ public async Task Can_select_fields_in_secondary_resources() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var blog = new Blog + var blog = new LegacyBlog { Title = "Some", Articles = new List
@@ -232,7 +232,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/articles?fields[articles]=caption,tags"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/articles?fields[articles]=caption,tags"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -249,7 +249,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.ManyData[0].Relationships["tags"].Links.Self.Should().NotBeNull(); responseDocument.ManyData[0].Relationships["tags"].Links.Related.Should().NotBeNull(); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(x => x is Blog).And.Subject.Single(); + var blogCaptured = (LegacyBlog)store.Resources.Should().ContainSingle(x => x is LegacyBlog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().BeNull(); @@ -379,7 +379,7 @@ public async Task Can_select_fields_of_HasMany_relationship_on_secondary_resourc var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var blog = new Blog + var blog = new LegacyBlog { Owner = new Author { @@ -402,7 +402,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}/owner?include=articles&fields[articles]=caption,revisions"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}/owner?include=articles&fields[articles]=caption,revisions"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -426,7 +426,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[0].Relationships["revisions"].Links.Self.Should().NotBeNull(); responseDocument.Included[0].Relationships["revisions"].Links.Related.Should().NotBeNull(); - var blogCaptured = (Blog) store.Resources.Should().ContainSingle(x => x is Blog).And.Subject.Single(); + var blogCaptured = (LegacyBlog) store.Resources.Should().ContainSingle(x => x is LegacyBlog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Owner.Should().NotBeNull(); blogCaptured.Owner.LastName.Should().Be(blog.Owner.LastName); @@ -500,7 +500,7 @@ public async Task Can_select_attributes_in_multiple_resource_types() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var blog = new Blog + var blog = new LegacyBlog { Title = "Technology", CompanyName = "Contoso", @@ -527,7 +527,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}?include=owner.articles&fields[blogs]=title&fields[authors]=firstName,lastName&fields[articles]=caption"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}?include=owner.articles&fields[legacyBlogs]=title&fields[authors]=firstName,lastName&fields[articles]=caption"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -554,7 +554,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[1].Attributes["caption"].Should().Be(blog.Owner.Articles[0].Caption); responseDocument.Included[1].Relationships.Should().BeNull(); - var blogCaptured = (Blog) store.Resources.Should().ContainSingle(x => x is Blog).And.Subject.Single(); + var blogCaptured = (LegacyBlog) store.Resources.Should().ContainSingle(x => x is LegacyBlog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().Be(blog.Title); blogCaptured.CompanyName.Should().BeNull(); @@ -575,7 +575,7 @@ public async Task Can_select_only_top_level_fields_with_multiple_includes() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - var blog = new Blog + var blog = new LegacyBlog { Title = "Technology", CompanyName = "Contoso", @@ -602,7 +602,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - var route = $"/api/v1/blogs/{blog.StringId}?include=owner.articles&fields[blogs]=title,owner"; + var route = $"/api/v1/legacyBlogs/{blog.StringId}?include=owner.articles&fields[legacyBlogs]=title,owner"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -637,7 +637,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[1].Relationships["tags"].Links.Self.Should().NotBeNull(); responseDocument.Included[1].Relationships["tags"].Links.Related.Should().NotBeNull(); - var blogCaptured = (Blog) store.Resources.Should().ContainSingle(x => x is Blog).And.Subject.Single(); + var blogCaptured = (LegacyBlog) store.Resources.Should().ContainSingle(x => x is LegacyBlog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().Be(blog.Title); blogCaptured.CompanyName.Should().BeNull(); diff --git a/test/UnitTests/QueryStringParameters/BaseParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/BaseParseTests.cs similarity index 88% rename from test/UnitTests/QueryStringParameters/BaseParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/BaseParseTests.cs index 3952d9af61..7c0bde044a 100644 --- a/test/UnitTests/QueryStringParameters/BaseParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/BaseParseTests.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging.Abstractions; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public abstract class BaseParseTests { @@ -16,7 +16,7 @@ protected BaseParseTests() Options = new JsonApiOptions(); ResourceGraph = new ResourceGraphBuilder(Options, NullLoggerFactory.Instance) - .Add() + .Add() .Add
() .Add() .Add
() @@ -27,7 +27,7 @@ protected BaseParseTests() Request = new JsonApiRequest { - PrimaryResource = ResourceGraph.GetResourceContext(), + PrimaryResource = ResourceGraph.GetResourceContext(), IsCollection = true }; } diff --git a/test/UnitTests/QueryStringParameters/DefaultsParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/DefaultsParseTests.cs similarity index 98% rename from test/UnitTests/QueryStringParameters/DefaultsParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/DefaultsParseTests.cs index 0a6e88449c..81383d00b7 100644 --- a/test/UnitTests/QueryStringParameters/DefaultsParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/DefaultsParseTests.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class DefaultsParseTests { diff --git a/test/UnitTests/QueryStringParameters/FilterParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/FilterParseTests.cs similarity index 96% rename from test/UnitTests/QueryStringParameters/FilterParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/FilterParseTests.cs index 7ebdfd5a0c..fe7e364ff8 100644 --- a/test/UnitTests/QueryStringParameters/FilterParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/FilterParseTests.cs @@ -10,7 +10,7 @@ using JsonApiDotNetCore.Resources; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class FilterParseTests : BaseParseTests { @@ -55,7 +55,7 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [Theory] [InlineData("filter[", "equals(caption,'some')", "Field name expected.")] - [InlineData("filter[caption]", "equals(url,'some')", "Relationship 'caption' does not exist on resource 'blogs'.")] + [InlineData("filter[caption]", "equals(url,'some')", "Relationship 'caption' does not exist on resource 'legacyBlogs'.")] [InlineData("filter[articles.caption]", "equals(firstName,'some')", "Relationship 'caption' in 'articles.caption' does not exist on resource 'articles'.")] [InlineData("filter[articles.author]", "equals(firstName,'some')", "Relationship 'author' in 'articles.author' must be a to-many relationship on resource 'articles'.")] [InlineData("filter[articles.revisions.author]", "equals(firstName,'some')", "Relationship 'author' in 'articles.revisions.author' must be a to-many relationship on resource 'revisions'.")] @@ -70,14 +70,14 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [InlineData("filter", "equals(count(articles),", "Count function, value between quotes, null or field name expected.")] [InlineData("filter", "equals(title,')", "' expected.")] [InlineData("filter", "equals(title,null", ") expected.")] - [InlineData("filter", "equals(null", "Field 'null' does not exist on resource 'blogs'.")] + [InlineData("filter", "equals(null", "Field 'null' does not exist on resource 'legacyBlogs'.")] [InlineData("filter", "equals(title,(", "Count function, value between quotes, null or field name expected.")] - [InlineData("filter", "equals(has(articles),'true')", "Field 'has' does not exist on resource 'blogs'.")] + [InlineData("filter", "equals(has(articles),'true')", "Field 'has' does not exist on resource 'legacyBlogs'.")] [InlineData("filter", "contains)", "( expected.")] [InlineData("filter", "contains(title,'a','b')", ") expected.")] [InlineData("filter", "contains(title,null)", "Value between quotes expected.")] [InlineData("filter[articles]", "contains(author,null)", "Attribute 'author' does not exist on resource 'articles'.")] - [InlineData("filter", "any(null,'a','b')", "Attribute 'null' does not exist on resource 'blogs'.")] + [InlineData("filter", "any(null,'a','b')", "Attribute 'null' does not exist on resource 'legacyBlogs'.")] [InlineData("filter", "any('a','b','c')", "Field name expected.")] [InlineData("filter", "any(title,'b','c',)", "Value between quotes expected.")] [InlineData("filter", "any(title,'b')", ", expected.")] diff --git a/test/UnitTests/QueryStringParameters/IncludeParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/IncludeParseTests.cs similarity index 97% rename from test/UnitTests/QueryStringParameters/IncludeParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/IncludeParseTests.cs index 281dc92cf4..3a1cee748a 100644 --- a/test/UnitTests/QueryStringParameters/IncludeParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/IncludeParseTests.cs @@ -9,7 +9,7 @@ using JsonApiDotNetCore.QueryStrings.Internal; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class IncludeParseTests : BaseParseTests { @@ -53,7 +53,7 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [InlineData("includes", ",", "Relationship name expected.")] [InlineData("includes", "articles,", "Relationship name expected.")] [InlineData("includes", "articles[", ", expected.")] - [InlineData("includes", "title", "Relationship 'title' does not exist on resource 'blogs'.")] + [InlineData("includes", "title", "Relationship 'title' does not exist on resource 'legacyBlogs'.")] [InlineData("includes", "articles.revisions.publishTime,", "Relationship 'publishTime' in 'articles.revisions.publishTime' does not exist on resource 'revisions'.")] public void Reader_Read_Fails(string parameterName, string parameterValue, string errorMessage) { diff --git a/test/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs similarity index 98% rename from test/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs index a5f9e3c6bb..67ea22c117 100644 --- a/test/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/LegacyFilterParseTests.cs @@ -9,7 +9,7 @@ using JsonApiDotNetCoreExample.Models; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class LegacyFilterParseTests : BaseParseTests { diff --git a/test/UnitTests/QueryStringParameters/NullsParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/NullsParseTests.cs similarity index 98% rename from test/UnitTests/QueryStringParameters/NullsParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/NullsParseTests.cs index 009950c644..20ab2d4eb8 100644 --- a/test/UnitTests/QueryStringParameters/NullsParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/NullsParseTests.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class NullsParseTests { diff --git a/test/UnitTests/QueryStringParameters/PaginationParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/PaginationParseTests.cs similarity index 98% rename from test/UnitTests/QueryStringParameters/PaginationParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/PaginationParseTests.cs index c70a38f814..497496573e 100644 --- a/test/UnitTests/QueryStringParameters/PaginationParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/PaginationParseTests.cs @@ -9,7 +9,7 @@ using JsonApiDotNetCore.QueryStrings.Internal; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class PaginationParseTests : BaseParseTests { @@ -66,7 +66,7 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [InlineData("articles.id", "Relationship 'id' in 'articles.id' does not exist on resource 'articles'.")] [InlineData("articles.tags.id", "Relationship 'id' in 'articles.tags.id' does not exist on resource 'tags'.")] [InlineData("articles.author", "Relationship 'author' in 'articles.author' must be a to-many relationship on resource 'articles'.")] - [InlineData("something", "Relationship 'something' does not exist on resource 'blogs'.")] + [InlineData("something", "Relationship 'something' does not exist on resource 'legacyBlogs'.")] public void Reader_Read_Page_Number_Fails(string parameterValue, string errorMessage) { // Act @@ -99,7 +99,7 @@ public void Reader_Read_Page_Number_Fails(string parameterValue, string errorMes [InlineData("articles.id", "Relationship 'id' in 'articles.id' does not exist on resource 'articles'.")] [InlineData("articles.tags.id", "Relationship 'id' in 'articles.tags.id' does not exist on resource 'tags'.")] [InlineData("articles.author", "Relationship 'author' in 'articles.author' must be a to-many relationship on resource 'articles'.")] - [InlineData("something", "Relationship 'something' does not exist on resource 'blogs'.")] + [InlineData("something", "Relationship 'something' does not exist on resource 'legacyBlogs'.")] public void Reader_Read_Page_Size_Fails(string parameterValue, string errorMessage) { // Act diff --git a/test/UnitTests/QueryStringParameters/SortParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SortParseTests.cs similarity index 95% rename from test/UnitTests/QueryStringParameters/SortParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SortParseTests.cs index 6fce86f153..60e52b45b5 100644 --- a/test/UnitTests/QueryStringParameters/SortParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SortParseTests.cs @@ -8,7 +8,7 @@ using JsonApiDotNetCore.QueryStrings.Internal; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class SortParseTests : BaseParseTests { @@ -51,12 +51,12 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [Theory] [InlineData("sort[", "id", "Field name expected.")] - [InlineData("sort[abc.def]", "id", "Relationship 'abc' in 'abc.def' does not exist on resource 'blogs'.")] + [InlineData("sort[abc.def]", "id", "Relationship 'abc' in 'abc.def' does not exist on resource 'legacyBlogs'.")] [InlineData("sort[articles.author]", "id", "Relationship 'author' in 'articles.author' must be a to-many relationship on resource 'articles'.")] [InlineData("sort", "", "-, count function or field name expected.")] [InlineData("sort", " ", "Unexpected whitespace.")] [InlineData("sort", "-", "Count function or field name expected.")] - [InlineData("sort", "abc", "Attribute 'abc' does not exist on resource 'blogs'.")] + [InlineData("sort", "abc", "Attribute 'abc' does not exist on resource 'legacyBlogs'.")] [InlineData("sort[articles]", "author", "Attribute 'author' does not exist on resource 'articles'.")] [InlineData("sort[articles]", "author.livingAddress", "Attribute 'livingAddress' in 'author.livingAddress' does not exist on resource 'authors'.")] [InlineData("sort", "-count", "( expected.")] @@ -64,8 +64,8 @@ public void Reader_Is_Enabled(StandardQueryStringParameters parametersDisabled, [InlineData("sort", "count(articles", ") expected.")] [InlineData("sort", "count(", "Field name expected.")] [InlineData("sort", "count(-abc)", "Field name expected.")] - [InlineData("sort", "count(abc)", "Relationship 'abc' does not exist on resource 'blogs'.")] - [InlineData("sort", "count(id)", "Relationship 'id' does not exist on resource 'blogs'.")] + [InlineData("sort", "count(abc)", "Relationship 'abc' does not exist on resource 'legacyBlogs'.")] + [InlineData("sort", "count(id)", "Relationship 'id' does not exist on resource 'legacyBlogs'.")] [InlineData("sort[articles]", "count(author)", "Relationship 'author' must be a to-many relationship on resource 'articles'.")] [InlineData("sort[articles]", "caption,", "-, count function or field name expected.")] [InlineData("sort[articles]", "caption:", ", expected.")] diff --git a/test/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs similarity index 98% rename from test/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs rename to test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs index fac44d731d..1e7f911c12 100644 --- a/test/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/SparseFieldSetParseTests.cs @@ -8,7 +8,7 @@ using JsonApiDotNetCore.QueryStrings.Internal; using Xunit; -namespace UnitTests.QueryStringParameters +namespace JsonApiDotNetCoreExampleTests.UnitTests.QueryStringParameters { public sealed class SparseFieldSetParseTests : BaseParseTests { From c2da909ad26369e63bd41662b359c905ed71768f Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 5 Feb 2021 23:13:32 +0100 Subject: [PATCH 40/47] Refactored query string integration tests to use private models. Changes: LegacyBlog -> Blog Title -> Title CompanyName -> PlatformName HasMany Articles -> Posts HasOne Owner -> Owner Article -> BlogPost Caption -> Caption Url -> Url HasOne Author -> Author + HasOne Reviewer HasManyThrough Tags -> Labels HasMany Revisions -> Comments HasOne Blog -> Parent Tag -> Label Name -> Name Color -> Color HasManyThrough Articles -> Posts Revision -> Comment + Text PublishTime -> CreatedAt HasOne Author -> Author HasOne Article -> Parent Author -> WebAccount FirstName/LastName -> UserName/DisplayName + Password DateOfBirth -> DateOfBirth BusinessEmail -> EmailAddress LivingAddress -> Preferences HasMany Articles -> Posts Address -> AccountPreferences + UseDarkTheme --- .../GettingStarted/Data/SampleDbContext.cs | 4 +- .../Data/AppDbContext.cs | 26 +- .../CompositeKeys/CompositeDbContext.cs | 8 +- .../Meta/ResourceMetaTests.cs | 8 +- .../QueryStrings/AccountPreferences.cs | 11 + .../IntegrationTests/QueryStrings/Blog.cs | 24 + .../IntegrationTests/QueryStrings/BlogPost.cs | 33 + .../QueryStrings/BlogPostLabel.cs | 11 + .../QueryStrings/BlogPostsController.cs | 16 + .../QueryStrings/BlogsController.cs | 16 + .../IntegrationTests/QueryStrings/Comment.cs | 21 + .../QueryStrings/CommentsController.cs | 16 + .../Filtering/FilterDataTypeTests.cs | 16 +- .../Filtering/FilterDepthTests.cs | 434 ++++-------- .../Filtering/FilterOperatorTests.cs | 19 +- .../QueryStrings/Filtering/FilterTests.cs | 41 +- .../QueryStrings/Includes/IncludeTests.cs | 647 +++++++----------- .../IntegrationTests/QueryStrings/Label.cs | 21 + .../QueryStrings/LabelColor.cs | 9 + .../PaginationWithTotalCountTests.cs | 420 ++++-------- .../PaginationWithoutTotalCountTests.cs | 87 ++- .../Pagination/RangeValidationTests.cs | 33 +- .../RangeValidationWithMaximumTests.cs | 24 +- .../QueryStrings/QueryStringDbContext.cs | 16 + .../QueryStrings/QueryStringFakers.cs | 44 ++ .../QueryStrings/Sorting/SortTests.cs | 538 ++++++--------- .../SparseFieldSets/SparseFieldSetTests.cs | 517 ++++++-------- .../QueryStrings/WebAccount.cs | 31 + .../QueryStrings/WebAccountsController.cs | 16 + .../InheritanceDbContext.cs | 10 +- 30 files changed, 1276 insertions(+), 1841 deletions(-) create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/AccountPreferences.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Blog.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPost.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPostLabel.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPostsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Comment.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/CommentsController.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Label.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/LabelColor.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/WebAccount.cs create mode 100644 test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/WebAccountsController.cs diff --git a/src/Examples/GettingStarted/Data/SampleDbContext.cs b/src/Examples/GettingStarted/Data/SampleDbContext.cs index b58223da50..95781423b6 100644 --- a/src/Examples/GettingStarted/Data/SampleDbContext.cs +++ b/src/Examples/GettingStarted/Data/SampleDbContext.cs @@ -12,9 +12,9 @@ public SampleDbContext(DbContextOptions options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - modelBuilder.Entity(); + builder.Entity(); } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 08b6442593..614f6646bc 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -16,53 +16,53 @@ public AppDbContext(DbContextOptions options) : base(options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - modelBuilder.Entity().HasBaseType(); + builder.Entity().HasBaseType(); - modelBuilder.Entity() + builder.Entity() .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); - modelBuilder.Entity() + builder.Entity() .HasOne(t => t.Assignee) .WithMany(p => p.AssignedTodoItems); - modelBuilder.Entity() + builder.Entity() .HasOne(t => t.Owner) .WithMany(p => p.TodoItems); - modelBuilder.Entity() + builder.Entity() .HasKey(bc => new {bc.ArticleId, bc.TagId}); - modelBuilder.Entity() + builder.Entity() .HasKey(bc => new {bc.ArticleId, bc.TagId}); - modelBuilder.Entity() + builder.Entity() .HasOne(t => t.StakeHolderTodoItem) .WithMany(t => t.StakeHolders) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() + builder.Entity() .HasMany(t => t.ChildrenTodos) .WithOne(t => t.ParentTodo); - modelBuilder.Entity() + builder.Entity() .HasOne(p => p.Person) .WithOne(p => p.Passport) .HasForeignKey("PassportKey") .OnDelete(DeleteBehavior.SetNull); - modelBuilder.Entity() + builder.Entity() .HasOne(p => p.OneToOnePerson) .WithOne(p => p.OneToOneTodoItem) .HasForeignKey("OneToOnePersonKey"); - modelBuilder.Entity() + builder.Entity() .HasOne(p => p.Owner) .WithMany(p => p.TodoCollections) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() + builder.Entity() .HasOne(p => p.Role) .WithOne(p => p.Person) .HasForeignKey("PersonRoleKey"); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs index 1a9017472e..0f64043bd8 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs @@ -13,17 +13,17 @@ public CompositeDbContext(DbContextOptions options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - modelBuilder.Entity() + builder.Entity() .HasKey(car => new {car.RegionId, car.LicensePlate}); - modelBuilder.Entity() + builder.Entity() .HasOne(engine => engine.Car) .WithOne(car => car.Engine) .HasForeignKey(); - modelBuilder.Entity() + builder.Entity() .HasMany(dealership => dealership.Inventory) .WithOne(car => car.Dealership); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs index 6236b7fdef..5550add9e2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Meta/ResourceMetaTests.cs @@ -26,9 +26,9 @@ public async Task Returns_resource_meta_from_ResourceDefinition() // Arrange var todoItems = new[] { - new TodoItem {Id = 1, Description = "Important: Pay the bills"}, - new TodoItem {Id = 2, Description = "Plan my birthday party"}, - new TodoItem {Id = 3, Description = "Important: Call mom"} + new TodoItem {Description = "Important: Pay the bills"}, + new TodoItem {Description = "Plan my birthday party"}, + new TodoItem {Description = "Important: Call mom"} }; await _testContext.RunOnDatabaseAsync(async dbContext => @@ -61,7 +61,7 @@ public async Task Returns_resource_meta_from_ResourceDefinition_in_included_reso { TodoItems = new HashSet { - new TodoItem {Id = 1, Description = "Important: Pay the bills"} + new TodoItem {Description = "Important: Pay the bills"} } }; diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/AccountPreferences.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/AccountPreferences.cs new file mode 100644 index 0000000000..ecc7308906 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/AccountPreferences.cs @@ -0,0 +1,11 @@ +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class AccountPreferences : Identifiable + { + [Attr] + public bool UseDarkTheme { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Blog.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Blog.cs new file mode 100644 index 0000000000..cc1e1765e9 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/Blog.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class Blog : Identifiable + { + [Attr] + public string Title { get; set; } + + [Attr] + public string PlatformName { get; set; } + + [Attr(Capabilities = AttrCapabilities.All & ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] + public bool ShowAdvertisements => PlatformName.EndsWith("(using free account)"); + + [HasMany] + public IList Posts { get; set; } + + [HasOne] + public WebAccount Owner { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPost.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPost.cs new file mode 100644 index 0000000000..10205816ab --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/QueryStrings/BlogPost.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.QueryStrings +{ + public sealed class BlogPost : Identifiable + { + [Attr] + public string Caption { get; set; } + + [Attr] + public string Url { get; set; } + + [HasOne] + public WebAccount Author { get; set; } + + [HasOne] + public WebAccount Reviewer { get; set; } + + [NotMapped] + [HasManyThrough(nameof(BlogPostLabels))] + public ISet