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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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); }); From 19953bdaaf44c7d7b740074d269d96b5df01937a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Fri, 26 Dec 2025 23:35:44 +0000 Subject: [PATCH 23/51] Add meta fakers and refactor --- .../IntegrationTests/Meta/MetaFakers.cs | 35 + .../IntegrationTests/Meta/RequestMetaTests.cs | 843 ++++++++++++------ 2 files changed, 596 insertions(+), 282 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs index 019dd6aa9b..4da41003d2 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs @@ -16,6 +16,41 @@ internal sealed class MetaFakers .MakeDeterministic() .RuleFor(supportTicket => supportTicket.Description, faker => faker.Lorem.Paragraph())); + private readonly Lazy>> _lazyDocumentMetaFaker = new(() => new Faker>() + .MakeDeterministic() + .CustomInstantiator(faker => new Dictionary + { + ["requestId"] = faker.Random.Guid().ToString() + })); + + private readonly Lazy>> _lazyResourceMetaFaker = new(() => new Faker>() + .MakeDeterministic() + .CustomInstantiator(faker => new Dictionary + { + ["editedBy"] = faker.Internet.UserName(), + ["revision"] = faker.Random.Int(1, 10) + })); + + private readonly Lazy>> _lazyRelationshipMetaFaker = new(() => new Faker>() + .MakeDeterministic() + .CustomInstantiator(faker => new Dictionary + { + ["source"] = faker.PickRandom("ui", "api", "import"), + ["confidence"] = faker.Random.Double(0.1, 1.0) + })); + + private readonly Lazy>> _lazyRelationshipIdentifierMetaFaker = new(() => new Faker>() + .MakeDeterministic() + .CustomInstantiator(faker => new Dictionary + { + ["index"] = faker.IndexFaker, + ["optionalNote"] = faker.Lorem.Word() + })); + public Faker ProductFamily => _lazyProductFamilyFaker.Value; public Faker SupportTicket => _lazySupportTicketFaker.Value; + public Faker> DocumentMeta => _lazyDocumentMetaFaker.Value; + public Faker> ResourceMeta => _lazyResourceMetaFaker.Value; + public Faker> RelationshipMeta => _lazyRelationshipMetaFaker.Value; + public Faker> RelationshipIdentifierMeta => _lazyRelationshipIdentifierMetaFaker.Value; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 29ed818767..704b3c59a6 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -4,7 +4,6 @@ using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCore.Serialization.Request.Adapters; using JsonApiDotNetCore.Serialization.Response; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; @@ -42,24 +41,29 @@ public RequestMetaTests(IntegrationTestContext, M [Fact] public async Task Accepts_meta_in_patch_resource_request_with_to_one_relationship() { + // Assert var store = _testContext.Factory.Services.GetRequiredService(); - SupportTicket ticket = _fakers.SupportTicket.GenerateOne(); - ProductFamily family = _fakers.ProductFamily.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async db => { - db.ProductFamilies.Add(family); - db.SupportTickets.Add(ticket); + db.ProductFamilies.Add(existingFamily); + db.SupportTickets.Add(existingTicket); await db.SaveChangesAsync(); }); - var body = new + var requestBody = new { data = new { type = "supportTickets", - id = ticket.StringId, + id = existingTicket.StringId, relationships = new { productFamily = new @@ -67,45 +71,94 @@ await _testContext.RunOnDatabaseAsync(async db => data = new { type = "productFamilies", - id = family.StringId + id = existingFamily.StringId }, - meta = GetExampleMetaData() + meta = relationshipMeta } }, - meta = GetExampleMetaData() + meta = resourceMeta }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync($"/supportTickets/{ticket.StringId}", body); + string route = $"/supportTickets/{existingTicket.StringId}"; + // Act + (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert response.ShouldHaveStatusCode(HttpStatusCode.NoContent); - ValidateMetaData(store.Document!.Meta); - ValidateMetaData(store.Document.Data.SingleValue!.Meta); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Data.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Meta.Should().HaveCount(resourceMeta.Count); + store.Document.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)resourceMeta["editedBy"]); + }); + store.Document.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)resourceMeta["revision"]); + }); + + store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + + value.Meta.Should().HaveCount(relationshipMeta.Count); + value.Meta.Should().ContainKey("source").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + value.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); + }); } [Fact] public async Task Accepts_meta_in_patch_resource_request_with_to_many_relationship() { + // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - ProductFamily family = _fakers.ProductFamily.GenerateOne(); - SupportTicket t1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket t2 = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); - await _testContext.RunOnDatabaseAsync(async db => + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => { - db.ProductFamilies.Add(family); - db.SupportTickets.AddRange(t1, t2); - await db.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); }); - var body = new + var requestBody = new { data = new { type = "productFamilies", - id = family.StringId, + id = existingFamily.StringId, relationships = new { tickets = new @@ -115,28 +168,99 @@ await _testContext.RunOnDatabaseAsync(async db => new { type = "supportTickets", - id = t1.StringId, - meta = GetExampleMetaData() + id = existingTicket1.StringId, + meta = identifierMeta1 }, new { type = "supportTickets", - id = t2.StringId, - meta = GetExampleMetaData() + id = existingTicket2.StringId, + meta = identifierMeta2 } }, - meta = GetExampleMetaData() + meta = relationshipMeta } }, - meta = GetExampleMetaData() + meta = resourceMeta }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync($"/productFamilies/{family.StringId}", body); + string route = $"/productFamilies/{existingFamily.StringId}"; + // Act + (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert response.ShouldHaveStatusCode(HttpStatusCode.NoContent); - ValidateMetaData(store.Document!.Meta); + + store.Document.Should().NotBeNull(); + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + // resource meta explicit validation + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Meta.Should().HaveCount(resourceMeta.Count); + store.Document.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)resourceMeta["editedBy"]); + }); + store.Document.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)resourceMeta["revision"]); + }); + + // relationship meta validation + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Meta.Should().HaveCount(relationshipMeta.Count); + value.Meta.Should().ContainKey("source").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + value.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); + + value.Data.ManyValue.Should().HaveCount(2); + + value.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); + value.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta1["index"]); + }); + value.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); + }); + + value.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); + value.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta2["index"]); + }); + value.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); + }); + }); } [Fact] @@ -145,13 +269,14 @@ public async Task Accepts_meta_in_post_resource_request_with_relationship() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); - ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.ProductFamilies.Add(existingProductFamily); + dbContext.ProductFamilies.Add(existingFamily); await dbContext.SaveChangesAsync(); }); @@ -162,7 +287,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description + description = newTicketDescription }, relationships = new { @@ -171,12 +296,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => data = new { type = "productFamilies", - id = existingProductFamily.StringId + id = existingFamily.StringId } } } }, - meta = GetExampleMetaData() + meta = documentMeta }; const string route = "/supportTickets"; @@ -188,11 +313,24 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); store.Document.Should().NotBeNull(); + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + 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); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Data.SingleValue.Should().NotBeNull(); + value.Data.SingleValue.Type.Should().Be("productFamilies"); + value.Data.SingleValue.Id.Should().Be(existingFamily.StringId); + }); } [Fact] @@ -201,13 +339,14 @@ public async Task Accepts_meta_in_delete_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); - ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - existingTicket.ProductFamily = existingProductFamily; + existingTicket.ProductFamily = existingFamily; dbContext.SupportTickets.Add(existingTicket); await dbContext.SaveChangesAsync(); }); @@ -215,7 +354,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => var requestBody = new { data = (object?)null, - meta = GetExampleMetaData() + meta = documentMeta }; string route = $"/supportTickets/{existingTicket.StringId}/relationships/productFamily"; @@ -227,17 +366,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); + store.Document.Data.SingleValue.Should().BeNull(); - await _testContext.RunOnDatabaseAsync(async dbContext => + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { - SupportTicket supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) - .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); - - supportTicketInDatabase.ProductFamily.Should().BeNull(); + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); }); - - ValidateMetaData(store.Document.Meta); } [Fact] @@ -246,6 +384,8 @@ public async Task Accepts_meta_in_atomic_update_resource_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); + var documentMeta = _fakers.DocumentMeta.Generate(); + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -272,7 +412,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } } }, - meta = GetExampleMetaData() + meta = documentMeta }; const string route = "/operations"; @@ -285,13 +425,26 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); - - await _testContext.RunOnDatabaseAsync(async db => + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { - SupportTicket updated = await db.SupportTickets.FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); - updated.Description.Should().Be(existingTicket.Description); + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); }); + + store.Document.Operations.Should().HaveCount(1); + + AtomicOperationObject? operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + operation.Data.Should().NotBeNull(); + + ResourceObject? resource = operation.Data.SingleValue; + resource.Should().NotBeNull(); + resource.Type.Should().Be("supportTickets"); + resource.Id.Should().Be(existingTicket.StringId); + resource.Attributes.Should().NotBeNull(); + resource.Attributes.Should().ContainKey("description"); } [Fact] @@ -300,6 +453,8 @@ public async Task Accepts_meta_in_atomic_remove_resource_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); + var documentMeta = _fakers.DocumentMeta.Generate(); + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -322,7 +477,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } } }, - meta = GetExampleMetaData() + meta = documentMeta }; const string route = "/operations"; @@ -336,9 +491,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Should().BeEmpty(); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); } [Fact] @@ -347,12 +507,16 @@ 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(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + + string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.ProductFamilies.Add(existingProductFamily); + dbContext.ProductFamilies.Add(existingFamily); await dbContext.SaveChangesAsync(); }); @@ -368,7 +532,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", attributes = new { - description = existingTicket.Description + description = newTicketDescription }, relationships = new { @@ -377,15 +541,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext => data = new { type = "productFamilies", - id = existingProductFamily.StringId + id = existingFamily.StringId }, - meta = GetExampleMetaData() + meta = relationshipMeta } - } + }, + meta = resourceMeta } } }, - meta = GetExampleMetaData() + meta = documentMeta }; const string route = "/operations"; @@ -397,29 +562,52 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); store.Document.Should().NotBeNull(); + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + 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(); - IDictionary? relationships = operation.Data.SingleValue.Relationships; - relationships.Should().NotBeNull(); - relationships.Should().ContainKey("productFamily"); - - RelationshipObject? relationship = relationships["productFamily"]; - relationship.Should().NotBeNull(); - relationship.Meta.Should().NotBeNull(); - - ValidateMetaData(relationship.Meta); - - await _testContext.RunOnDatabaseAsync(async db => + // resource/meta validation on operation data (resourceMeta -> relationshipMeta mapping) + operation.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); + operation.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)resourceMeta["editedBy"]); + }); + operation.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(val => { - SupportTicket ticket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily).FirstAsync(); + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().Be((int)resourceMeta["revision"]); + }); - ticket.ProductFamily.Should().NotBeNull(); + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Meta.Should().NotBeNull(); + value.Meta.Should().HaveCount(relationshipMeta.Count); + value.Meta.Should().ContainKey("source").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + value.Meta.Should().ContainKey("confidence").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); }); } @@ -429,12 +617,16 @@ public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operati // Arrange var store = _testContext.Factory.Services.GetRequiredService(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingProductFamily = _fakers.ProductFamily.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - existingTicket.ProductFamily = existingProductFamily; + existingTicket.ProductFamily = existingFamily; dbContext.SupportTickets.Add(existingTicket); await dbContext.SaveChangesAsync(); }); @@ -454,13 +646,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { productFamily = new { - data = (object?)null + data = (object?)null, + meta = relationshipMeta } } }, - meta = GetExampleMetaData() + meta = resourceMeta } - } + }, + meta = documentMeta }; const string route = "/operations"; @@ -472,21 +666,53 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + store.Document.Operations.Should().NotBeNull(); store.Document.Operations.Should().HaveCount(1); AtomicOperationObject? operation = store.Document.Operations[0]; operation.Should().NotBeNull(); - operation.Meta.Should().NotBeNull(); - ValidateMetaData(operation.Meta); + // operation meta explicit validation (resourceMeta) + operation.Meta.Should().HaveCount(resourceMeta.Count); + operation.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)resourceMeta["editedBy"]); + }); + operation.Meta.Should().ContainKey("revision").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)resourceMeta["revision"]); + }); - await _testContext.RunOnDatabaseAsync(async dbContext => + operation.Data.Should().NotBeNull(); + + operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { - SupportTicket supportTicketInDatabase = await dbContext.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) - .FirstAsync(supportTicket => supportTicket.Id == existingTicket.Id); + value.Should().NotBeNull(); + value.Meta.Should().NotBeNull(); - supportTicketInDatabase.ProductFamily.Should().BeNull(); + value.Meta.Should().HaveCount(relationshipMeta.Count); + value.Meta.Should().ContainKey("source").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + value.Meta.Should().ContainKey("confidence").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); }); } @@ -495,13 +721,17 @@ 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(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async db => { - db.ProductFamilies.Add(family); - db.SupportTickets.Add(ticket); + db.ProductFamilies.Add(existingFamily); + db.SupportTickets.Add(existingTicket); await db.SaveChangesAsync(); }); @@ -515,55 +745,92 @@ await _testContext.RunOnDatabaseAsync(async db => @ref = new { type = "supportTickets", - id = ticket.StringId, + id = existingTicket.StringId, relationship = "productFamily" }, data = new { type = "productFamilies", - id = family.StringId, - meta = GetExampleMetaData() + id = existingFamily.StringId, + meta = relationshipMeta }, - meta = GetExampleMetaData() + meta = resourceMeta } }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + const string route = "/operations"; + + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); - AtomicOperationObject? op = store.Document.Operations![0]; + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? 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 => + // operation meta explicit validation (resourceMeta) + op.Meta.Should().HaveCount(resourceMeta.Count); + op.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => { - SupportTicket dbTicket = await db.SupportTickets.Include(supportTicket => supportTicket.ProductFamily) - .FirstAsync(supportTicket => supportTicket.Id == ticket.Id); + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)resourceMeta["editedBy"]); + }); + op.Meta.Should().ContainKey("revision").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)resourceMeta["revision"]); + }); + + op.Data.SingleValue.Should().NotBeNull(); + op.Data.SingleValue.Meta.Should().NotBeNull(); - dbTicket.ProductFamily!.Id.Should().Be(family.Id); + // data meta explicit validation (relationshipMeta) + op.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); + op.Data.SingleValue.Meta.Should().ContainKey("source").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + op.Data.SingleValue.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); }); } [Fact] public async Task Accepts_meta_in_update_to_many_relationship_operation() { + // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - ProductFamily family = _fakers.ProductFamily.GenerateOne(); - SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); - await _testContext.RunOnDatabaseAsync(async db => + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => { - db.ProductFamilies.Add(family); - db.SupportTickets.AddRange(ticket1, ticket2); - await db.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -576,7 +843,7 @@ await _testContext.RunOnDatabaseAsync(async db => @ref = new { type = "productFamilies", - id = family.StringId, + id = existingFamily.StringId, relationship = "tickets" }, data = new[] @@ -584,60 +851,106 @@ await _testContext.RunOnDatabaseAsync(async db => new { type = "supportTickets", - id = ticket1.StringId, - meta = GetExampleMetaData() + id = existingTicket1.StringId, + meta = identifierMeta1 }, new { type = "supportTickets", - id = ticket2.StringId, - meta = GetExampleMetaData() + id = existingTicket2.StringId, + meta = identifierMeta2 } }, - meta = GetExampleMetaData() + meta = relationshipMeta } }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); - AtomicOperationObject? op = store.Document.Operations![0]; + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - ValidateMetaData(op.Meta); - foreach (ResourceObject data in op.Data.ManyValue!) + // op meta explicit validation (relationshipMeta) + op.Meta.Should().HaveCount(relationshipMeta.Count); + op.Meta.Should().ContainKey("source").WhoseValue.With(val => { - ValidateMetaData(data.Meta); - } + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); - await _testContext.RunOnDatabaseAsync(async db => + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(2); + + op.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); + op.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta1["index"]); + }); + op.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => { - ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets) - .FirstAsync(productFamily => productFamily.Id == family.Id); + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); + }); - dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); + op.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); + op.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta2["index"]); + }); + op.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); }); } [Fact] public async Task Accepts_meta_in_add_to_relationship_operation() { + // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - ProductFamily family = _fakers.ProductFamily.GenerateOne(); - SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); - await _testContext.RunOnDatabaseAsync(async db => + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => { - db.ProductFamilies.Add(family); - db.SupportTickets.AddRange(ticket1, ticket2); - await db.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -650,7 +963,7 @@ await _testContext.RunOnDatabaseAsync(async db => @ref = new { type = "productFamilies", - id = family.StringId, + id = existingFamily.StringId, relationship = "tickets" }, data = new[] @@ -658,66 +971,110 @@ await _testContext.RunOnDatabaseAsync(async db => new { type = "supportTickets", - id = ticket1.StringId, - meta = GetExampleMetaData() + id = existingTicket1.StringId, + meta = identifierMeta1 }, new { type = "supportTickets", - id = ticket2.StringId, - meta = GetExampleMetaData() + id = existingTicket2.StringId, + meta = identifierMeta2 } }, - meta = GetExampleMetaData() + meta = relationshipMeta } }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + store.Document.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); - AtomicOperationObject? op = store.Document.Operations![0]; + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - ValidateMetaData(op.Meta); - foreach (ResourceObject data in op.Data.ManyValue!) + // op meta explicit validation (relationshipMeta) + op.Meta.Should().HaveCount(relationshipMeta.Count); + op.Meta.Should().ContainKey("source").WhoseValue.With(val => { - ValidateMetaData(data.Meta); - } + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); - await _testContext.RunOnDatabaseAsync(async db => + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(2); + + op.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); + op.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => { - ProductFamily dbFamily = await db.ProductFamilies.Include(productFamily => productFamily.Tickets) - .FirstAsync(productFamily => productFamily.Id == family.Id); + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta1["index"]); + }); + op.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); + }); - dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket1.Id); - dbFamily.Tickets.Should().ContainSingle(supportTicket => supportTicket.Id == ticket2.Id); + op.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); + op.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta2["index"]); + }); + op.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); }); } [Fact] public async Task Accepts_meta_in_delete_from_relationship_operation() { + // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - ProductFamily family = _fakers.ProductFamily.GenerateOne(); - SupportTicket ticket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket ticket2 = _fakers.SupportTicket.GenerateOne(); + var documentMeta = _fakers.DocumentMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); - family.Tickets = new List + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + existingFamily.Tickets = new List { - ticket1, - ticket2 + existingTicket1, + existingTicket2 }; - await _testContext.RunOnDatabaseAsync(async db => + await _testContext.RunOnDatabaseAsync(async dbContext => { - db.ProductFamilies.Add(family); - db.SupportTickets.AddRange(ticket1, ticket2); - await db.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -730,7 +1087,7 @@ await _testContext.RunOnDatabaseAsync(async db => @ref = new { type = "productFamilies", - id = family.StringId, + id = existingFamily.StringId, relationship = "tickets" }, data = new[] @@ -738,133 +1095,55 @@ await _testContext.RunOnDatabaseAsync(async db => new { type = "supportTickets", - id = ticket1.StringId, - meta = GetExampleMetaData() + id = existingTicket1.StringId, } }, - meta = GetExampleMetaData() + meta = relationshipMeta } }, - meta = GetExampleMetaData() + meta = documentMeta }; - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync("/operations", requestBody); - - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - store.Document.Should().NotBeNull(); - ValidateMetaData(store.Document.Meta); - AtomicOperationObject? op = store.Document.Operations![0]; - op.Should().NotBeNull(); - ValidateMetaData(op.Meta); - - foreach (ResourceObject data in op.Data.ManyValue!) - { - ValidateMetaData(data.Meta); - } - - await _testContext.RunOnDatabaseAsync(async db => - { - 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); - }); - } + const string route = "/operations"; - 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 - } - }; - } + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - private static void ValidateMetaData(IDictionary? meta) - { - meta.Should().NotBeNull(); - meta.Should().HaveCount(6); + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - meta.Should().ContainKey("category").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be("bug"); - }); + store.Document.Should().NotBeNull(); - meta.Should().ContainKey("priority").WhoseValue.With(value => + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be(1); + element.GetString().Should().Be((string)documentMeta["requestId"]); }); - meta.Should().ContainKey("components").WhoseValue.With(value => - { - string innerJson = value.Should().BeOfType().Subject.ToString(); + store.Document.Operations.Should().NotBeNull(); - innerJson.Should().BeJson(""" - [ - "login", - "single-sign-on" - ] - """); - }); + AtomicOperationObject? op = store.Document.Operations[0]; + op.Should().NotBeNull(); - meta.Should().ContainKey("relatedTo").WhoseValue.With(value => + // op meta explicit validation (relationshipMeta) + op.Meta.Should().HaveCount(relationshipMeta.Count); + op.Meta.Should().ContainKey("source").WhoseValue.With(val => { - 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" - } - ] - """); + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); }); - - meta.Should().ContainKey("contextInfo").WhoseValue.With(value => + op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => { - string innerJson = value.Should().BeOfType().Subject.ToString(); - - innerJson.Should().BeJson(""" - { - "source": "form-submission", - "retries": 1, - "authenticated": false - } - """); + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); }); + + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(1); + op.Data.ManyValue[0].Type.Should().Be("supportTickets"); + op.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); } private sealed class CapturingDocumentAdapter : IDocumentAdapter From 2dd1daf6eb50b43752745baa20b40ef72cf95bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 27 Dec 2025 16:36:12 +0000 Subject: [PATCH 24/51] Add post resource request with to many relationships --- .../IntegrationTests/Meta/RequestMetaTests.cs | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 704b3c59a6..bc28d6f1e7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -264,7 +264,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_post_resource_request_with_relationship() + public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -333,6 +333,96 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }); } + [Fact] + public async Task Accepts_meta_in_post_resource_request_with_to_many_relationship() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.Generate(); + var resourceMeta = _fakers.ResourceMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + + string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "productFamilies", + attributes = new + { + name = newFamilyName + }, + relationships = new + { + tickets = new + { + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + description = existingTicket1.Description, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + description = existingTicket2.Description, + meta = identifierMeta2 + } + }, + meta = resourceMeta + } + } + }, + meta = documentMeta + }; + + const string route = "/productFamilies"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); + + store.Document.Should().NotBeNull(); + + // document meta explicit validation + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Data.ManyValue.Should().HaveCount(2); + + value.Data.ManyValue[0].Type.Should().Be("supportTickets"); + value.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); + + value.Data.ManyValue[1].Type.Should().Be("supportTickets"); + value.Data.ManyValue[1].Id.Should().Be(existingTicket2.StringId); + }); + } + [Fact] public async Task Accepts_meta_in_delete_relationship_request() { From b4823d7e85fdf6fe9e4f6719af5783f3532e23fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 28 Dec 2025 18:46:18 +0000 Subject: [PATCH 25/51] Add relationship endpoints tests --- .../IntegrationTests/Meta/RequestMetaTests.cs | 259 ++++++++++++++++-- 1 file changed, 236 insertions(+), 23 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index bc28d6f1e7..2a4bb52d28 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -196,7 +196,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -204,7 +203,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => element.GetString().Should().Be((string)documentMeta["requestId"]); }); - // resource meta explicit validation store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Meta.Should().HaveCount(resourceMeta.Count); store.Document.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(value => @@ -218,7 +216,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => element.GetInt32().Should().Be((int)resourceMeta["revision"]); }); - // relationship meta validation store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { @@ -314,7 +311,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -373,14 +369,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket1.StringId, - description = existingTicket1.Description, meta = identifierMeta1 }, new { type = "supportTickets", id = existingTicket2.StringId, - description = existingTicket2.Description, meta = identifierMeta2 } }, @@ -401,7 +395,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -409,6 +402,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => element.GetString().Should().Be((string)documentMeta["requestId"]); }); + //TODO: resource meta and relationship identifier meta validation store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { @@ -423,6 +417,241 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }); } + [Fact] + public async Task Accepts_meta_in_update_to_one_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.Generate(); + var relationshipMeta = _fakers.RelationshipMeta.Generate(); + var identifierMeta = _fakers.RelationshipIdentifierMeta.Generate(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "productFamilies", + id = existingFamily.StringId, + meta = relationshipMeta + }, + meta = documentMeta + }; + + string route = $"/supportTickets/{existingTicket.StringId}/relationships/productFamily"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Data.SingleValue.Should().NotBeNull(); + + store.Document.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); + store.Document.Data.SingleValue.Meta.Should().ContainKey("source").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetString().Should().Be((string)relationshipMeta["source"]); + }); + store.Document.Data.SingleValue.Meta.Should().ContainKey("confidence").WhoseValue.With(val => + { + JsonElement element = val.Should().BeOfType().Subject; + element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); + }); + } + + [Fact] + public async Task Accepts_meta_in_update_to_many_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.Add(existingTicket1); + dbContext.SupportTickets.Add(existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1, + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2, + }, + }, + meta = documentMeta + }; + + string route = $"/productFamilies/{existingFamily.StringId}/relationships/tickets"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Data.ManyValue.Should().HaveCount(2); + + store.Document.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); + store.Document.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta1["index"]); + }); + store.Document.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); + }); + + store.Document.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); + store.Document.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta2["index"]); + }); + store.Document.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); + }); + } + + [Fact] + public async Task Accepts_meta_in_post_relationship_request() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.Generate(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.Add(existingTicket1); + dbContext.SupportTickets.Add(existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1, + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2, + }, + }, + meta = documentMeta + }; + + string route = $"/productFamilies/{existingFamily.StringId}/relationships/tickets"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().HaveCount(documentMeta.Count); + store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => + { + JsonElement element = value.Should().BeOfType().Subject; + element.GetString().Should().Be((string)documentMeta["requestId"]); + }); + + store.Document.Data.ManyValue.Should().HaveCount(2); + + store.Document.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); + store.Document.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta1["index"]); + }); + store.Document.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); + }); + + store.Document.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); + store.Document.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetInt32().Should().Be((int)identifierMeta2["index"]); + }); + store.Document.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => + { + JsonElement element = v.Should().BeOfType().Subject; + element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); + }); + } + [Fact] public async Task Accepts_meta_in_delete_relationship_request() { @@ -459,7 +688,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Should().BeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -515,7 +743,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -582,7 +809,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -653,7 +879,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -670,7 +895,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); - // resource/meta validation on operation data (resourceMeta -> relationshipMeta mapping) operation.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); operation.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => { @@ -757,7 +981,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -771,7 +994,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => AtomicOperationObject? operation = store.Document.Operations[0]; operation.Should().NotBeNull(); - // operation meta explicit validation (resourceMeta) operation.Meta.Should().HaveCount(resourceMeta.Count); operation.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => { @@ -858,7 +1080,6 @@ await _testContext.RunOnDatabaseAsync(async db => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -871,7 +1092,6 @@ await _testContext.RunOnDatabaseAsync(async db => AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - // operation meta explicit validation (resourceMeta) op.Meta.Should().HaveCount(resourceMeta.Count); op.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => { @@ -887,7 +1107,6 @@ await _testContext.RunOnDatabaseAsync(async db => op.Data.SingleValue.Should().NotBeNull(); op.Data.SingleValue.Meta.Should().NotBeNull(); - // data meta explicit validation (relationshipMeta) op.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); op.Data.SingleValue.Meta.Should().ContainKey("source").WhoseValue.With(val => { @@ -967,7 +1186,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -980,7 +1198,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - // op meta explicit validation (relationshipMeta) op.Meta.Should().HaveCount(relationshipMeta.Count); op.Meta.Should().ContainKey("source").WhoseValue.With(val => { @@ -1087,7 +1304,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -1100,7 +1316,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - // op meta explicit validation (relationshipMeta) op.Meta.Should().HaveCount(relationshipMeta.Count); op.Meta.Should().ContainKey("source").WhoseValue.With(val => { @@ -1204,7 +1419,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - // document meta explicit validation store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => { @@ -1217,7 +1431,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => AtomicOperationObject? op = store.Document.Operations[0]; op.Should().NotBeNull(); - // op meta explicit validation (relationshipMeta) op.Meta.Should().HaveCount(relationshipMeta.Count); op.Meta.Should().ContainKey("source").WhoseValue.With(val => { From cacd9c67bdfdab6e1e4850799ee8739b5c527106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Mon, 29 Dec 2025 22:30:52 +0000 Subject: [PATCH 26/51] Isolate atomic operations request tests --- .../IntegrationTests/Meta/MetaFakers.cs | 27 +- .../Meta/OperationsRequestMetaTests.cs | 649 +++++++++++ .../IntegrationTests/Meta/RequestMetaTests.cs | 1022 +---------------- .../FluentMetaExtensions.cs | 24 + 4 files changed, 746 insertions(+), 976 deletions(-) create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs index 4da41003d2..e1a73503a0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs @@ -16,32 +16,33 @@ internal sealed class MetaFakers .MakeDeterministic() .RuleFor(supportTicket => supportTicket.Description, faker => faker.Lorem.Paragraph())); - private readonly Lazy>> _lazyDocumentMetaFaker = new(() => new Faker>() + private readonly Lazy>> _lazyDocumentMetaFaker = new(() => new Faker>() .MakeDeterministic() - .CustomInstantiator(faker => new Dictionary + .CustomInstantiator(faker => new Dictionary { - ["requestId"] = faker.Random.Guid().ToString() + ["requestId"] = faker.Random.Guid().ToString(), + ["isUrgent"] = faker.Random.Bool() })); - private readonly Lazy>> _lazyResourceMetaFaker = new(() => new Faker>() + private readonly Lazy>> _lazyResourceMetaFaker = new(() => new Faker>() .MakeDeterministic() - .CustomInstantiator(faker => new Dictionary + .CustomInstantiator(faker => new Dictionary { ["editedBy"] = faker.Internet.UserName(), ["revision"] = faker.Random.Int(1, 10) })); - private readonly Lazy>> _lazyRelationshipMetaFaker = new(() => new Faker>() + private readonly Lazy>> _lazyRelationshipMetaFaker = new(() => new Faker>() .MakeDeterministic() - .CustomInstantiator(faker => new Dictionary + .CustomInstantiator(faker => new Dictionary { ["source"] = faker.PickRandom("ui", "api", "import"), ["confidence"] = faker.Random.Double(0.1, 1.0) })); - private readonly Lazy>> _lazyRelationshipIdentifierMetaFaker = new(() => new Faker>() + private readonly Lazy>> _lazyRelationshipIdentifierMetaFaker = new(() => new Faker>() .MakeDeterministic() - .CustomInstantiator(faker => new Dictionary + .CustomInstantiator(faker => new Dictionary { ["index"] = faker.IndexFaker, ["optionalNote"] = faker.Lorem.Word() @@ -49,8 +50,8 @@ internal sealed class MetaFakers public Faker ProductFamily => _lazyProductFamilyFaker.Value; public Faker SupportTicket => _lazySupportTicketFaker.Value; - public Faker> DocumentMeta => _lazyDocumentMetaFaker.Value; - public Faker> ResourceMeta => _lazyResourceMetaFaker.Value; - public Faker> RelationshipMeta => _lazyRelationshipMetaFaker.Value; - public Faker> RelationshipIdentifierMeta => _lazyRelationshipIdentifierMetaFaker.Value; + public Faker> DocumentMeta => _lazyDocumentMetaFaker.Value; + public Faker> ResourceMeta => _lazyResourceMetaFaker.Value; + public Faker> RelationshipMeta => _lazyRelationshipMetaFaker.Value; + public Faker> RelationshipIdentifierMeta => _lazyRelationshipIdentifierMetaFaker.Value; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs new file mode 100644 index 0000000000..ec6ffab261 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -0,0 +1,649 @@ +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 OperationsRequestMetaTests : IClassFixture, MetaDbContext>> +{ + private readonly IntegrationTestContext, MetaDbContext> _testContext; + private readonly MetaFakers _fakers = new(); + + public OperationsRequestMetaTests(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_meta_in_atomic_update_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + + 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 = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().HaveCount(1); + + AtomicOperationObject? operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + operation.Data.Should().NotBeNull(); + + ResourceObject? resource = operation.Data.SingleValue; + resource.Should().NotBeNull(); + resource.Type.Should().Be("supportTickets"); + resource.Id.Should().Be(existingTicket.StringId); + resource.Attributes.Should().NotBeNull(); + resource.Attributes.Should().ContainKey("description"); + } + + [Fact] + public async Task Accepts_meta_in_atomic_remove_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + + 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 = documentMeta + }; + + const 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().BeEquivalentToJson(documentMeta); + } + + [Fact] + public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + + string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "supportTickets", + attributes = new + { + description = newTicketDescription + }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingFamily.StringId + }, + meta = relationshipMeta + } + }, + meta = resourceMeta + } + } + }, + meta = documentMeta + }; + + 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().HaveCount(documentMeta.Count); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + 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().BeEquivalentToJson(resourceMeta); + + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + }); + } + + [Fact] + public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + existingTicket.ProductFamily = existingFamily; + 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 = relationshipMeta + } + } + }, + meta = resourceMeta + } + }, + meta = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().NotBeNull(); + store.Document.Operations.Should().HaveCount(1); + + AtomicOperationObject? operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(resourceMeta); + + operation.Data.Should().NotBeNull(); + + operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + }); + } + + [Fact] + public async Task Accepts_meta_in_update_to_one_relationship_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContex => + { + dbContex.ProductFamilies.Add(existingFamily); + dbContex.SupportTickets.Add(existingTicket); + await dbContex.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "supportTickets", + id = existingTicket.StringId, + relationship = "productFamily" + }, + data = new + { + type = "productFamilies", + id = existingFamily.StringId, + meta = relationshipMeta + }, + meta = resourceMeta + } + }, + meta = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; + op.Should().NotBeNull(); + + op.Meta.Should().BeEquivalentToJson(resourceMeta); + + op.Data.SingleValue.Should().NotBeNull(); + op.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); + } + + [Fact] + public async Task Accepts_meta_in_update_to_many_relationship_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "productFamilies", + id = existingFamily.StringId, + relationship = "tickets" + }, + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 + } + }, + meta = relationshipMeta + } + }, + meta = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; + op.Should().NotBeNull(); + + op.Meta.Should().BeEquivalentToJson(relationshipMeta); + + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(2); + + op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + + op.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + } + + [Fact] + public async Task Accepts_meta_in_add_to_relationship_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + @ref = new + { + type = "productFamilies", + id = existingFamily.StringId, + relationship = "tickets" + }, + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 + } + }, + meta = relationshipMeta + } + }, + meta = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; + op.Should().NotBeNull(); + + op.Meta.Should().BeEquivalentToJson(relationshipMeta); + + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(2); + + op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + + op.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + } + + [Fact] + public async Task Accepts_meta_in_remove_from_relationship_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + + existingFamily.Tickets = new List + { + existingTicket1, + existingTicket2 + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "remove", + @ref = new + { + type = "productFamilies", + id = existingFamily.StringId, + relationship = "tickets" + }, + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + } + }, + meta = relationshipMeta + } + }, + meta = documentMeta + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().NotBeNull(); + + AtomicOperationObject? op = store.Document.Operations[0]; + op.Should().NotBeNull(); + + op.Meta.Should().BeEquivalentToJson(relationshipMeta); + + op.Data.ManyValue.Should().NotBeNull(); + op.Data.ManyValue.Should().HaveCount(1); + + op.Data.ManyValue[0].Type.Should().Be("supportTickets"); + op.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); + } + + 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; } + } +} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 2a4bb52d28..406f76418d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -39,23 +39,23 @@ public RequestMetaTests(IntegrationTestContext, M } [Fact] - public async Task Accepts_meta_in_patch_resource_request_with_to_one_relationship() + public async Task Accepts_meta_in_update_resource_request_with_to_one_relationship() { - // Assert + // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - await _testContext.RunOnDatabaseAsync(async db => + await _testContext.RunOnDatabaseAsync(async dbContex => { - db.ProductFamilies.Add(existingFamily); - db.SupportTickets.Add(existingTicket); - await db.SaveChangesAsync(); + dbContex.ProductFamilies.Add(existingFamily); + dbContex.SupportTickets.Add(existingTicket); + await dbContex.SaveChangesAsync(); }); var requestBody = new @@ -92,55 +92,31 @@ await _testContext.RunOnDatabaseAsync(async db => store.Document.Should().NotBeNull(); store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.Should().NotBeNull(); store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().HaveCount(resourceMeta.Count); - store.Document.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)resourceMeta["editedBy"]); - }); - store.Document.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)resourceMeta["revision"]); - }); + + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); - - value.Meta.Should().HaveCount(relationshipMeta.Count); - value.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - value.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); }); } [Fact] - public async Task Accepts_meta_in_patch_resource_request_with_to_many_relationship() + public async Task Accepts_meta_in_update_resource_request_with_to_many_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -196,67 +172,21 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().HaveCount(resourceMeta.Count); - store.Document.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)resourceMeta["editedBy"]); - }); - store.Document.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)resourceMeta["revision"]); - }); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().HaveCount(relationshipMeta.Count); - value.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - value.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); - value.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta1["index"]); - }); - value.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); - }); - - value.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); - value.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta2["index"]); - }); - value.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); - }); + value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } @@ -266,7 +196,7 @@ public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -311,13 +241,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => @@ -335,10 +259,10 @@ public async Task Accepts_meta_in_post_resource_request_with_to_many_relationshi // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -378,9 +302,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = identifierMeta2 } }, - meta = resourceMeta + } - } + }, + meta = resourceMeta }, meta = documentMeta }; @@ -395,25 +320,21 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - //TODO: resource meta and relationship identifier meta validation store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); + + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); + value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Type.Should().Be("supportTickets"); - value.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); + value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - value.Data.ManyValue[1].Type.Should().Be("supportTickets"); - value.Data.ManyValue[1].Id.Should().Be(existingTicket2.StringId); + value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } @@ -423,9 +344,8 @@ public async Task Accepts_meta_in_update_to_one_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - var identifierMeta = _fakers.RelationshipIdentifierMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -458,26 +378,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); - store.Document.Data.SingleValue.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - store.Document.Data.SingleValue.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); } [Fact] @@ -486,9 +391,9 @@ public async Task Accepts_meta_in_update_to_many_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -532,38 +437,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); - store.Document.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta1["index"]); - }); - store.Document.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); - }); + store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - store.Document.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); - store.Document.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta2["index"]); - }); - store.Document.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); - }); + store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } [Fact] @@ -572,9 +452,9 @@ public async Task Accepts_meta_in_post_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -618,47 +498,21 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); - store.Document.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta1["index"]); - }); - store.Document.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); - }); + store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - store.Document.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); - store.Document.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta2["index"]); - }); - store.Document.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); - }); + store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } - [Fact] - public async Task Accepts_meta_in_delete_relationship_request() + public async Task Accepts_meta_in_remove_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.Generate(); + var documentMeta = _fakers.DocumentMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -688,765 +542,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Should().BeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - } - - [Fact] - public async Task Accepts_meta_in_atomic_update_resource_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - - 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 = documentMeta - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().HaveCount(1); - - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); - operation.Data.Should().NotBeNull(); - - ResourceObject? resource = operation.Data.SingleValue; - resource.Should().NotBeNull(); - resource.Type.Should().Be("supportTickets"); - resource.Id.Should().Be(existingTicket.StringId); - resource.Attributes.Should().NotBeNull(); - resource.Attributes.Should().ContainKey("description"); - } - - [Fact] - public async Task Accepts_meta_in_atomic_remove_resource_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - - 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 = documentMeta - }; - - const 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().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - } - - [Fact] - public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - - string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.ProductFamilies.Add(existingFamily); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "add", - data = new - { - type = "supportTickets", - attributes = new - { - description = newTicketDescription - }, - relationships = new - { - productFamily = new - { - data = new - { - type = "productFamilies", - id = existingFamily.StringId - }, - meta = relationshipMeta - } - }, - meta = resourceMeta - } - } - }, - meta = documentMeta - }; - - 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().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - 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().HaveCount(relationshipMeta.Count); - operation.Data.SingleValue.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)resourceMeta["editedBy"]); - }); - operation.Data.SingleValue.Meta.Should().ContainKey("revision").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().Be((int)resourceMeta["revision"]); - }); - - operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => - { - value.Should().NotBeNull(); - value.Meta.Should().NotBeNull(); - value.Meta.Should().HaveCount(relationshipMeta.Count); - value.Meta.Should().ContainKey("source").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - value.Meta.Should().ContainKey("confidence").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - }); - } - - [Fact] - public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - existingTicket.ProductFamily = existingFamily; - 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 = relationshipMeta - } - } - }, - meta = resourceMeta - } - }, - meta = documentMeta - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().HaveCount(1); - - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); - - operation.Meta.Should().HaveCount(resourceMeta.Count); - operation.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)resourceMeta["editedBy"]); - }); - operation.Meta.Should().ContainKey("revision").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)resourceMeta["revision"]); - }); - - operation.Data.Should().NotBeNull(); - - operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => - { - value.Should().NotBeNull(); - value.Meta.Should().NotBeNull(); - - value.Meta.Should().HaveCount(relationshipMeta.Count); - value.Meta.Should().ContainKey("source").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - value.Meta.Should().ContainKey("confidence").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - }); - } - - [Fact] - public async Task Accepts_meta_in_update_to_one_relationship_operation() - { - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var resourceMeta = _fakers.ResourceMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async db => - { - db.ProductFamilies.Add(existingFamily); - db.SupportTickets.Add(existingTicket); - await db.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "update", - @ref = new - { - type = "supportTickets", - id = existingTicket.StringId, - relationship = "productFamily" - }, - data = new - { - type = "productFamilies", - id = existingFamily.StringId, - meta = relationshipMeta - }, - meta = resourceMeta - } - }, - meta = documentMeta - }; - - const string route = "/operations"; - - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().NotBeNull(); - - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); - - op.Meta.Should().HaveCount(resourceMeta.Count); - op.Meta.Should().ContainKey("editedBy").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)resourceMeta["editedBy"]); - }); - op.Meta.Should().ContainKey("revision").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)resourceMeta["revision"]); - }); - - op.Data.SingleValue.Should().NotBeNull(); - op.Data.SingleValue.Meta.Should().NotBeNull(); - - op.Data.SingleValue.Meta.Should().HaveCount(relationshipMeta.Count); - op.Data.SingleValue.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - op.Data.SingleValue.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - } - - [Fact] - public async Task Accepts_meta_in_update_to_many_relationship_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); - - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "update", - @ref = new - { - type = "productFamilies", - id = existingFamily.StringId, - relationship = "tickets" - }, - data = new[] - { - new - { - type = "supportTickets", - id = existingTicket1.StringId, - meta = identifierMeta1 - }, - new - { - type = "supportTickets", - id = existingTicket2.StringId, - meta = identifierMeta2 - } - }, - meta = relationshipMeta - } - }, - meta = documentMeta - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().NotBeNull(); - - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); - - op.Meta.Should().HaveCount(relationshipMeta.Count); - op.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(2); - - op.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); - op.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta1["index"]); - }); - op.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); - }); - - op.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); - op.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta2["index"]); - }); - op.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); - }); - } - - [Fact] - public async Task Accepts_meta_in_add_to_relationship_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.Generate(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.Generate(); - - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "add", - @ref = new - { - type = "productFamilies", - id = existingFamily.StringId, - relationship = "tickets" - }, - data = new[] - { - new - { - type = "supportTickets", - id = existingTicket1.StringId, - meta = identifierMeta1 - }, - new - { - type = "supportTickets", - id = existingTicket2.StringId, - meta = identifierMeta2 - } - }, - meta = relationshipMeta - } - }, - meta = documentMeta - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().NotBeNull(); - - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); - - op.Meta.Should().HaveCount(relationshipMeta.Count); - op.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(2); - - op.Data.ManyValue[0].Meta.Should().HaveCount(identifierMeta1.Count); - op.Data.ManyValue[0].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta1["index"]); - }); - op.Data.ManyValue[0].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta1["optionalNote"]); - }); - - op.Data.ManyValue[1].Meta.Should().HaveCount(identifierMeta2.Count); - op.Data.ManyValue[1].Meta.Should().ContainKey("index").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetInt32().Should().Be((int)identifierMeta2["index"]); - }); - op.Data.ManyValue[1].Meta.Should().ContainKey("optionalNote").WhoseValue.With(v => - { - JsonElement element = v.Should().BeOfType().Subject; - element.GetString().Should().Be((string)identifierMeta2["optionalNote"]); - }); - } - - [Fact] - public async Task Accepts_meta_in_delete_from_relationship_operation() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - var documentMeta = _fakers.DocumentMeta.Generate(); - var relationshipMeta = _fakers.RelationshipMeta.Generate(); - - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - - existingFamily.Tickets = new List - { - existingTicket1, - existingTicket2 - }; - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "remove", - @ref = new - { - type = "productFamilies", - id = existingFamily.StringId, - relationship = "tickets" - }, - data = new[] - { - new - { - type = "supportTickets", - id = existingTicket1.StringId, - } - }, - meta = relationshipMeta - } - }, - meta = documentMeta - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Meta.Should().HaveCount(documentMeta.Count); - store.Document.Meta.Should().ContainKey("requestId").WhoseValue.With(value => - { - JsonElement element = value.Should().BeOfType().Subject; - element.GetString().Should().Be((string)documentMeta["requestId"]); - }); - - store.Document.Operations.Should().NotBeNull(); - - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); - - op.Meta.Should().HaveCount(relationshipMeta.Count); - op.Meta.Should().ContainKey("source").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetString().Should().Be((string)relationshipMeta["source"]); - }); - op.Meta.Should().ContainKey("confidence").WhoseValue.With(val => - { - JsonElement element = val.Should().BeOfType().Subject; - element.GetDouble().Should().BeApproximately((double)relationshipMeta["confidence"], 1e-6); - }); - - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(1); - op.Data.ManyValue[0].Type.Should().Be("supportTickets"); - op.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); } private sealed class CapturingDocumentAdapter : IDocumentAdapter diff --git a/test/TestBuildingBlocks/FluentMetaExtensions.cs b/test/TestBuildingBlocks/FluentMetaExtensions.cs index 72a5a8bb2c..5cb52f6ccf 100644 --- a/test/TestBuildingBlocks/FluentMetaExtensions.cs +++ b/test/TestBuildingBlocks/FluentMetaExtensions.cs @@ -1,4 +1,6 @@ +using System.Text.Encodings.Web; using System.Text.Json; +using System.Text.Json.Serialization; using FluentAssertions; using FluentAssertions.Collections; @@ -77,4 +79,26 @@ private static JsonElement GetMetaJsonElement(GenericDictionaryAssertions().Subject; } + + private static readonly JsonSerializerOptions MetaSerializerOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + ReferenceHandler = ReferenceHandler.IgnoreCycles + }; + + /// + /// Asserts that the content of a "meta" dictionary matches the expected structure and values, after conversion to JSON. + /// + [CustomAssertion] + public static void BeEquivalentToJson(this GenericDictionaryAssertions, string, object?> source, + Dictionary expected) + { + source.NotBeNull(); + + string sourceJson = JsonSerializer.Serialize(source.Subject, MetaSerializerOptions); + string expectedJson = JsonSerializer.Serialize(expected, MetaSerializerOptions); + + sourceJson.Should().Be(expectedJson); + } } From 607ca909480c9680f023030ab3db98c5cba64bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Mon, 29 Dec 2025 22:48:23 +0000 Subject: [PATCH 27/51] Add missing meta data coverage --- .../Meta/OperationsRequestMetaTests.cs | 26 +++++++++----- .../IntegrationTests/Meta/RequestMetaTests.cs | 35 ++++++++++++++----- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index ec6ffab261..1c389a84d9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Text.Json; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCore.Serialization.Request.Adapters; @@ -45,6 +44,7 @@ public async Task Accepts_meta_in_atomic_update_resource_operation() var store = _testContext.Factory.Services.GetRequiredService(); var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -68,7 +68,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => attributes = new { description = existingTicket.Description - } + }, + meta = resourceMeta } } }, @@ -95,10 +96,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ResourceObject? resource = operation.Data.SingleValue; resource.Should().NotBeNull(); - resource.Type.Should().Be("supportTickets"); - resource.Id.Should().Be(existingTicket.StringId); - resource.Attributes.Should().NotBeNull(); - resource.Attributes.Should().ContainKey("description"); + resource.Meta.Should().BeEquivalentToJson(resourceMeta); } [Fact] @@ -158,6 +156,7 @@ public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation( var documentMeta = _fakers.DocumentMeta.GenerateOne(); var resourceMeta = _fakers.ResourceMeta.GenerateOne(); var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -189,7 +188,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => data = new { type = "productFamilies", - id = existingFamily.StringId + id = existingFamily.StringId, + meta = identifierMeta }, meta = relationshipMeta } @@ -228,7 +228,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + + value.Data.Should().NotBeNull(); + + value.Data.SingleValue.Should().NotBeNull(); + + value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); } @@ -550,6 +557,7 @@ public async Task Accepts_meta_in_remove_from_relationship_operation() var documentMeta = _fakers.DocumentMeta.GenerateOne(); var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -587,6 +595,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket1.StringId, + meta = identifierMeta } }, meta = relationshipMeta @@ -617,8 +626,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => op.Data.ManyValue.Should().NotBeNull(); op.Data.ManyValue.Should().HaveCount(1); - op.Data.ManyValue[0].Type.Should().Be("supportTickets"); - op.Data.ManyValue[0].Id.Should().Be(existingTicket1.StringId); + op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); } private sealed class CapturingDocumentAdapter : IDocumentAdapter diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 406f76418d..cdd6cc640a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Text.Json; using FluentAssertions; using JsonApiDotNetCore.Serialization.Objects; using JsonApiDotNetCore.Serialization.Request.Adapters; @@ -47,6 +46,7 @@ public async Task Accepts_meta_in_update_resource_request_with_to_one_relationsh var documentMeta = _fakers.DocumentMeta.GenerateOne(); var resourceMeta = _fakers.ResourceMeta.GenerateOne(); var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -71,7 +71,8 @@ await _testContext.RunOnDatabaseAsync(async dbContex => data = new { type = "productFamilies", - id = existingFamily.StringId + id = existingFamily.StringId, + meta = identifierMeta }, meta = relationshipMeta } @@ -103,6 +104,8 @@ await _testContext.RunOnDatabaseAsync(async dbContex => { value.Should().NotBeNull(); value.Meta.Should().BeEquivalentToJson(relationshipMeta); + value.Data.SingleValue.Should().NotBeNull(); + value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); } @@ -197,6 +200,9 @@ public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship var store = _testContext.Factory.Services.GetRequiredService(); var documentMeta = _fakers.DocumentMeta.GenerateOne(); + var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -223,10 +229,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => data = new { type = "productFamilies", - id = existingFamily.StringId - } + id = existingFamily.StringId, + meta = identifierMeta + }, + meta = relationshipMeta } - } + }, + meta = resourceMeta }, meta = documentMeta }; @@ -243,13 +252,19 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); + + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); + store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); + + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + value.Data.SingleValue.Should().NotBeNull(); - value.Data.SingleValue.Type.Should().Be("productFamilies"); - value.Data.SingleValue.Id.Should().Be(existingFamily.StringId); + + value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); } @@ -261,6 +276,7 @@ public async Task Accepts_meta_in_post_resource_request_with_to_many_relationshi var documentMeta = _fakers.DocumentMeta.GenerateOne(); var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); @@ -302,7 +318,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = identifierMeta2 } }, - + meta = relationshipMeta } }, meta = resourceMeta @@ -326,10 +342,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + value.Data.ManyValue.Should().HaveCount(2); value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); From 925989fe0de27dfa55b55c9d1a9009a068fadcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Mon, 29 Dec 2025 22:53:51 +0000 Subject: [PATCH 28/51] Resolve code cleanup warnings --- .../Meta/OperationsRequestMetaTests.cs | 44 ++++++++-------- .../IntegrationTests/Meta/RequestMetaTests.cs | 50 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 1c389a84d9..5a4f2cdeb8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -43,8 +43,8 @@ public async Task Accepts_meta_in_atomic_update_resource_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -105,7 +105,7 @@ public async Task Accepts_meta_in_atomic_remove_resource_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -153,9 +153,9 @@ public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation( // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; @@ -245,9 +245,9 @@ public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operati // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -321,9 +321,9 @@ public async Task Accepts_meta_in_update_to_one_relationship_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -389,10 +389,10 @@ public async Task Accepts_meta_in_update_to_many_relationship_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -472,10 +472,10 @@ public async Task Accepts_meta_in_add_to_relationship_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -555,8 +555,8 @@ public async Task Accepts_meta_in_remove_from_relationship_operation() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index cdd6cc640a..076405f2df 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -43,9 +43,9 @@ public async Task Accepts_meta_in_update_resource_request_with_to_one_relationsh // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -115,11 +115,11 @@ public async Task Accepts_meta_in_update_resource_request_with_to_many_relations // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -199,9 +199,9 @@ public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; @@ -274,11 +274,11 @@ public async Task Accepts_meta_in_post_resource_request_with_to_many_relationshi // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var resourceMeta = _fakers.ResourceMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -363,8 +363,8 @@ public async Task Accepts_meta_in_update_to_one_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -410,9 +410,9 @@ public async Task Accepts_meta_in_update_to_many_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -471,9 +471,9 @@ public async Task Accepts_meta_in_post_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); - var identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - var identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -531,7 +531,7 @@ public async Task Accepts_meta_in_remove_relationship_request() // Arrange var store = _testContext.Factory.Services.GetRequiredService(); - var documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); From 5c540b59172840a57e705ed7139baa368cad9633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Mon, 29 Dec 2025 23:02:54 +0000 Subject: [PATCH 29/51] Resolve remaining code cleanup warnings --- test/TestBuildingBlocks/FluentMetaExtensions.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/TestBuildingBlocks/FluentMetaExtensions.cs b/test/TestBuildingBlocks/FluentMetaExtensions.cs index 5cb52f6ccf..fac9431613 100644 --- a/test/TestBuildingBlocks/FluentMetaExtensions.cs +++ b/test/TestBuildingBlocks/FluentMetaExtensions.cs @@ -8,6 +8,13 @@ namespace TestBuildingBlocks; public static class FluentMetaExtensions { + private static readonly JsonSerializerOptions MetaSerializerOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + ReferenceHandler = ReferenceHandler.IgnoreCycles + }; + /// /// Asserts that a "meta" dictionary contains a single element named "total" with the specified value. /// @@ -80,13 +87,6 @@ private static JsonElement GetMetaJsonElement(GenericDictionaryAssertions().Subject; } - private static readonly JsonSerializerOptions MetaSerializerOptions = new() - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - ReferenceHandler = ReferenceHandler.IgnoreCycles - }; - /// /// Asserts that the content of a "meta" dictionary matches the expected structure and values, after conversion to JSON. /// From 63754fd388af31931bf5036a7d3e3dfcda2b80f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:10:28 +0000 Subject: [PATCH 30/51] Fix build --- .../IntegrationTests/Meta/RequestMetaTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 076405f2df..1b7b092034 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -526,6 +526,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } + [Fact] public async Task Accepts_meta_in_remove_relationship_request() { // Arrange From b5e99a6e49c79dce4adb572dfe7ea46a01538d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:32:46 +0000 Subject: [PATCH 31/51] fix warnings --- .../IntegrationTests/Meta/MetaFakers.cs | 2 +- .../Meta/OperationsRequestMetaTests.cs | 5 +++-- .../IntegrationTests/Meta/RequestMetaTests.cs | 13 +++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs index e1a73503a0..dea2d0eecb 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs @@ -37,7 +37,7 @@ internal sealed class MetaFakers .CustomInstantiator(faker => new Dictionary { ["source"] = faker.PickRandom("ui", "api", "import"), - ["confidence"] = faker.Random.Double(0.1, 1.0) + ["confidence"] = faker.Random.Double(0.1) })); private readonly Lazy>> _lazyRelationshipIdentifierMetaFaker = new(() => new Faker>() diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 5a4f2cdeb8..b6319d75f1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -156,7 +156,7 @@ public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation( Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -308,6 +308,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); @@ -557,7 +558,7 @@ public async Task Accepts_meta_in_remove_from_relationship_operation() Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 1b7b092034..6f052c0126 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -46,7 +46,7 @@ public async Task Accepts_meta_in_update_resource_request_with_to_one_relationsh Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -202,7 +202,7 @@ public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - var identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -256,6 +256,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); @@ -434,13 +435,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket1.StringId, - meta = identifierMeta1, + meta = identifierMeta1 }, new { type = "supportTickets", id = existingTicket2.StringId, - meta = identifierMeta2, + meta = identifierMeta2 }, }, meta = documentMeta @@ -495,13 +496,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket1.StringId, - meta = identifierMeta1, + meta = identifierMeta1 }, new { type = "supportTickets", id = existingTicket2.StringId, - meta = identifierMeta2, + meta = identifierMeta2 }, }, meta = documentMeta From 5ee4d79bd1954559ef9893ac73a45635548288d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:49:32 +0000 Subject: [PATCH 32/51] Add test to check meta in operation level --- run-docker-postgres.ps1 | 1 - .../Meta/OperationsRequestMetaTests.cs | 11 ++++++++++- .../IntegrationTests/Meta/RequestMetaTests.cs | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/run-docker-postgres.ps1 b/run-docker-postgres.ps1 index 9474758fa0..8b854a6deb 100644 --- a/run-docker-postgres.ps1 +++ b/run-docker-postgres.ps1 @@ -1,4 +1,3 @@ -#Requires -Version 7.0 # This script starts a PostgreSQL database in a docker container, which is required for running tests locally. # When the -UI switch is passed, pgAdmin (a web-based PostgreSQL management tool) is started in a second container, which lets you query the database. diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index b6319d75f1..92deea01ca 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -106,6 +106,7 @@ public async Task Accepts_meta_in_atomic_remove_resource_operation() var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary operationMeta = _fakers.ResourceMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -126,7 +127,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket.StringId - } + }, + meta = operationMeta } }, meta = documentMeta @@ -145,6 +147,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().HaveCount(1); + + AtomicOperationObject? operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 6f052c0126..67c4955730 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -178,6 +178,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); @@ -503,7 +504,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", id = existingTicket2.StringId, meta = identifierMeta2 - }, + } }, meta = documentMeta }; From 5b44ea46c9d964273b49d9da8c4499795f06e38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:52:48 +0000 Subject: [PATCH 33/51] Revert "Add test to check meta in operation level" This reverts commit 5ee4d79bd1954559ef9893ac73a45635548288d8. --- run-docker-postgres.ps1 | 1 + .../Meta/OperationsRequestMetaTests.cs | 11 +---------- .../IntegrationTests/Meta/RequestMetaTests.cs | 3 +-- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/run-docker-postgres.ps1 b/run-docker-postgres.ps1 index 8b854a6deb..9474758fa0 100644 --- a/run-docker-postgres.ps1 +++ b/run-docker-postgres.ps1 @@ -1,3 +1,4 @@ +#Requires -Version 7.0 # This script starts a PostgreSQL database in a docker container, which is required for running tests locally. # When the -UI switch is passed, pgAdmin (a web-based PostgreSQL management tool) is started in a second container, which lets you query the database. diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 92deea01ca..b6319d75f1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -106,7 +106,6 @@ public async Task Accepts_meta_in_atomic_remove_resource_operation() var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary operationMeta = _fakers.ResourceMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -127,8 +126,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket.StringId - }, - meta = operationMeta + } } }, meta = documentMeta @@ -147,13 +145,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - - store.Document.Operations.Should().HaveCount(1); - - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); - - operation.Meta.Should().BeEquivalentToJson(operationMeta); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 67c4955730..6f052c0126 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -178,7 +178,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); @@ -504,7 +503,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", id = existingTicket2.StringId, meta = identifierMeta2 - } + }, }, meta = documentMeta }; From 0c6328f8f6188f51ad9d6b6300c4eb2a143b8e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:55:07 +0000 Subject: [PATCH 34/51] Add test to check meta in operation level --- .../Meta/OperationsRequestMetaTests.cs | 11 ++++++++++- .../IntegrationTests/Meta/RequestMetaTests.cs | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index b6319d75f1..92deea01ca 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -106,6 +106,7 @@ public async Task Accepts_meta_in_atomic_remove_resource_operation() var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary operationMeta = _fakers.ResourceMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -126,7 +127,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket.StringId - } + }, + meta = operationMeta } }, meta = documentMeta @@ -145,6 +147,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().HaveCount(1); + + AtomicOperationObject? operation = store.Document.Operations[0]; + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 6f052c0126..47ab9cdbd4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -178,6 +178,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); @@ -504,7 +505,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => id = existingTicket2.StringId, meta = identifierMeta2 }, - }, + } meta = documentMeta }; From 8c306ef0cc64ac187fc9b5ff3ea335421f90de8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 22:56:36 +0000 Subject: [PATCH 35/51] Fix compilation error --- .../IntegrationTests/Meta/RequestMetaTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 47ab9cdbd4..67c4955730 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -504,8 +504,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", id = existingTicket2.StringId, meta = identifierMeta2 - }, - } + } + }, meta = documentMeta }; From 35e6533c1e5e9ea5f3bdd9d7d0a3388da205ce8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 23:05:46 +0000 Subject: [PATCH 36/51] fix warnings --- .../IntegrationTests/Meta/RequestMetaTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 67c4955730..67b138d11b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -195,7 +195,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_post_resource_request_with_to_one_relationship() + public async Task Accepts_meta_in_add_resource_request_with_to_one_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -271,7 +271,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_post_resource_request_with_to_many_relationship() + public async Task Accepts_meta_in_add_resource_request_with_to_many_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -443,7 +443,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => type = "supportTickets", id = existingTicket2.StringId, meta = identifierMeta2 - }, + } }, meta = documentMeta }; @@ -468,7 +468,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_post_relationship_request() + public async Task Accepts_meta_in_add_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); From fdac07694b0fac6fb269b54469c20481f67e335f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 14 Jan 2026 23:15:31 +0000 Subject: [PATCH 37/51] fix cleanup-code task --- .../IntegrationTests/Meta/RequestMetaTests.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 67b138d11b..59ac4fffc0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -182,6 +182,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); + store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); @@ -567,6 +568,60 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); } + [Fact] + public async Task Accepts_meta_in_delete_relationship_request_with_identifier_meta() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.ProductFamilies.Add(existingFamily); + existingTicket.ProductFamily = existingFamily; + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket.StringId, + meta = identifierMeta + } + }, + meta = documentMeta + }; + + string route = $"/productFamilies/{existingFamily.StringId}/relationships/tickets"; + + // Act + (HttpResponseMessage httpResponse, _) = await _testContext.ExecuteDeleteAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + + store.Document.Should().NotBeNull(); + + store.Document.Data.SingleValue.Should().BeNull(); + + store.Document.Meta.Should().BeEquivalentToJson(documentMeta); + + store.Document.Data.Should().NotBeNull(); + store.Document.Data.ManyValue.Should().HaveCount(1); + + store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); + } + private sealed class CapturingDocumentAdapter : IDocumentAdapter { private readonly IDocumentAdapter _innerAdapter; From 539382b42599c4c7bc32f1c632830122839e3bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 17 Jan 2026 16:28:34 +0000 Subject: [PATCH 38/51] Pushed updates addressing review comments. --- .../IntegrationTests/Meta/MetaFakers.cs | 15 ++- .../IntegrationTests/Meta/RequestMetaTests.cs | 98 ++++++------------- 2 files changed, 40 insertions(+), 73 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs index dea2d0eecb..07b41b4f0e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaFakers.cs @@ -37,10 +37,11 @@ internal sealed class MetaFakers .CustomInstantiator(faker => new Dictionary { ["source"] = faker.PickRandom("ui", "api", "import"), - ["confidence"] = faker.Random.Double(0.1) + ["confidence"] = faker.Random.Double(0.1), + ["details"] = null })); - private readonly Lazy>> _lazyRelationshipIdentifierMetaFaker = new(() => new Faker>() + private readonly Lazy>> _lazyIdentifierMetaFaker = new(() => new Faker>() .MakeDeterministic() .CustomInstantiator(faker => new Dictionary { @@ -48,10 +49,18 @@ internal sealed class MetaFakers ["optionalNote"] = faker.Lorem.Word() })); + private readonly Lazy>> _lazyOperationMetaFaker = new(() => new Faker>() + .MakeDeterministic() + .CustomInstantiator(faker => new Dictionary + { + ["version"] = faker.Random.Int(1, 10) + })); + public Faker ProductFamily => _lazyProductFamilyFaker.Value; public Faker SupportTicket => _lazySupportTicketFaker.Value; public Faker> DocumentMeta => _lazyDocumentMetaFaker.Value; public Faker> ResourceMeta => _lazyResourceMetaFaker.Value; public Faker> RelationshipMeta => _lazyRelationshipMetaFaker.Value; - public Faker> RelationshipIdentifierMeta => _lazyRelationshipIdentifierMetaFaker.Value; + public Faker> IdentifierMeta => _lazyIdentifierMetaFaker.Value; + public Faker> OperationMeta => _lazyOperationMetaFaker.Value; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 59ac4fffc0..eff751b577 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -38,7 +38,7 @@ public RequestMetaTests(IntegrationTestContext, M } [Fact] - public async Task Accepts_meta_in_update_resource_request_with_to_one_relationship() + public async Task Accepts_meta_in_update_resource_request_with_ToOne_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -46,16 +46,16 @@ public async Task Accepts_meta_in_update_resource_request_with_to_one_relationsh Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - await _testContext.RunOnDatabaseAsync(async dbContex => + await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContex.ProductFamilies.Add(existingFamily); - dbContex.SupportTickets.Add(existingTicket); - await dbContex.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -92,7 +92,6 @@ await _testContext.RunOnDatabaseAsync(async dbContex => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.Should().NotBeNull(); @@ -110,7 +109,7 @@ await _testContext.RunOnDatabaseAsync(async dbContex => } [Fact] - public async Task Accepts_meta_in_update_resource_request_with_to_many_relationship() + public async Task Accepts_meta_in_update_resource_request_with_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -118,8 +117,8 @@ public async Task Accepts_meta_in_update_resource_request_with_to_many_relations Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -196,7 +195,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_resource_request_with_to_one_relationship() + public async Task Accepts_meta_in_add_resource_request_with_ToOne_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -204,7 +203,7 @@ public async Task Accepts_meta_in_add_resource_request_with_to_one_relationship( Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -272,7 +271,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_resource_request_with_to_many_relationship() + public async Task Accepts_meta_in_add_resource_request_with_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -280,8 +279,8 @@ public async Task Accepts_meta_in_add_resource_request_with_to_many_relationship Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -361,13 +360,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_to_one_relationship_request() + public async Task Accepts_meta_in_update_ToOne_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -385,7 +384,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "productFamilies", id = existingFamily.StringId, - meta = relationshipMeta + meta = identifierMeta }, meta = documentMeta }; @@ -404,18 +403,18 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); + store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); } [Fact] - public async Task Accepts_meta_in_update_to_many_relationship_request() + public async Task Accepts_meta_in_update_ToMany_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -424,8 +423,7 @@ public async Task Accepts_meta_in_update_to_many_relationship_request() await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.Add(existingTicket1); - dbContext.SupportTickets.Add(existingTicket2); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -469,14 +467,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_relationship_request() + public async Task Accepts_meta_in_add_ToMany_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -485,8 +483,7 @@ public async Task Accepts_meta_in_add_relationship_request() await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.Add(existingTicket1); - dbContext.SupportTickets.Add(existingTicket2); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -530,52 +527,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_remove_relationship_request() - { - // Arrange - var store = _testContext.Factory.Services.GetRequiredService(); - - Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - existingTicket.ProductFamily = existingFamily; - dbContext.SupportTickets.Add(existingTicket); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - data = (object?)null, - meta = documentMeta - }; - - string route = $"/supportTickets/{existingTicket.StringId}/relationships/productFamily"; - - // Act - (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - - store.Document.Should().NotBeNull(); - - store.Document.Data.SingleValue.Should().BeNull(); - - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - } - - [Fact] - public async Task Accepts_meta_in_delete_relationship_request_with_identifier_meta() + public async Task Accepts_meta_in_remove_from_ToMany_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); From 689405b2d7207701411778810de1ae90d8bae9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 18 Jan 2026 18:29:20 +0000 Subject: [PATCH 39/51] Pushed updates addressing missing review comments. --- .../Meta/OperationsRequestMetaTests.cs | 427 +++++++++++++----- .../IntegrationTests/Meta/RequestMetaTests.cs | 2 +- 2 files changed, 309 insertions(+), 120 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 92deea01ca..ce67ff3e3b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -38,18 +38,23 @@ public OperationsRequestMetaTests(IntegrationTestContext(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { + dbContext.ProductFamilies.Add(existingFamily); dbContext.SupportTickets.Add(existingTicket); await dbContext.SaveChangesAsync(); }); @@ -69,8 +74,22 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { description = existingTicket.Description }, + relationships = new + { + productFamily = new + { + data = new + { + type = "productFamilies", + id = existingFamily.StringId, + meta = identifierMeta + }, + meta = relationshipMeta + } + }, meta = resourceMeta - } + }, + meta = operationMeta } }, meta = documentMeta @@ -90,29 +109,54 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().HaveCount(1); - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); - operation.Data.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); + + operation.Data.Should().NotBeNull(); + + operation.Data.SingleValue.Should().NotBeNull(); + + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); + + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); + + value.Meta.Should().BeEquivalentToJson(relationshipMeta); - ResourceObject? resource = operation.Data.SingleValue; - resource.Should().NotBeNull(); - resource.Meta.Should().BeEquivalentToJson(resourceMeta); + value.Data.Should().NotBeNull(); + + value.Data.SingleValue.Should().NotBeNull(); + + value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); + }); + }); } [Fact] - public async Task Accepts_meta_in_atomic_remove_resource_operation() + public async Task Accepts_meta_in_update_resource_operation_with_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary operationMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.SupportTickets.Add(existingTicket); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -122,11 +166,37 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { new { - op = "remove", - @ref = new + op = "update", + data = new { - type = "supportTickets", - id = existingTicket.StringId + type = "productFamilies", + id = existingFamily.StringId, + attributes = new + { + name = existingFamily.Name + }, + relationships = new + { + tickets = new + { + data = new[] + { + new { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 + } + }, + meta = relationshipMeta + } + }, + meta = resourceMeta }, meta = operationMeta } @@ -137,35 +207,57 @@ await _testContext.RunOnDatabaseAsync(async dbContext => const string route = "/operations"; // Act - (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - responseDocument.Should().BeEmpty(); - store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().HaveCount(1); - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); + + operation.Data.SingleValue.Should().NotBeNull(); + + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); + + operation.Data.SingleValue.Relationships.Should().NotBeNull(); + + operation.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => + { + value.Should().NotBeNull(); + + value.Meta.Should().BeEquivalentToJson(relationshipMeta); - operation.Meta.Should().BeEquivalentToJson(operationMeta); + value.Data.Should().NotBeNull(); + + value.Data.ManyValue.Should().NotBeNull(); + value.Data.ManyValue.Should().HaveCount(2); + + value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + }); + }); } [Fact] - public async Task Accepts_meta_in_relationship_of_atomic_add_resource_operation() + public async Task Accepts_meta_in_add_resource_request_with_ToOne_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -204,7 +296,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } }, meta = resourceMeta - } + }, + meta = operationMeta } }, meta = documentMeta @@ -224,71 +317,95 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().HaveCount(1); - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); - operation.Data.Should().NotBeNull(); - operation.Data.SingleValue.Should().NotBeNull(); + operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); + operation.Data.Should().NotBeNull(); + operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => - { - value.Should().NotBeNull(); + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => + { + value.Should().NotBeNull(); - value.Data.Should().NotBeNull(); + value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.SingleValue.Should().NotBeNull(); + value.Data.Should().NotBeNull(); - value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); + value.Data.SingleValue.Should().NotBeNull(); + + value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); + }); }); } [Fact] - public async Task Accepts_meta_in_relationship_of_atomic_update_resource_operation() + public async Task Accepts_meta_in_add_resource_request_with_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; + + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { - existingTicket.ProductFamily = existingFamily; - dbContext.SupportTickets.Add(existingTicket); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); - var requestBody = new { atomic__operations = new[] { new { - op = "update", + op = "add", data = new { - type = "supportTickets", - id = existingTicket.StringId, + type = "productFamilies", + attributes = new + { + name = newFamilyName + }, relationships = new { - productFamily = new + tickets = new { - data = (object?)null, + data = new[] + { + new + { + type = "supportTickets", + id = existingTicket1.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 + } + }, meta = relationshipMeta } - } + }, + meta = resourceMeta }, - meta = resourceMeta + meta = operationMeta } }, meta = documentMeta @@ -300,49 +417,60 @@ await _testContext.RunOnDatabaseAsync(async dbContext => (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().HaveCount(1); - AtomicOperationObject? operation = store.Document.Operations[0]; - operation.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(resourceMeta); + operation.Meta.Should().BeEquivalentToJson(operationMeta); + operation.Data.Should().NotBeNull(); - operation.Data.Should().NotBeNull(); + operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => - { - value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => + { + value.Should().NotBeNull(); + + value.Meta.Should().BeEquivalentToJson(relationshipMeta); + + value.Data.Should().NotBeNull(); + + value.Data.ManyValue.Should().NotBeNull(); + value.Data.ManyValue.Should().HaveCount(2); + + value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + }); }); } [Fact] - public async Task Accepts_meta_in_update_to_one_relationship_operation() + public async Task Accepts_meta_in_update_ToOne_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - await _testContext.RunOnDatabaseAsync(async dbContex => + await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContex.ProductFamilies.Add(existingFamily); - dbContex.SupportTickets.Add(existingTicket); - await dbContex.SaveChangesAsync(); + dbContext.ProductFamilies.Add(existingFamily); + dbContext.SupportTickets.Add(existingTicket); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -364,7 +492,7 @@ await _testContext.RunOnDatabaseAsync(async dbContex => id = existingFamily.StringId, meta = relationshipMeta }, - meta = resourceMeta + meta = operationMeta } }, meta = documentMeta @@ -384,25 +512,27 @@ await _testContext.RunOnDatabaseAsync(async dbContex => store.Document.Operations.Should().NotBeNull(); - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); - op.Meta.Should().BeEquivalentToJson(resourceMeta); + operation.Meta.Should().BeEquivalentToJson(operationMeta); - op.Data.SingleValue.Should().NotBeNull(); - op.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Data.SingleValue.Should().NotBeNull(); + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); + }); } [Fact] - public async Task Accepts_meta_in_update_to_many_relationship_operation() + public async Task Accepts_meta_in_update_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -443,7 +573,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = identifierMeta2 } }, - meta = relationshipMeta + meta = operationMeta } }, meta = documentMeta @@ -463,29 +593,31 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().NotBeNull(); - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); - op.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Meta.Should().BeEquivalentToJson(operationMeta); - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(2); + operation.Data.ManyValue.Should().NotBeNull(); + operation.Data.ManyValue.Should().HaveCount(2); - op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - op.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + }); } [Fact] - public async Task Accepts_meta_in_add_to_relationship_operation() + public async Task Accepts_meta_in_add_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta1 = _fakers.RelationshipIdentifierMeta.GenerateOne(); - Dictionary identifierMeta2 = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); @@ -526,7 +658,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => meta = identifierMeta2 } }, - meta = relationshipMeta + meta = operationMeta } }, meta = documentMeta @@ -546,43 +678,39 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().NotBeNull(); - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); - op.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Meta.Should().BeEquivalentToJson(operationMeta); - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(2); + operation.Data.ManyValue.Should().NotBeNull(); + operation.Data.ManyValue.Should().HaveCount(2); - op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - op.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); + }); } [Fact] - public async Task Accepts_meta_in_remove_from_relationship_operation() + public async Task Accepts_meta_in_remove_from_ToOne_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.RelationshipIdentifierMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - - existingFamily.Tickets = new List - { - existingTicket1, - existingTicket2 - }; + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); + existingTicket.ProductFamily = existingFamily; + dbContext.SupportTickets.Add(existingTicket); await dbContext.SaveChangesAsync(); }); @@ -604,11 +732,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => new { type = "supportTickets", - id = existingTicket1.StringId, + id = existingTicket.StringId, meta = identifierMeta } }, - meta = relationshipMeta + meta = operationMeta } }, meta = documentMeta @@ -628,15 +756,76 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Operations.Should().NotBeNull(); - AtomicOperationObject? op = store.Document.Operations[0]; - op.Should().NotBeNull(); + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); - op.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Data.ManyValue.Should().NotBeNull(); + operation.Data.ManyValue.Should().HaveCount(1); - op.Data.ManyValue.Should().NotBeNull(); - op.Data.ManyValue.Should().HaveCount(1); + operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); + }); + } - op.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); + [Fact] + public async Task Accepts_meta_in_atomic_remove_resource_operation() + { + // Arrange + var store = _testContext.Factory.Services.GetRequiredService(); + + Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); + Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + + 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 = operationMeta + } + }, + meta = documentMeta + }; + + const 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().BeEquivalentToJson(documentMeta); + + store.Document.Operations.Should().HaveCount(1); + + store.Document.Operations.Should().ContainSingle().Which.With(operation => + { + operation.Should().NotBeNull(); + + operation.Meta.Should().BeEquivalentToJson(operationMeta); + }); } private sealed class CapturingDocumentAdapter : IDocumentAdapter diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index eff751b577..c90b2b11c1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -527,7 +527,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_remove_from_ToMany_relationship_request() + public async Task Accepts_meta_in_remove_from_ToOne_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); From a133a32095d6041ce635a13b3569d5d8d6585401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 18 Jan 2026 18:45:07 +0000 Subject: [PATCH 40/51] Fix failed code inspection --- .../IntegrationTests/Meta/OperationsRequestMetaTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index ce67ff3e3b..02c73a4984 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -181,7 +181,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { data = new[] { - new { + new + { type = "supportTickets", id = existingTicket1.StringId, meta = identifierMeta1 @@ -367,6 +368,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); + var requestBody = new { atomic__operations = new[] From b072dd568910729e744151aa3667410574fa9963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Mon, 19 Jan 2026 23:14:40 +0000 Subject: [PATCH 41/51] Pushed updates addressing review comments. --- .../IntegrationTests/Meta/RequestMetaTests.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index c90b2b11c1..59db5f5060 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -94,7 +94,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.Should().NotBeNull(); store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); @@ -527,22 +526,25 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_remove_from_ToOne_relationship_request() + public async Task Accepts_meta_in_remove_from_ToMany_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + existingTicket1.ProductFamily = existingFamily; + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + existingTicket2.ProductFamily = existingFamily; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - existingTicket.ProductFamily = existingFamily; - dbContext.SupportTickets.Add(existingTicket); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -553,8 +555,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => new { type = "supportTickets", - id = existingTicket.StringId, - meta = identifierMeta + id = existingTicket2.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 } }, meta = documentMeta @@ -570,14 +578,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Data.SingleValue.Should().BeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.Should().NotBeNull(); - store.Document.Data.ManyValue.Should().HaveCount(1); + store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); + store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } private sealed class CapturingDocumentAdapter : IDocumentAdapter From 845d4adae3d6ce066d7025bc3fcef2f08f20eb22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Tue, 20 Jan 2026 12:32:38 +0000 Subject: [PATCH 42/51] Pushed updates addressing missing review comments. --- .../Meta/OperationsRequestMetaTests.cs | 86 +++++++------------ .../IntegrationTests/Meta/RequestMetaTests.cs | 14 ++- 2 files changed, 42 insertions(+), 58 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 02c73a4984..32bd0464d8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -38,15 +38,15 @@ public OperationsRequestMetaTests(IntegrationTestContext(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); @@ -107,16 +107,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().HaveCount(1); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.Should().NotBeNull(); - operation.Data.SingleValue.Should().NotBeNull(); operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); @@ -127,8 +123,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.Should().NotBeNull(); - value.Data.SingleValue.Should().NotBeNull(); value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); @@ -137,14 +131,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_resource_operation_with_ToMany_relationship_operation() + public async Task Accepts_meta_in_update_resource_operation_with_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); - Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); + Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); @@ -229,17 +223,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - operation.Data.SingleValue.Relationships.Should().NotBeNull(); - operation.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.Should().NotBeNull(); - - value.Data.ManyValue.Should().NotBeNull(); value.Data.ManyValue.Should().HaveCount(2); value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); @@ -249,7 +238,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_resource_request_with_ToOne_relationship_operation() + public async Task Accepts_meta_in_add_resource_operation_with_ToOne_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -314,18 +303,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Should().NotBeNull(); - store.Document.Meta.Should().HaveCount(documentMeta.Count); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); @@ -336,8 +321,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.Should().NotBeNull(); - value.Data.SingleValue.Should().NotBeNull(); value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); @@ -346,15 +329,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_resource_request_with_ToMany_relationship_operation() + public async Task Accepts_meta_in_add_resource_operation_with_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary resourceMeta = _fakers.ResourceMeta.GenerateOne(); + Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); @@ -425,14 +408,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.Should().NotBeNull(); operation.Data.SingleValue.Should().NotBeNull(); @@ -444,9 +424,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.Should().NotBeNull(); - - value.Data.ManyValue.Should().NotBeNull(); value.Data.ManyValue.Should().HaveCount(2); value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); @@ -456,7 +433,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_ToOne_relationship_operation() + public async Task Accepts_meta_in_update_operation_ToOne_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -512,8 +489,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); @@ -526,7 +501,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_ToMany_relationship_operation() + public async Task Accepts_meta_in_update_operation_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -593,15 +568,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().NotBeNull(); operation.Data.ManyValue.Should().HaveCount(2); operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); @@ -611,7 +583,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_ToMany_relationship_operation() + public async Task Accepts_meta_in_add_operation_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -678,15 +650,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().NotBeNull(); operation.Data.ManyValue.Should().HaveCount(2); operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); @@ -696,23 +665,26 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_remove_from_ToOne_relationship_operation() + public async Task Accepts_meta_in_remove_operation_from_ToMany_relationship() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); - Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); + Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); + existingTicket1.ProductFamily = existingFamily; + SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); + existingTicket2.ProductFamily = existingFamily; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - existingTicket.ProductFamily = existingFamily; - dbContext.SupportTickets.Add(existingTicket); + dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -734,8 +706,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => new { type = "supportTickets", - id = existingTicket.StringId, - meta = identifierMeta + id = existingTicket1.StringId, + meta = identifierMeta1 + }, + new + { + type = "supportTickets", + id = existingTicket2.StringId, + meta = identifierMeta2 } }, meta = operationMeta @@ -756,23 +734,21 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().NotBeNull(); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().NotBeNull(); - operation.Data.ManyValue.Should().HaveCount(1); + operation.Data.ManyValue.Should().HaveCount(2); - operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta); + operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); + operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } [Fact] - public async Task Accepts_meta_in_atomic_remove_resource_operation() + public async Task Accepts_meta_in_remove_resource_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -820,8 +796,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().HaveCount(1); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 59db5f5060..e23937fae2 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -49,6 +49,7 @@ public async Task Accepts_meta_in_update_resource_request_with_ToOne_relationshi Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -64,6 +65,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "supportTickets", id = existingTicket.StringId, + attributes = new + { + description = newTicketDescription + }, relationships = new { productFamily = new @@ -120,6 +125,7 @@ public async Task Accepts_meta_in_update_resource_request_with_ToMany_relationsh Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -136,6 +142,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "productFamilies", id = existingFamily.StringId, + attributes = new + { + name = newFamilyName + }, relationships = new { tickets = new @@ -415,9 +425,9 @@ public async Task Accepts_meta_in_update_ToMany_relationship_request() Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -466,7 +476,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_ToMany_relationship_request() + public async Task Accepts_meta_in_add_to_ToMany_relationship_request() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); From 45dc467592aa529354a38005de704a21e679f27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 24 Jan 2026 21:50:36 +0000 Subject: [PATCH 43/51] Pushed updates addressing review comments. --- .../Meta/CapturingDocumentAdapter.cs | 27 ++++++++++ .../Meta/OperationsRequestMetaTests.cs | 53 ++++--------------- .../Meta/RequestDocumentStore.cs | 8 +++ .../IntegrationTests/Meta/RequestMetaTests.cs | 38 ++----------- 4 files changed, 49 insertions(+), 77 deletions(-) create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs new file mode 100644 index 0000000000..53e43f71ef --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs @@ -0,0 +1,27 @@ +using JsonApiDotNetCore.Serialization.Objects; +using JsonApiDotNetCore.Serialization.Request.Adapters; + +namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; + +public 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); + } +} + + diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 32bd0464d8..0dff117e60 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -50,6 +50,7 @@ public async Task Accepts_meta_in_update_resource_operation_with_ToOne_relations Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); + string newTicketDescription = _fakers.SupportTicket.GenerateOne().Description; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -72,7 +73,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => id = existingTicket.StringId, attributes = new { - description = existingTicket.Description + description = newTicketDescription }, relationships = new { @@ -211,8 +212,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Operations.Should().HaveCount(1); - store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); @@ -433,14 +432,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_operation_ToOne_relationship() + public async Task Accepts_meta_in_update_ToOne_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); Dictionary documentMeta = _fakers.DocumentMeta.GenerateOne(); Dictionary operationMeta = _fakers.OperationMeta.GenerateOne(); - Dictionary relationshipMeta = _fakers.RelationshipMeta.GenerateOne(); + Dictionary identifierMeta = _fakers.IdentifierMeta.GenerateOne(); SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); @@ -469,7 +468,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { type = "productFamilies", id = existingFamily.StringId, - meta = relationshipMeta + meta = identifierMeta }, meta = operationMeta } @@ -501,7 +500,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_update_operation_ToMany_relationship() + public async Task Accepts_meta_in_update_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -583,7 +582,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_operation_ToMany_relationship() + public async Task Accepts_meta_in_add_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -676,15 +675,11 @@ public async Task Accepts_meta_in_remove_operation_from_ToMany_relationship() Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - existingTicket1.ProductFamily = existingFamily; - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - existingTicket2.ProductFamily = existingFamily; + existingFamily.Tickets = _fakers.SupportTicket.GenerateList(2); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -706,13 +701,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => new { type = "supportTickets", - id = existingTicket1.StringId, + id = existingFamily.Tickets[0].StringId, meta = identifierMeta1 }, new { type = "supportTickets", - id = existingTicket2.StringId, + id = existingFamily.Tickets[1].StringId, meta = identifierMeta2 } }, @@ -790,8 +785,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - responseDocument.Should().BeEmpty(); - store.Document.Should().NotBeNull(); store.Document.Meta.Should().BeEquivalentToJson(documentMeta); @@ -803,30 +796,4 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Meta.Should().BeEquivalentToJson(operationMeta); }); } - - 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; } - } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs new file mode 100644 index 0000000000..aa395bdb88 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs @@ -0,0 +1,8 @@ +using JsonApiDotNetCore.Serialization.Objects; + +namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; + +public sealed class RequestDocumentStore +{ + public Document? Document { get; set; } +} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index e23937fae2..1076927737 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -485,9 +485,9 @@ public async Task Accepts_meta_in_add_to_ToMany_relationship_request() Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); + ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -546,15 +546,11 @@ public async Task Accepts_meta_in_remove_from_ToMany_relationship_request() Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); - existingTicket1.ProductFamily = existingFamily; - SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); - existingTicket2.ProductFamily = existingFamily; + existingFamily.Tickets = _fakers.SupportTicket.GenerateList(2); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.ProductFamilies.Add(existingFamily); - dbContext.SupportTickets.AddRange(existingTicket1, existingTicket2); await dbContext.SaveChangesAsync(); }); @@ -565,13 +561,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => new { type = "supportTickets", - id = existingTicket2.StringId, + id = existingFamily.Tickets[0].StringId, meta = identifierMeta1 }, new { type = "supportTickets", - id = existingTicket2.StringId, + id = existingFamily.Tickets[1].StringId, meta = identifierMeta2 } }, @@ -595,30 +591,4 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } - - 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 f9bd02fb13c9d0f019b9cacadd5469123c9ce654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 24 Jan 2026 21:59:24 +0000 Subject: [PATCH 44/51] Fix compilation error. --- .../IntegrationTests/Meta/OperationsRequestMetaTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 0dff117e60..b0c89e2d87 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -495,7 +495,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => operation.Meta.Should().BeEquivalentToJson(operationMeta); operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(relationshipMeta); + operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); } From db6c3e8e92cd36fd8b304b6ab68ef8d3efc6c142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 24 Jan 2026 22:11:57 +0000 Subject: [PATCH 45/51] Fix cleanup code errors. --- .../IntegrationTests/Meta/CapturingDocumentAdapter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs index 53e43f71ef..b4378ffa84 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs @@ -23,5 +23,3 @@ public CapturingDocumentAdapter(IDocumentAdapter innerAdapter, RequestDocumentSt return _innerAdapter.Convert(document); } } - - From 7ddd9496a217b82b8f2241a4d15b334e053708fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 25 Jan 2026 22:42:41 +0000 Subject: [PATCH 46/51] Fix build errors. --- .../IntegrationTests/Meta/CapturingDocumentAdapter.cs | 2 +- .../IntegrationTests/Meta/RequestDocumentStore.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs index b4378ffa84..9ff423fda1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/CapturingDocumentAdapter.cs @@ -3,7 +3,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; -public sealed class CapturingDocumentAdapter : IDocumentAdapter +internal sealed class CapturingDocumentAdapter : IDocumentAdapter { private readonly IDocumentAdapter _innerAdapter; private readonly RequestDocumentStore _requestDocumentStore; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs index aa395bdb88..666b5d7c29 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestDocumentStore.cs @@ -2,7 +2,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; -public sealed class RequestDocumentStore +internal sealed class RequestDocumentStore { public Document? Document { get; set; } } From a4628ae7ace3d82d92be75a41f1d8bc08f7f7712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sun, 25 Jan 2026 22:59:01 +0000 Subject: [PATCH 47/51] Fix build errors. --- .../IntegrationTests/Meta/OperationsRequestMetaTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index b0c89e2d87..6ca5e8d746 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -780,7 +780,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => const string route = "/operations"; // Act - (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePostAtomicAsync(route, requestBody); // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); From 635ebd54ae9596c9b9b59ff90c74287f83d379e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Wed, 28 Jan 2026 21:20:38 +0000 Subject: [PATCH 48/51] Pushed updates addressing review comments. --- .../IntegrationTests/Meta/OperationsRequestMetaTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 6ca5e8d746..07120e3e8d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -582,7 +582,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_add_ToMany_relationship_operation() + public async Task Accepts_meta_in_add_to_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); @@ -664,7 +664,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => } [Fact] - public async Task Accepts_meta_in_remove_operation_from_ToMany_relationship() + public async Task Accepts_meta_in_remove_from_ToMany_relationship_operation() { // Arrange var store = _testContext.Factory.Services.GetRequiredService(); From 1cd04a201c9005244ac0342552768e6f8fda0ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Magalh=C3=A3es?= Date: Sat, 31 Jan 2026 08:39:31 +0000 Subject: [PATCH 49/51] Pushed updates addressing review comments. --- .../Meta/OperationsRequestMetaTests.cs | 3 ++- .../IntegrationTests/Meta/RequestMetaTests.cs | 14 ++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 07120e3e8d..738dbe8e7d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -144,6 +144,7 @@ public async Task Accepts_meta_in_update_resource_operation_with_ToMany_relation Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); + string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -168,7 +169,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => id = existingFamily.StringId, attributes = new { - name = existingFamily.Name + name = newFamilyName }, relationships = new { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 1076927737..1f8342e85c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -90,10 +90,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/supportTickets/{existingTicket.StringId}"; // Act - (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.ShouldHaveStatusCode(HttpStatusCode.NoContent); + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); @@ -176,10 +176,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string route = $"/productFamilies/{existingFamily.StringId}"; // Act - (HttpResponseMessage response, _) = await _testContext.ExecutePatchAsync(route, requestBody); + (HttpResponseMessage httpResponse, _) = await _testContext.ExecutePatchAsync(route, requestBody); // Assert - response.ShouldHaveStatusCode(HttpStatusCode.NoContent); + httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); @@ -189,8 +189,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); - store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); @@ -265,8 +263,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); - store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); @@ -352,8 +348,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); - store.Document.Data.SingleValue.Relationships.Should().NotBeNull(); - store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); From f1ca076270c7d842077df192c572740909e8ab4f Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 31 Jan 2026 11:09:02 +0100 Subject: [PATCH 50/51] Fix declaration order --- .../IntegrationTests/Meta/OperationsRequestMetaTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 738dbe8e7d..22fc8e454f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -144,8 +144,8 @@ public async Task Accepts_meta_in_update_resource_operation_with_ToMany_relation Dictionary identifierMeta1 = _fakers.IdentifierMeta.GenerateOne(); Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); - string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; ProductFamily existingFamily = _fakers.ProductFamily.GenerateOne(); + string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); From c27cc40b79cd0d6c9df7f6d3ac96004c46e42e47 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 31 Jan 2026 11:25:30 +0100 Subject: [PATCH 51/51] Cleanup whitespace --- .../Meta/OperationsRequestMetaTests.cs | 48 ------------------- .../IntegrationTests/Meta/RequestMetaTests.cs | 34 ------------- 2 files changed, 82 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs index 22fc8e454f..77203ffabc 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/OperationsRequestMetaTests.cs @@ -105,27 +105,20 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.SingleValue.Should().NotBeNull(); - value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); }); @@ -210,27 +203,20 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); operation.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); @@ -302,27 +288,20 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); operation.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.SingleValue.Should().NotBeNull(); - value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); }); @@ -342,7 +321,6 @@ public async Task Accepts_meta_in_add_resource_operation_with_ToMany_relationshi Dictionary identifierMeta2 = _fakers.IdentifierMeta.GenerateOne(); string newFamilyName = _fakers.ProductFamily.GenerateOne().Name; - SupportTicket existingTicket1 = _fakers.SupportTicket.GenerateOne(); SupportTicket existingTicket2 = _fakers.SupportTicket.GenerateOne(); @@ -405,27 +383,20 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Should().NotBeNull(); - operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); operation.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); @@ -486,15 +457,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.SingleValue.Should().NotBeNull(); operation.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); @@ -565,19 +533,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().HaveCount(2); - operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } @@ -647,19 +610,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().HaveCount(2); - operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } @@ -727,17 +685,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); - operation.Data.ManyValue.Should().HaveCount(2); - operation.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); operation.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); @@ -787,13 +741,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Operations.Should().ContainSingle().Which.With(operation => { operation.Should().NotBeNull(); - operation.Meta.Should().BeEquivalentToJson(operationMeta); }); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs index 1f8342e85c..63dca10a6f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/RequestMetaTests.cs @@ -96,11 +96,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => @@ -182,20 +179,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); @@ -257,20 +249,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().ContainKey("productFamily").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.SingleValue.Should().NotBeNull(); - value.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); }); } @@ -342,22 +329,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.SingleValue.Should().NotBeNull(); store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(resourceMeta); store.Document.Data.SingleValue.Relationships.Should().ContainKey("tickets").WhoseValue.With(value => { value.Should().NotBeNull(); - value.Meta.Should().BeEquivalentToJson(relationshipMeta); - value.Data.ManyValue.Should().HaveCount(2); - value.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - value.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }); } @@ -401,11 +382,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.SingleValue.Should().NotBeNull(); - store.Document.Data.SingleValue.Meta.Should().BeEquivalentToJson(identifierMeta); } @@ -459,13 +437,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } @@ -519,13 +493,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); - store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); } @@ -575,13 +545,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent); - store.Document.Should().NotBeNull(); - store.Document.Meta.Should().BeEquivalentToJson(documentMeta); - store.Document.Data.ManyValue.Should().HaveCount(2); - store.Document.Data.ManyValue[0].Meta.Should().BeEquivalentToJson(identifierMeta1); store.Document.Data.ManyValue[1].Meta.Should().BeEquivalentToJson(identifierMeta2); }