From cd9fa5080b4ac77f45a0f31298870f737828b44f Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:30:30 +0100 Subject: [PATCH 01/16] Basic boilerplate for writing meta tests Related to https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1500 --- .../Meta/OperationsController.cs | 13 ++ .../IntegrationTests/Meta/RequestMetaTests.cs | 173 ++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsController.cs create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsController.cs new file mode 100644 index 0000000000..3679bafad3 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsController.cs @@ -0,0 +1,13 @@ +using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; + +public sealed class OperationsController( + IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) + : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs new file mode 100644 index 0000000000..f3d7eb7730 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -0,0 +1,173 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCore.Serialization.Request.Adapters; +using JsonApiDotNetCore.Serialization.Response; +using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; +using Xunit; + +namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; + +public sealed class RequestMetaTests : IClassFixture, MetaDbContext>> +{ + private readonly IntegrationTestContext, MetaDbContext> _testContext; + private readonly MetaFakers _fakers = new(); + + public RequestMetaTests(IntegrationTestContext, MetaDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + testContext.UseController(); + + testContext.ConfigureServices(services => + { + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + + services.AddScoped(serviceProvider => + { + var documentAdapter = serviceProvider.GetRequiredService(); + var requestDocumentStore = serviceProvider.GetRequiredService(); + return new CapturingDocumentAdapter(documentAdapter, requestDocumentStore); + }); + }); + } + + [Fact] + public async Task Accepts_top_level_meta_in_patch_resource_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "supportTickets", + id = existingTicket.StringId + }, + meta = new + { + category = "bug", + priority = 1, + components = new[] + { + "login", + "single-sign-on" + }, + relatedTo = new[] + { + new + { + id = 123, + link = "https://www.ticket-system.com/bugs/123" + }, + new + { + id = 789, + link = "https://www.ticket-system.com/bugs/789" + } + } + } + }; + + string route = $"/supportTickets/{existingTicket.StringId}"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + store.Document.Meta.Should().HaveCount(4); + + store.Document.Meta.Should().ContainKey("category").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be("bug"); + }); + + store.Document.Meta.Should().ContainKey("priority").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetInt32().Should().Be(1); + }); + + store.Document.Meta.Should().ContainKey("components").WhoseValue.With(value => + { + string innerJson = value.Should().BeOfType().Subject.ToString(); + + innerJson.Should().BeJson(""" + [ + "login", + "single-sign-on" + ] + """); + }); + + store.Document.Meta.Should().ContainKey("relatedTo").WhoseValue.With(value => + { + string innerJson = value.Should().BeOfType().Subject.ToString(); + + innerJson.Should().BeJson(""" + [ + { + "id": 123, + "link": "https://www.ticket-system.com/bugs/123" + }, + { + "id": 789, + "link": "https://www.ticket-system.com/bugs/789" + } + ] + """); + }); + } + + // TODO: Add more tests, creating a mixture of: + // - Different endpoints: post resource, patch resource, post relationship, patch relationship, delete relationship, atomic:operations + // - Meta at different depths in the request body + // For example, assert on store.Document.Data.SingleValue.Meta + // See IHasMeta usage at https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/openapi/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiObjects for where meta can occur + // - Varying data structures: primitive types such as string/int/bool, arrays, dictionaries, and nested combinations of them + + private sealed class CapturingDocumentAdapter : IDocumentAdapter + { + private readonly IDocumentAdapter _innerAdapter; + private readonly RequestDocumentStore _requestDocumentStore; + + public CapturingDocumentAdapter(IDocumentAdapter innerAdapter, RequestDocumentStore requestDocumentStore) + { + ArgumentNullException.ThrowIfNull(innerAdapter); + ArgumentNullException.ThrowIfNull(requestDocumentStore); + + _innerAdapter = innerAdapter; + _requestDocumentStore = requestDocumentStore; + } + + public object? Convert(Document document) + { + _requestDocumentStore.Document = document; + return _innerAdapter.Convert(document); + } + } + + private sealed class RequestDocumentStore + { + public Document? Document { get; set; } + } +} From f0208a056c93a219aff3418c68b24a9895b37c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:30:47 +0100 Subject: [PATCH 02/16] test: accepts top level meta in POST resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 165 ++++++++++++------ 1 file changed, 114 insertions(+), 51 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index f3d7eb7730..be2ace782a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -57,31 +57,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => data = new { type = "supportTickets", - id = existingTicket.StringId - }, - meta = new - { - category = "bug", - priority = 1, - components = new[] - { - "login", - "single-sign-on" - }, - relatedTo = new[] + id = existingTicket.StringId, + attributes = new { - new - { - id = 123, - link = "https://www.ticket-system.com/bugs/123" - }, - new - { - id = 789, - link = "https://www.ticket-system.com/bugs/789" - } + description = existingTicket.Description } - } + }, + meta = GetExampleMetaData() }; string route = $"/supportTickets/{existingTicket.StringId}"; @@ -93,57 +75,138 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(4); - store.Document.Meta.Should().ContainKey("category").WhoseValue.With(value => + ValidateMetaData(store.Document.Meta); + } + + [Fact] + public async Task Accepts_top_level_meta_in_post_resource_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + var requestBody = new + { + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/supportTickets"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + + store.Document.Should().NotBeNull(); + + ValidateMetaData(store.Document.Meta); + } + + private static Object GetExampleMetaData() + { + return new + { + category = "bug", + priority = 1, + urgent = true, + components = new[] + { + "login", + "single-sign-on" + }, + relatedTo = new[] + { + new + { + id = 123, + link = "https://www.ticket-system.com/bugs/123" + }, + new + { + id = 789, + link = "https://www.ticket-system.com/bugs/789" + } + }, + contextInfo = new Dictionary + { + ["source"] = "form-submission", + ["retries"] = 1, + ["authenticated"] = false + } + }; + } + + private static void ValidateMetaData(IDictionary? meta) + { + meta.Should().NotBeNull(); + meta.Should().HaveCount(6); + + meta.Should().ContainKey("category").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; element.GetString().Should().Be("bug"); }); - store.Document.Meta.Should().ContainKey("priority").WhoseValue.With(value => + meta.Should().ContainKey("priority").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; element.GetInt32().Should().Be(1); }); - store.Document.Meta.Should().ContainKey("components").WhoseValue.With(value => + meta.Should().ContainKey("components").WhoseValue.With(value => { string innerJson = value.Should().BeOfType().Subject.ToString(); innerJson.Should().BeJson(""" - [ - "login", - "single-sign-on" - ] - """); + [ + "login", + "single-sign-on" + ] + """); }); - store.Document.Meta.Should().ContainKey("relatedTo").WhoseValue.With(value => + meta.Should().ContainKey("relatedTo").WhoseValue.With(value => { string innerJson = value.Should().BeOfType().Subject.ToString(); innerJson.Should().BeJson(""" - [ - { - "id": 123, - "link": "https://www.ticket-system.com/bugs/123" - }, - { - "id": 789, - "link": "https://www.ticket-system.com/bugs/789" - } - ] - """); + [ + { + "id": 123, + "link": "https://www.ticket-system.com/bugs/123" + }, + { + "id": 789, + "link": "https://www.ticket-system.com/bugs/789" + } + ] + """); }); - } - // TODO: Add more tests, creating a mixture of: - // - Different endpoints: post resource, patch resource, post relationship, patch relationship, delete relationship, atomic:operations - // - Meta at different depths in the request body - // For example, assert on store.Document.Data.SingleValue.Meta - // See IHasMeta usage at https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/openapi/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiObjects for where meta can occur - // - Varying data structures: primitive types such as string/int/bool, arrays, dictionaries, and nested combinations of them + meta.Should().ContainKey("contextInfo").WhoseValue.With(value => + { + string innerJson = value.Should().BeOfType().Subject.ToString(); + + innerJson.Should().BeJson(""" + { + "source": "form-submission", + "retries": 1, + "authenticated": false + } + """); + }); + } private sealed class CapturingDocumentAdapter : IDocumentAdapter { From 11a65807c0caf6247090a65fd0b7e4b668820856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:32:38 +0100 Subject: [PATCH 03/16] test: accepts top level meta in PATCH relationship request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index be2ace782a..b99f5f8ad3 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -113,6 +113,65 @@ public async Task Accepts_top_level_meta_in_post_resource_request() ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_patch_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingProductFamily); + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "supportTickets", + id = existingTicket.StringId, + attributes = new + { + description = existingTicket.Description + }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingProductFamily.StringId + } + } + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/supportTickets/{existingTicket.StringId}"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().HaveCount(1); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From d34319ec2819ce73a84e5741e499d3c66d107023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:35:03 +0100 Subject: [PATCH 04/16] test: accepts top level meta in POST relationship request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index b99f5f8ad3..702c476779 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -172,6 +172,62 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_post_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingProductFamily); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description, + }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingProductFamily.StringId + } + } + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/supportTickets"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + + store.Document.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().HaveCount(1); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From b68dd8f820cd3b9839c8df8c6fe661988b8240b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:35:35 +0100 Subject: [PATCH 05/16] test: accepts top level meta in DELETE relationship request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 702c476779..da6a6faf3d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -228,6 +228,52 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_delete_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + existingTicket.ProductFamily = existingProductFamily; + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = (object?)null, + meta = GetExampleMetaData() + }; + + string route = $"/supportTickets/{existingTicket.StringId}/relationships/productFamily"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().BeNull(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var supportTicketInDatabase = await dbContext.SupportTickets + .Include(supportTicket => supportTicket.ProductFamily) + .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); + + supportTicketInDatabase.ProductFamily.Should().BeNull(); + }); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From 1498a0b8275a91cfb3f62ca6d1d56493cb8eaa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:36:35 +0100 Subject: [PATCH 06/16] test: accepts top level meta in atomic UPDATE resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index da6a6faf3d..c170c3ec59 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -4,6 +4,7 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCore.Serialization.Request.Adapters; using JsonApiDotNetCore.Serialization.Response; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; @@ -274,6 +275,54 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_atomic_update_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + data = new + { + type = "supportTickets", + id = existingTicket.StringId, + attributes = new + { + description = existingTicket.Description + } + } + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From eeebcd958947567a829746fb6518c1ccfe2169e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:37:26 +0100 Subject: [PATCH 07/16] test: accepts top level meta in atomic ADD resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index c170c3ec59..b8723aefe6 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -323,6 +323,48 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_atomic_add_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description + } + } + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + store.Document.Should().NotBeNull(); + store.Document.Meta.Should().NotBeNull(); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From c33e9dbf678032d544f8403f89ff28e3b2042dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:38:23 +0100 Subject: [PATCH 08/16] test: accepts top level meta in atomic REMOVE resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index b8723aefe6..5f6a5ce576 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -365,6 +365,53 @@ public async Task Accepts_top_level_meta_in_atomic_add_resource_operation() ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_top_level_meta_in_atomic_remove_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "remove", + @ref = new + { + type = "supportTickets", + id = existingTicket.StringId + } + } + }, + meta = GetExampleMetaData() + }; + + string route = $"/operations"; + + // Act + (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + responseDocument.Should().BeEmpty(); + + store.Document.Should().NotBeNull(); + store.Document.Meta.Should().NotBeNull(); + + ValidateMetaData(store.Document.Meta); + } + private static Object GetExampleMetaData() { return new From 6bf58d36ccaf3824fa9a1474176e13a935c2256e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:39:03 +0100 Subject: [PATCH 09/16] test: accepts meta in data of POST resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 5f6a5ce576..dfb9b9acd7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -412,6 +412,42 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(store.Document.Meta); } + [Fact] + public async Task Accepts_meta_in_data_of_post_resource_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + var requestBody = new + { + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description + }, + meta = GetExampleMetaData() + } + }; + + string route = $"/supportTickets/{existingTicket.StringId}"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + + store.Document.Should().NotBeNull(); + store.Document.Data.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().NotBeNull(); + + ValidateMetaData(store.Document.Data.SingleValue.Meta); + } + private static Object GetExampleMetaData() { return new From 7c768eb3ec65f047961de8004ed6b4b0d60b7878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:39:44 +0100 Subject: [PATCH 10/16] test: accepts meta in relationship of POST resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index dfb9b9acd7..deeabbc640 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -448,6 +448,64 @@ public async Task Accepts_meta_in_data_of_post_resource_request() ValidateMetaData(store.Document.Data.SingleValue.Meta); } + [Fact] + public async Task Accepts_meta_in_relationship_of_post_resource_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingProductFamily); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description, + }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingProductFamily.StringId + }, + meta = GetExampleMetaData() + } + } + } + }; + + string route = $"/supportTickets"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + + store.Document.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().HaveCount(1); + store.Document.Data.SingleValue.Relationships.TryGetValue("productFamily", out var relationship).Should().BeTrue(); + relationship!.Meta.Should().NotBeNull(); + + ValidateMetaData(relationship.Meta); + } + private static Object GetExampleMetaData() { return new From b083bd2bd4815fc161948deb2dd184af5a20f443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:40:25 +0100 Subject: [PATCH 11/16] test: accepts meta in data of atomic ADD resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index deeabbc640..737454f01b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -506,6 +506,55 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(relationship.Meta); } + [Fact] + public async Task Accepts_meta_in_data_of_atomic_add_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description + }, + meta = GetExampleMetaData() + } + } + } + }; + + string route = $"/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + store.Document.Should().NotBeNull(); + store.Document.Operations.Should().NotBeNull(); + store.Document.Operations.Should().HaveCount(1); + + var operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + operation.Data.Should().NotBeNull(); + operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Meta.Should().NotBeNull(); + + ValidateMetaData(operation.Data.SingleValue.Meta); + } + private static Object GetExampleMetaData() { return new From b05bb8fe795a6b5c86184fe09bd5784b26bd77e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:44:38 +0100 Subject: [PATCH 12/16] test: accepts meta in relationship of atomic UPDATE resource request --- .../IntegrationTests/Meta/RequestMetaTests.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 737454f01b..2946552bcd 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -506,6 +506,80 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ValidateMetaData(relationship.Meta); } + [Fact] + public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingProductFamily); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "supportTickets", + attributes = new + { + description = existingTicket.Description + }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingProductFamily.StringId + }, + meta = GetExampleMetaData() + } + } + } + } + } + }; + + string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + store.Document.Should().NotBeNull(); + store.Document.Operations.Should().NotBeNull(); + store.Document.Operations.Should().HaveCount(1); + + var operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + operation.Data.Should().NotBeNull(); + operation.Data.SingleValue.Should().NotBeNull(); + + var relationships = operation.Data.SingleValue.Relationships; + relationships.Should().NotBeNull(); + relationships.Should().ContainKey("productFamily"); + + var relationship = relationships["productFamily"]; + relationship.Should().NotBeNull(); + relationship.Meta.Should().NotBeNull(); + + ValidateMetaData(relationship.Meta); + } + [Fact] public async Task Accepts_meta_in_data_of_atomic_add_resource_operation() { @@ -555,6 +629,74 @@ public async Task Accepts_meta_in_data_of_atomic_add_resource_operation() ValidateMetaData(operation.Data.SingleValue.Meta); } + [Fact] + public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + existingTicket.ProductFamily = existingProductFamily; + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + data = new + { + type = "supportTickets", + id = existingTicket.StringId, + relationships = new + { + productFamily = new + { + data = (object?)null + } + } + }, + meta = GetExampleMetaData() + } + } + }; + + string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + store.Document.Operations.Should().NotBeNull(); + store.Document.Operations.Should().HaveCount(1); + + var operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + operation.Meta.Should().NotBeNull(); + + ValidateMetaData(operation.Meta); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + var ticketInDatabase = await dbContext.SupportTickets + .Include(supportTicket => supportTicket.ProductFamily) + .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); + + ticketInDatabase.ProductFamily.Should().BeNull(); + }); + } + private static Object GetExampleMetaData() { return new From fd5d2bdd0335298b3a2b1b70bf7b15596ad998d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 09:59:56 +0100 Subject: [PATCH 13/16] refactor after execute inspect code script --- .../IntegrationTests/Meta/RequestMetaTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 2946552bcd..00e53a1114 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -104,7 +104,7 @@ public async Task Accepts_top_level_meta_in_post_resource_request() string route = $"/supportTickets"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); @@ -216,7 +216,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/supportTickets"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); @@ -255,7 +255,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/supportTickets/{existingTicket.StringId}/relationships/productFamily"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); @@ -313,7 +313,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/operations"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); @@ -491,7 +491,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/supportTickets"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); @@ -555,7 +555,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = "/operations"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); @@ -672,9 +672,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = "/operations"; // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - // Assert + // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); From 6238c918c65b4acd02b5040512cb7984b52853f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 16 Jul 2025 10:17:19 +0100 Subject: [PATCH 14/16] remove code style warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 00e53a1114..b545088ad0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -101,7 +101,7 @@ public async Task Accepts_top_level_meta_in_post_resource_request() meta = GetExampleMetaData() }; - string route = $"/supportTickets"; + const string route = "/supportTickets"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); @@ -196,7 +196,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description, + description = existingTicket.Description }, relationships = new { @@ -213,7 +213,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = GetExampleMetaData() }; - string route = $"/supportTickets"; + const string route = "/supportTickets"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); @@ -265,7 +265,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - var supportTicketInDatabase = await dbContext.SupportTickets + SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets .Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); @@ -310,7 +310,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = GetExampleMetaData() }; - string route = $"/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -351,7 +351,7 @@ public async Task Accepts_top_level_meta_in_atomic_add_resource_operation() meta = GetExampleMetaData() }; - string route = $"/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -396,7 +396,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = GetExampleMetaData() }; - string route = $"/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -471,7 +471,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description, + description = existingTicket.Description }, relationships = new { @@ -488,7 +488,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } }; - string route = $"/supportTickets"; + const string route = "/supportTickets"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); @@ -500,7 +500,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().HaveCount(1); - store.Document.Data.SingleValue.Relationships.TryGetValue("productFamily", out var relationship).Should().BeTrue(); + store.Document.Data.SingleValue.Relationships.TryGetValue("productFamily", out RelationshipObject? relationship).Should().BeTrue(); relationship!.Meta.Should().NotBeNull(); ValidateMetaData(relationship.Meta); @@ -552,7 +552,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } }; - string route = "/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -564,16 +564,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().NotBeNull(); store.Document.Operations.Should().HaveCount(1); - var operation = store.Document.Operations[0]; + AtomicOperationObject? operation = store.Document.Operations[0]; operation.Should().NotBeNull(); operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); - var relationships = operation.Data.SingleValue.Relationships; + IDictionary? relationships = operation.Data.SingleValue.Relationships; relationships.Should().NotBeNull(); relationships.Should().ContainKey("productFamily"); - var relationship = relationships["productFamily"]; + RelationshipObject? relationship = relationships["productFamily"]; relationship.Should().NotBeNull(); relationship.Meta.Should().NotBeNull(); @@ -608,7 +608,7 @@ public async Task Accepts_meta_in_data_of_atomic_add_resource_operation() } }; - string route = $"/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -620,7 +620,7 @@ public async Task Accepts_meta_in_data_of_atomic_add_resource_operation() store.Document.Operations.Should().NotBeNull(); store.Document.Operations.Should().HaveCount(1); - var operation = store.Document.Operations[0]; + AtomicOperationObject? operation = store.Document.Operations[0]; operation.Should().NotBeNull(); operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); @@ -669,7 +669,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } }; - string route = "/operations"; + const string route = "/operations"; // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); @@ -681,7 +681,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().NotBeNull(); store.Document.Operations.Should().HaveCount(1); - var operation = store.Document.Operations[0]; + AtomicOperationObject? operation = store.Document.Operations[0]; operation.Should().NotBeNull(); operation.Meta.Should().NotBeNull(); @@ -689,15 +689,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - var ticketInDatabase = await dbContext.SupportTickets + SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets .Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); - ticketInDatabase.ProductFamily.Should().BeNull(); + supportTicketInDatabase.ProductFamily.Should().BeNull(); }); } - private static Object GetExampleMetaData() + private static object GetExampleMetaData() { return new { From 062c0bc236dd83d02d866655508dfeed6825e958 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 19 Jul 2025 11:17:25 +0200 Subject: [PATCH 15/16] Rune code cleanup --- .../IntegrationTests/Meta/RequestMetaTests.cs | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index b545088ad0..f94cda2279 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -265,8 +265,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets - .Include(supportTicket => supportTicket.ProductFamily) + SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); supportTicketInDatabase.ProductFamily.Should().BeNull(); @@ -689,8 +688,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets - .Include(supportTicket => supportTicket.ProductFamily) + SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); supportTicketInDatabase.ProductFamily.Should().BeNull(); @@ -753,11 +751,11 @@ private static void ValidateMetaData(IDictionary? meta) string innerJson = value.Should().BeOfType().Subject.ToString(); innerJson.Should().BeJson(""" - [ - "login", - "single-sign-on" - ] - """); + [ + "login", + "single-sign-on" + ] + """); }); meta.Should().ContainKey("relatedTo").WhoseValue.With(value => @@ -765,17 +763,17 @@ private static void ValidateMetaData(IDictionary? meta) string innerJson = value.Should().BeOfType().Subject.ToString(); innerJson.Should().BeJson(""" - [ - { - "id": 123, - "link": "https://www.ticket-system.com/bugs/123" - }, - { - "id": 789, - "link": "https://www.ticket-system.com/bugs/789" - } - ] - """); + [ + { + "id": 123, + "link": "https://www.ticket-system.com/bugs/123" + }, + { + "id": 789, + "link": "https://www.ticket-system.com/bugs/789" + } + ] + """); }); meta.Should().ContainKey("contextInfo").WhoseValue.With(value => @@ -783,12 +781,12 @@ private static void ValidateMetaData(IDictionary? meta) string innerJson = value.Should().BeOfType().Subject.ToString(); innerJson.Should().BeJson(""" - { - "source": "form-submission", - "retries": 1, - "authenticated": false - } - """); + { + "source": "form-submission", + "retries": 1, + "authenticated": false + } + """); }); } From 36054909e66cce19fad373de2ee17e255ca4a9f1 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 19 Jul 2025 11:37:08 +0200 Subject: [PATCH 16/16] Fix inspections --- .../IntegrationTests/Meta/RequestMetaTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index f94cda2279..0a13548e1a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -265,7 +265,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) + SupportTicket supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); supportTicketInDatabase.ProductFamily.Should().BeNull(); @@ -673,7 +673,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Act (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - // Assert + // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); @@ -688,7 +688,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - SupportTicket? supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) + SupportTicket supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); supportTicketInDatabase.ProductFamily.Should().BeNull();