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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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(); From e49e5dd350d222a4939b241ae612dd41322a61ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 20 Dec 2025 18:03:07 +0000 Subject: [PATCH 17/22] Align request meta integration tests with JSON:API requirements --- .../IntegrationTests/Meta/RequestMetaTests.cs | 578 +++++++++--------- 1 file changed, 300 insertions(+), 278 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 0a13548e1a..52f6fd0caf 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -40,141 +40,97 @@ public RequestMetaTests(IntegrationTestContext, M } [Fact] - public async Task Accepts_top_level_meta_in_patch_resource_request() + public async Task Accepts_meta_in_patch_resource_request_with_to_one_relationship() { - // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + SupportTicket ticket = _fakers.SupportTicket.GenerateOne(); + ProductFamily family = _fakers.ProductFamily.GenerateOne(); - await _testContext.RunOnDatabaseAsync(async dbContext => + await _testContext.RunOnDatabaseAsync(async db => { - dbContext.SupportTickets.Add(existingTicket); - await dbContext.SaveChangesAsync(); + db.ProductFamilies.Add(family); + db.SupportTickets.Add(ticket); + await db.SaveChangesAsync(); }); - var requestBody = new - { - data = new - { - type = "supportTickets", - id = existingTicket.StringId, - attributes = new - { - description = existingTicket.Description - } - }, - meta = GetExampleMetaData() - }; - - string route = $"/supportTickets/{existingTicket.StringId}"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - 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 + var body = new { data = new { type = "supportTickets", - attributes = new + id = ticket.StringId, + relationships = new { - description = existingTicket.Description - } + productFamily = new + { + data = new { type = "productFamilies", id = family.StringId }, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() }, meta = GetExampleMetaData() }; - const string route = "/supportTickets"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + (HttpResponseMessage response, _) = + await _testContext.ExecutePatchAsync( + $"/supportTickets/{ticket.StringId}", body); - store.Document.Should().NotBeNull(); - - ValidateMetaData(store.Document.Meta); + response.ShouldHaveStatusCode(HttpStatusCode.NoContent); + ValidateMetaData(store.Document!.Meta); + ValidateMetaData(store.Document.Data.SingleValue!.Meta); } [Fact] - public async Task Accepts_top_level_meta_in_patch_relationship_request() + public async Task Accepts_meta_in_patch_resource_request_with_to_many_relationship() { - // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - - ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + ProductFamily family = _fakers.ProductFamily.GenerateOne(); + SupportTicket t1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket t2 = _fakers.SupportTicket.GenerateOne(); - await _testContext.RunOnDatabaseAsync(async dbContext => + await _testContext.RunOnDatabaseAsync(async db => { - dbContext.ProductFamilies.Add(existingProductFamily); - dbContext.SupportTickets.Add(existingTicket); - await dbContext.SaveChangesAsync(); + db.ProductFamilies.Add(family); + db.SupportTickets.AddRange(t1, t2); + await db.SaveChangesAsync(); }); - var requestBody = new + var body = new { data = new { - type = "supportTickets", - id = existingTicket.StringId, - attributes = new - { - description = existingTicket.Description - }, + type = "productFamilies", + id = family.StringId, relationships = new { - productFamily = new + tickets = new { - data = new + data = new[] { - type = "productFamilies", - id = existingProductFamily.StringId - } + new { type = "supportTickets", id = t1.StringId, meta = GetExampleMetaData() }, + new { type = "supportTickets", id = t2.StringId, meta = GetExampleMetaData() } + }, + meta = GetExampleMetaData() } - } + }, + meta = GetExampleMetaData() }, meta = GetExampleMetaData() }; - string route = $"/supportTickets/{existingTicket.StringId}"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + (HttpResponseMessage response, _) = + await _testContext.ExecutePatchAsync( + $"/productFamilies/{family.StringId}", body); - // 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); + response.ShouldHaveStatusCode(HttpStatusCode.NoContent); + ValidateMetaData(store.Document!.Meta); } [Fact] - public async Task Accepts_top_level_meta_in_post_relationship_request() + public async Task Accepts_meta_in_post_resource_request_with_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -196,7 +152,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description + description = existingTicket.Description, }, relationships = new { @@ -230,7 +186,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_top_level_meta_in_delete_relationship_request() + public async Task Accepts_meta_in_delete_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -275,7 +231,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_top_level_meta_in_atomic_update_resource_operation() + public async Task Accepts_meta_in_atomic_update_resource_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -320,52 +276,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); 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 + await _testContext.RunOnDatabaseAsync(async db => { - atomic__operations = new[] - { - new - { - op = "add", - data = new - { - type = "supportTickets", - attributes = new - { - description = existingTicket.Description - } - } - } - }, - meta = GetExampleMetaData() - }; - - const 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); + var updated = await db.SupportTickets.FirstAsync(t => t.Id == existingTicket.Id); + updated.Description.Should().Be(existingTicket.Description); + }); } [Fact] - public async Task Accepts_top_level_meta_in_atomic_remove_resource_operation() + public async Task Accepts_meta_in_atomic_remove_resource_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -411,100 +331,6 @@ 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); - } - - [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() - } - } - } - }; - - const string route = "/supportTickets"; - - // Act - (HttpResponseMessage httpResponse, _) = 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 RelationshipObject? relationship).Should().BeTrue(); - relationship!.Meta.Should().NotBeNull(); - - ValidateMetaData(relationship.Meta); - } - [Fact] public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation() { @@ -532,7 +358,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description + description = existingTicket.Description, }, relationships = new { @@ -548,7 +374,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } } } - } + }, + meta = GetExampleMetaData() }; const string route = "/operations"; @@ -577,55 +404,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => relationship.Meta.Should().NotBeNull(); 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 + await _testContext.RunOnDatabaseAsync(async db => { - atomic__operations = new[] - { - new - { - op = "add", - data = new - { - type = "supportTickets", - attributes = new - { - description = existingTicket.Description - }, - meta = GetExampleMetaData() - } - } - } - }; - - const string route = "/operations"; + var ticket = await db.SupportTickets + .Include(t => t.ProductFamily) + .FirstAsync(); - // 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); - - AtomicOperationObject? 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); + ticket.ProductFamily.Should().NotBeNull(); + }); } [Fact] @@ -695,6 +482,241 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }); } + [Fact] + public async Task Accepts_meta_in_update_to_one_relationship_operation() + { + var store = _testContext.Factory.Services.GetRequiredService(); + + SupportTicket ticket = _fakers.SupportTicket.GenerateOne(); + ProductFamily family = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async db => + { + db.ProductFamilies.Add(family); + db.SupportTickets.Add(ticket); + await db.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "supportTickets", + id = ticket.StringId, + relationship = "productFamily" + }, + data = new + { + type = "productFamilies", + id = family.StringId, + meta = GetExampleMetaData() + }, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() + }; + + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); + ValidateMetaData(store.Document.Meta); + var op = store.Document.Operations![0]; + op.Should().NotBeNull(); + ValidateMetaData(op.Meta); + op.Data.SingleValue.Should().NotBeNull(); + ValidateMetaData(op.Data.SingleValue.Meta); + + await _testContext.RunOnDatabaseAsync(async db => + { + var dbTicket = await db.SupportTickets.Include(t => t.ProductFamily).FirstAsync(t => t.Id == ticket.Id); + dbTicket.ProductFamily!.Id.Should().Be(family.Id); + }); + } + + [Fact] + public async Task Accepts_meta_in_update_to_many_relationship_operation() + { + var store = _testContext.Factory.Services.GetRequiredService(); + + ProductFamily family = _fakers.ProductFamily.GenerateOne(); + SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async db => + { + db.ProductFamilies.Add(family); + db.SupportTickets.AddRange(ticket1, ticket2); + await db.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "productFamilies", + id = family.StringId, + relationship = "tickets" + }, + data = new[] + { + new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() }, + new { type = "supportTickets", id = ticket2.StringId, meta = GetExampleMetaData() } + }, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() + }; + + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); + ValidateMetaData(store.Document.Meta); + var op = store.Document.Operations![0]; + op.Should().NotBeNull(); + ValidateMetaData(op.Meta); + foreach (var data in op.Data.ManyValue!) + ValidateMetaData(data.Meta); + + await _testContext.RunOnDatabaseAsync(async db => + { + var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + }); + } + + [Fact] + public async Task Accepts_meta_in_add_to_relationship_operation() + { + var store = _testContext.Factory.Services.GetRequiredService(); + + ProductFamily family = _fakers.ProductFamily.GenerateOne(); + SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async db => + { + db.ProductFamilies.Add(family); + db.SupportTickets.AddRange(ticket1, ticket2); + await db.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + @ref = new + { + type = "productFamilies", + id = family.StringId, + relationship = "tickets" + }, + data = new[] + { + new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() }, + new { type = "supportTickets", id = ticket2.StringId, meta = GetExampleMetaData() } + }, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() + }; + + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); + ValidateMetaData(store.Document.Meta); + var op = store.Document.Operations![0]; + op.Should().NotBeNull(); + ValidateMetaData(op.Meta); + foreach (var data in op.Data.ManyValue!) + ValidateMetaData(data.Meta); + + await _testContext.RunOnDatabaseAsync(async db => + { + var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + }); + } + + [Fact] + public async Task Accepts_meta_in_delete_from_relationship_operation() + { + var store = _testContext.Factory.Services.GetRequiredService(); + + ProductFamily family = _fakers.ProductFamily.GenerateOne(); + SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + family.Tickets = new List { ticket1, ticket2 }; + + await _testContext.RunOnDatabaseAsync(async db => + { + db.ProductFamilies.Add(family); + db.SupportTickets.AddRange(ticket1, ticket2); + await db.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "remove", + @ref = new + { + type = "productFamilies", + id = family.StringId, + relationship = "tickets" + }, + data = new[] + { + new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() } + }, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() + }; + + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); + ValidateMetaData(store.Document.Meta); + var op = store.Document.Operations![0]; + op.Should().NotBeNull(); + ValidateMetaData(op.Meta); + foreach (var data in op.Data.ManyValue!) + ValidateMetaData(data.Meta); + + await _testContext.RunOnDatabaseAsync(async db => + { + var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + dbFamily.Tickets.Should().NotContain(t => t.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + }); + } + private static object GetExampleMetaData() { return new From 26502ea087143b9af9e2c7a50b12fee610aa5b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 20 Dec 2025 18:15:20 +0000 Subject: [PATCH 18/22] fix some code styles --- .../IntegrationTests/Meta/RequestMetaTests.cs | 90 +++++++++++++------ 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 52f6fd0caf..77447bc4c9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -64,7 +64,11 @@ await _testContext.RunOnDatabaseAsync(async db => { productFamily = new { - data = new { type = "productFamilies", id = family.StringId }, + data = new + { + type = "productFamilies", + id = family.StringId + }, meta = GetExampleMetaData() } }, @@ -110,8 +114,17 @@ await _testContext.RunOnDatabaseAsync(async db => { data = new[] { - new { type = "supportTickets", id = t1.StringId, meta = GetExampleMetaData() }, - new { type = "supportTickets", id = t2.StringId, meta = GetExampleMetaData() } + new + { + type = "supportTickets", + id = t1.StringId, + meta = GetExampleMetaData() + }, + new { + type = "supportTickets", + id = t2.StringId, + meta = GetExampleMetaData() + } }, meta = GetExampleMetaData() } @@ -152,7 +165,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description, + description = existingTicket.Description }, relationships = new { @@ -358,7 +371,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description, + description = existingTicket.Description }, relationships = new { @@ -501,24 +514,24 @@ await _testContext.RunOnDatabaseAsync(async db => { atomic__operations = new[] { - new - { - op = "update", - @ref = new - { - type = "supportTickets", - id = ticket.StringId, - relationship = "productFamily" - }, - data = new + new { - type = "productFamilies", - id = family.StringId, + op = "update", + @ref = new + { + type = "supportTickets", + id = ticket.StringId, + relationship = "productFamily" + }, + data = new + { + type = "productFamilies", + id = family.StringId, + meta = GetExampleMetaData() + }, meta = GetExampleMetaData() - }, - meta = GetExampleMetaData() - } - }, + } + }, meta = GetExampleMetaData() }; @@ -571,8 +584,18 @@ await _testContext.RunOnDatabaseAsync(async db => }, data = new[] { - new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() }, - new { type = "supportTickets", id = ticket2.StringId, meta = GetExampleMetaData() } + new + { + type = "supportTickets", + id = ticket1.StringId, + meta = GetExampleMetaData() + }, + new + { + type = "supportTickets", + id = ticket2.StringId, + meta = GetExampleMetaData() + } }, meta = GetExampleMetaData() } @@ -630,8 +653,18 @@ await _testContext.RunOnDatabaseAsync(async db => }, data = new[] { - new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() }, - new { type = "supportTickets", id = ticket2.StringId, meta = GetExampleMetaData() } + new + { + type = "supportTickets", + id = ticket1.StringId, + meta = GetExampleMetaData() + }, + new + { + type = "supportTickets", + id = ticket2.StringId, + meta = GetExampleMetaData() + } }, meta = GetExampleMetaData() } @@ -690,7 +723,12 @@ await _testContext.RunOnDatabaseAsync(async db => }, data = new[] { - new { type = "supportTickets", id = ticket1.StringId, meta = GetExampleMetaData() } + new + { + type = "supportTickets", + id = ticket1.StringId, + meta = GetExampleMetaData() + } }, meta = GetExampleMetaData() } From 377d9bf99ab16793bec83295ef89d1ad7c0f7fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 20 Dec 2025 18:59:46 +0000 Subject: [PATCH 19/22] fix missing code styles warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 77447bc4c9..2fe39cca85 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -120,7 +120,8 @@ await _testContext.RunOnDatabaseAsync(async db => id = t1.StringId, meta = GetExampleMetaData() }, - new { + new + { type = "supportTickets", id = t2.StringId, meta = GetExampleMetaData() @@ -292,7 +293,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { - var updated = await db.SupportTickets.FirstAsync(t => t.Id == existingTicket.Id); + SupportTicket updated = await db.SupportTickets.FirstAsync(t => t.Id == existingTicket.Id); updated.Description.Should().Be(existingTicket.Description); }); } @@ -420,7 +421,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { - var ticket = await db.SupportTickets + SupportTicket ticket = await db.SupportTickets .Include(t => t.ProductFamily) .FirstAsync(); @@ -540,7 +541,7 @@ await _testContext.RunOnDatabaseAsync(async db => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); ValidateMetaData(store.Document.Meta); - var op = store.Document.Operations![0]; + AtomicOperationObject? op = store.Document.Operations![0]; op.Should().NotBeNull(); ValidateMetaData(op.Meta); op.Data.SingleValue.Should().NotBeNull(); @@ -548,7 +549,7 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - var dbTicket = await db.SupportTickets.Include(t => t.ProductFamily).FirstAsync(t => t.Id == ticket.Id); + SupportTicket dbTicket = await db.SupportTickets.Include(t => t.ProductFamily).FirstAsync(t => t.Id == ticket.Id); dbTicket.ProductFamily!.Id.Should().Be(family.Id); }); } @@ -599,7 +600,7 @@ await _testContext.RunOnDatabaseAsync(async db => }, meta = GetExampleMetaData() } - }, + }, meta = GetExampleMetaData() }; @@ -608,15 +609,18 @@ await _testContext.RunOnDatabaseAsync(async db => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); ValidateMetaData(store.Document.Meta); - var op = store.Document.Operations![0]; + AtomicOperationObject? op = store.Document.Operations![0]; op.Should().NotBeNull(); ValidateMetaData(op.Meta); - foreach (var data in op.Data.ManyValue!) + + foreach (ResourceObject data in op.Data.ManyValue!) + { ValidateMetaData(data.Meta); + } await _testContext.RunOnDatabaseAsync(async db => { - var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); }); @@ -677,15 +681,18 @@ await _testContext.RunOnDatabaseAsync(async db => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); ValidateMetaData(store.Document.Meta); - var op = store.Document.Operations![0]; + AtomicOperationObject? op = store.Document.Operations![0]; op.Should().NotBeNull(); ValidateMetaData(op.Meta); - foreach (var data in op.Data.ManyValue!) + + foreach (ResourceObject data in op.Data.ManyValue!) + { ValidateMetaData(data.Meta); + } await _testContext.RunOnDatabaseAsync(async db => { - var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); }); @@ -699,7 +706,12 @@ public async Task Accepts_meta_in_delete_from_relationship_operation() ProductFamily family = _fakers.ProductFamily.GenerateOne(); SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); - family.Tickets = new List { ticket1, ticket2 }; + + family.Tickets = new List + { + ticket1, + ticket2 + }; await _testContext.RunOnDatabaseAsync(async db => { @@ -712,27 +724,27 @@ await _testContext.RunOnDatabaseAsync(async db => { atomic__operations = new[] { - new - { - op = "remove", - @ref = new - { - type = "productFamilies", - id = family.StringId, - relationship = "tickets" - }, - data = new[] + new { - new + op = "remove", + @ref = new { - type = "supportTickets", - id = ticket1.StringId, - meta = GetExampleMetaData() - } - }, - meta = GetExampleMetaData() - } - }, + type = "productFamilies", + id = family.StringId, + relationship = "tickets" + }, + data = new[] + { + new + { + type = "supportTickets", + id = ticket1.StringId, + meta = GetExampleMetaData() + } + }, + meta = GetExampleMetaData() + } + }, meta = GetExampleMetaData() }; @@ -741,15 +753,18 @@ await _testContext.RunOnDatabaseAsync(async db => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); ValidateMetaData(store.Document.Meta); - var op = store.Document.Operations![0]; + AtomicOperationObject? op = store.Document.Operations![0]; op.Should().NotBeNull(); ValidateMetaData(op.Meta); - foreach (var data in op.Data.ManyValue!) + + foreach (ResourceObject data in op.Data.ManyValue!) + { ValidateMetaData(data.Meta); + } await _testContext.RunOnDatabaseAsync(async db => { - var dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); dbFamily.Tickets.Should().NotContain(t => t.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); }); From 1de1666e19bb9254b9957005e1cfd24501a4d605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 21 Dec 2025 16:31:14 +0000 Subject: [PATCH 20/22] Fix clean up code warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 2fe39cca85..596cc12462 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -293,7 +293,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket updated = await db.SupportTickets.FirstAsync(t => t.Id == existingTicket.Id); + SupportTicket updated = await db.SupportTickets.FirstAsync(ticket => ticket.Id == existingTicket.Id); updated.Description.Should().Be(existingTicket.Description); }); } @@ -422,7 +422,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { SupportTicket ticket = await db.SupportTickets - .Include(t => t.ProductFamily) + .Include(ticket => ticket.ProductFamily) .FirstAsync(); ticket.ProductFamily.Should().NotBeNull(); @@ -549,7 +549,7 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket dbTicket = await db.SupportTickets.Include(t => t.ProductFamily).FirstAsync(t => t.Id == ticket.Id); + SupportTicket dbTicket = await db.SupportTickets.Include(ticket => ticket.ProductFamily).FirstAsync(ticket => ticket.Id == ticket.Id); dbTicket.ProductFamily!.Id.Should().Be(family.Id); }); } @@ -620,9 +620,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); - dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); }); } @@ -692,9 +692,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); - dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); }); } @@ -764,9 +764,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(f => f.Tickets).FirstAsync(f => f.Id == family.Id); - dbFamily.Tickets.Should().NotContain(t => t.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(t => t.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); + dbFamily.Tickets.Should().NotContain(ticket => ticket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); }); } From 71644520c97d921e8cbf30150203563a0af477c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 21 Dec 2025 16:53:27 +0000 Subject: [PATCH 21/22] Fix inspect code warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 596cc12462..0e0a309d0c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -293,7 +293,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket updated = await db.SupportTickets.FirstAsync(ticket => ticket.Id == existingTicket.Id); + SupportTicket updated = await db.SupportTickets.FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); updated.Description.Should().Be(existingTicket.Description); }); } @@ -422,7 +422,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { SupportTicket ticket = await db.SupportTickets - .Include(ticket => ticket.ProductFamily) + .Include(supportTicket => supportTicket.ProductFamily) .FirstAsync(); ticket.ProductFamily.Should().NotBeNull(); @@ -549,7 +549,7 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket dbTicket = await db.SupportTickets.Include(ticket => ticket.ProductFamily).FirstAsync(ticket => ticket.Id == ticket.Id); + SupportTicket dbTicket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily).FirstAsync(supportTicket => supportTicket.Id == ticket.Id); dbTicket.ProductFamily!.Id.Should().Be(family.Id); }); } @@ -620,9 +620,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); - dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); }); } @@ -692,9 +692,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); - dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); }); } @@ -764,9 +764,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(family => family.Tickets).FirstAsync(family => family.Id == family.Id); - dbFamily.Tickets.Should().NotContain(ticket => ticket.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(ticket => ticket.Id == ticket2.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().NotContain(supportTicket => supportTicket.Id == ticket1.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); }); } From 03aeac886c03d4dfaef9578206494a2aa68fa92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 21 Dec 2025 17:22:52 +0000 Subject: [PATCH 22/22] Fix clean up warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 0e0a309d0c..29ed818767 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -77,9 +77,7 @@ await _testContext.RunOnDatabaseAsync(async db => meta = GetExampleMetaData() }; - (HttpResponseMessage response, _) = - await _testContext.ExecutePatchAsync( - $"/supportTickets/{ticket.StringId}", body); + (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync($"/supportTickets/{ticket.StringId}", body); response.ShouldHaveStatusCode(HttpStatusCode.NoContent); ValidateMetaData(store.Document!.Meta); @@ -135,9 +133,7 @@ await _testContext.RunOnDatabaseAsync(async db => meta = GetExampleMetaData() }; - (HttpResponseMessage response, _) = - await _testContext.ExecutePatchAsync( - $"/productFamilies/{family.StringId}", body); + (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync($"/productFamilies/{family.StringId}", body); response.ShouldHaveStatusCode(HttpStatusCode.NoContent); ValidateMetaData(store.Document!.Meta); @@ -421,9 +417,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket ticket = await db.SupportTickets - .Include(supportTicket => supportTicket.ProductFamily) - .FirstAsync(); + SupportTicket ticket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily).FirstAsync(); ticket.ProductFamily.Should().NotBeNull(); }); @@ -549,7 +543,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - SupportTicket dbTicket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily).FirstAsync(supportTicket => supportTicket.Id == ticket.Id); + SupportTicket dbTicket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) + .FirstAsync(supportTicket => supportTicket.Id == ticket.Id); + dbTicket.ProductFamily!.Id.Should().Be(family.Id); }); } @@ -620,7 +616,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets) + .FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); }); @@ -692,7 +690,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets) + .FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); }); @@ -764,7 +764,9 @@ await _testContext.RunOnDatabaseAsync(async db => await _testContext.RunOnDatabaseAsync(async db => { - ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets).FirstAsync(productFamily => productFamily.Id == family.Id); + ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets) + .FirstAsync(productFamily => productFamily.Id == family.Id); + dbFamily.Tickets.Should().NotContain(supportTicket => supportTicket.Id == ticket1.Id); dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); });