From 8d8f115dc31629d44180faacf0559ceb8a37e8f8 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Thu, 25 Sep 2025 15:01:18 -0700 Subject: [PATCH 01/17] fixes empty cell bug --- src/Core/Resolvers/QueryExecutor.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Core/Resolvers/QueryExecutor.cs b/src/Core/Resolvers/QueryExecutor.cs index 908c1bb1e8..225e4a6753 100644 --- a/src/Core/Resolvers/QueryExecutor.cs +++ b/src/Core/Resolvers/QueryExecutor.cs @@ -740,6 +740,12 @@ internal int StreamCharData(DbDataReader dbDataReader, long availableSize, Strin // else we throw exception. ValidateSize(availableSize, resultFieldSize); + // If the cell is empty, don't append anything to the resultJsonString and return 0. + if (resultFieldSize == 0) + { + return 0; + } + char[] buffer = new char[resultFieldSize]; // read entire field into buffer and reduce available size. From 5765e719d792c9707225f7b4fe049d0ed297b0f5 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Fri, 26 Sep 2025 10:01:39 -0700 Subject: [PATCH 02/17] adds unit test --- .../UnitTests/SqlQueryExecutorUnitTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs index 92b076107a..d4388af201 100644 --- a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs @@ -616,6 +616,50 @@ public void ValidateStreamingLogicForStoredProcedures(int readDataLoops, bool ex } } + /// + /// Makes sure the stream logic handles cells with empty strings correctly. + /// + [DataTestMethod, TestCategory(TestCategory.MSSQL)] + public void ValidateStreamingLogicForEmptyCellsAsync() + { + TestHelper.SetupDatabaseEnvironment(TestCategory.MSSQL); + FileSystem fileSystem = new(); + FileSystemRuntimeConfigLoader loader = new(fileSystem); + RuntimeConfig runtimeConfig = new( + Schema: "UnitTestSchema", + DataSource: new DataSource(DatabaseType: DatabaseType.MSSQL, "", Options: null), + Runtime: new( + Rest: new(), + GraphQL: new(), + Host: new(Cors: null, Authentication: null, MaxResponseSizeMB: 5) + ), + Entities: new(new Dictionary())); + + RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(runtimeConfig); + + Mock>> queryExecutorLogger = new(); + Mock httpContextAccessor = new(); + DbExceptionParser dbExceptionParser = new MsSqlDbExceptionParser(runtimeConfigProvider); + + // Instantiate the MsSqlQueryExecutor and Setup parameters for the query + MsSqlQueryExecutor msSqlQueryExecutor = new(runtimeConfigProvider, dbExceptionParser, queryExecutorLogger.Object, httpContextAccessor.Object); + + Mock dbDataReader = new(); + dbDataReader.Setup(d => d.HasRows).Returns(true); + + // Make sure GetChars returns 0 when buffer is null + dbDataReader.Setup(x => x.GetChars(It.IsAny(), It.IsAny(), null, It.IsAny(), It.IsAny())).Returns(0); + + // Make sure available size is set to > 0 + int availableSize = (int)runtimeConfig.MaxResponseSizeMB() * 1024 * 1024; + + // Stream char data should not return an exception + availableSize -= msSqlQueryExecutor.StreamCharData( + dbDataReader: dbDataReader.Object, availableSize: availableSize, resultJsonString: new(), ordinal: 0); + + Assert.AreEqual(availableSize, (int)runtimeConfig.MaxResponseSizeMB() * 1024 * 1024); + } + [TestCleanup] public void CleanupAfterEachTest() { From 2aeec4714248e783b8db41f7e76b410b56c4f019 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 8 Oct 2025 14:04:57 -0700 Subject: [PATCH 03/17] adds integration tests --- src/Core/Resolvers/QueryExecutor.cs | 7 +++++++ src/Service.Tests/DatabaseSchema-MySql.sql | 3 ++- .../GraphQLQueryTests/GraphQLQueryTestBase.cs | 15 +++++++++++++++ .../GraphQLQueryTests/MsSqlGraphQLQueryTests.cs | 6 ++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Core/Resolvers/QueryExecutor.cs b/src/Core/Resolvers/QueryExecutor.cs index 225e4a6753..97e2f7e8d4 100644 --- a/src/Core/Resolvers/QueryExecutor.cs +++ b/src/Core/Resolvers/QueryExecutor.cs @@ -772,6 +772,13 @@ internal int StreamByteData(DbDataReader dbDataReader, long availableSize, int o // else we throw exception. ValidateSize(availableSize, resultFieldSize); + // If the cell is empty, set resultBytes to an empty array and return 0. + if (resultFieldSize == 0) + { + resultBytes = Array.Empty(); + return 0; + } + resultBytes = new byte[resultFieldSize]; dbDataReader.GetBytes(ordinal: ordinal, dataOffset: 0, buffer: resultBytes, bufferOffset: 0, length: resultBytes.Length); diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index f746bc063a..dda93d86d1 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -388,7 +388,8 @@ INSERT INTO books(id, title, publisher_id) (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\\YOU', 1234), - (20, 'C:\\\\LIFE', 1234); + (20, 'C:\\\\LIFE', 1234), + (21, '', 1234); INSERT INTO book_website_placements(book_id, price) VALUES (1, 100), (2, 50), (3, 23), (5, 33); INSERT INTO website_users(id, username) VALUES (1, 'George'), (2, NULL), (3, ''), (4, 'book_lover_95'), (5, 'null'); INSERT INTO book_author_link(book_id, author_id) VALUES (1, 123), (2, 124), (3, 123), (3, 124), (4, 123), (4, 124), (5, 126); diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index 55a1becb4a..a8f8611549 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -855,6 +855,21 @@ public virtual async Task QueryWithNullResult() Assert.IsNull(actual.GetString()); } + [TestMethod] + public virtual async Task QueryWithEmptyStringResult() + { + string graphQLQueryName = "book_by_pk"; + string graphQLQuery = @"{ + book_by_pk(id: 21) { + title + } + }"; + + JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); + + Assert.IsNull(actual.GetString()); + } + [TestMethod] public async Task QueryWithNullableForeignKey(string dbQuery) { diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs index f65e7a5088..bfa7059400 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs @@ -268,6 +268,12 @@ SELECT title FROM books await QueryWithSingleColumnPrimaryKey(msSqlQuery); } + [TestMethod] + public async Task QueryWithEmptyStringCell() + { + await QueryWithEmptyStringResult(); + } + [TestMethod] public async Task QueryWithSingleColumnPrimaryKeyAndMappings() { From 1e0940f66f7f44d79d9f255c37af9faa8b01503e Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro <44936262+gilemos@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:06:21 -0700 Subject: [PATCH 04/17] Update src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs Co-authored-by: RubenCerna2079 <32799214+RubenCerna2079@users.noreply.github.com> --- src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs index f6972ee8e9..05b550344b 100644 --- a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs @@ -636,6 +636,11 @@ public void ValidateStreamingLogicForEmptyCellsAsync() Schema: "UnitTestSchema", DataSource: new DataSource(DatabaseType: DatabaseType.MSSQL, "", Options: null), Runtime: new( + Rest: new(), + GraphQL: new(), + Mcp: new(), + Host: new(Cors: null, Authentication: null, MaxResponseSizeMB: 5) + ), Rest: new(), GraphQL: new(), Host: new(Cors: null, Authentication: null, MaxResponseSizeMB: 5) From 7b6a07e0443bb5ac0088bc7be71b6742d62e81fa Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 8 Oct 2025 14:08:00 -0700 Subject: [PATCH 05/17] nit fix for test --- .../SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index a8f8611549..28acea7e12 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -867,7 +867,7 @@ public virtual async Task QueryWithEmptyStringResult() JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - Assert.IsNull(actual.GetString()); + Assert.AreEqual(actual.GetString(), ""); } [TestMethod] From 933171a9a7bbd5bf6d564042b6aa553f43ffa714 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 8 Oct 2025 14:09:12 -0700 Subject: [PATCH 06/17] nit test fix --- src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs index 05b550344b..2b62c6b444 100644 --- a/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs @@ -641,10 +641,6 @@ public void ValidateStreamingLogicForEmptyCellsAsync() Mcp: new(), Host: new(Cors: null, Authentication: null, MaxResponseSizeMB: 5) ), - Rest: new(), - GraphQL: new(), - Host: new(Cors: null, Authentication: null, MaxResponseSizeMB: 5) - ), Entities: new(new Dictionary())); RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(runtimeConfig); From 63629a69d6e27ce8c8c5e896161adbafaea029ac Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 8 Oct 2025 14:53:02 -0700 Subject: [PATCH 07/17] integration test fix --- .../SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index 28acea7e12..a8f8611549 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -867,7 +867,7 @@ public virtual async Task QueryWithEmptyStringResult() JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - Assert.AreEqual(actual.GetString(), ""); + Assert.IsNull(actual.GetString()); } [TestMethod] From 9814f2b058b2b8a28aeab6fff726e629d9177418 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 8 Oct 2025 15:40:58 -0700 Subject: [PATCH 08/17] fix more tests --- .../GraphQLPaginationTestBase.cs | 8 ++++++++ .../GraphQLQueryTests/GraphQLQueryTestBase.cs | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index e7e18d6090..de4efb7e54 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -167,6 +167,10 @@ public async Task RequestMaxUsingNegativeOne() { ""id"": 20, ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" } ], ""endCursor"": null, @@ -277,6 +281,10 @@ public async Task RequestNoParamFullConnection() { ""id"": 20, ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" } ], ""endCursor"": null, diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index a8f8611549..5937811f1e 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -867,7 +867,17 @@ public virtual async Task QueryWithEmptyStringResult() JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - Assert.IsNull(actual.GetString()); + // If the result is null (no book found), actual.ValueKind == Null + if (actual.ValueKind == JsonValueKind.Null) + { + Assert.IsNull(actual.GetString()); + } + else + { + // Otherwise, get the "title" property + string title = actual.GetProperty("title").GetString(); + Assert.IsTrue(string.IsNullOrEmpty(title)); + } } [TestMethod] From a445873e7eb5e95949b887292630516bae63a1b9 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Thu, 9 Oct 2025 11:42:39 -0700 Subject: [PATCH 09/17] fix more tests --- src/Service.Tests/DatabaseSchema-MsSql.sql | 3 ++- src/Service.Tests/DatabaseSchema-MySql.sql | 3 +-- .../GraphQLQueryTests/GraphQLQueryTestBase.cs | 13 ++----------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 3605b2628a..4e87394aee 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -531,7 +531,8 @@ VALUES (1, 'Awesome book', 1234), (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\YOU', 1234), -(20, 'C:\\LIFE', 1234); +(20, 'C:\\LIFE', 1234), +(21, '', 1234); SET IDENTITY_INSERT books OFF SET IDENTITY_INSERT books_mm ON diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index dda93d86d1..f746bc063a 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -388,8 +388,7 @@ INSERT INTO books(id, title, publisher_id) (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\\YOU', 1234), - (20, 'C:\\\\LIFE', 1234), - (21, '', 1234); + (20, 'C:\\\\LIFE', 1234); INSERT INTO book_website_placements(book_id, price) VALUES (1, 100), (2, 50), (3, 23), (5, 33); INSERT INTO website_users(id, username) VALUES (1, 'George'), (2, NULL), (3, ''), (4, 'book_lover_95'), (5, 'null'); INSERT INTO book_author_link(book_id, author_id) VALUES (1, 123), (2, 124), (3, 123), (3, 124), (4, 123), (4, 124), (5, 126); diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index 5937811f1e..7089717097 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -867,17 +867,8 @@ public virtual async Task QueryWithEmptyStringResult() JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - // If the result is null (no book found), actual.ValueKind == Null - if (actual.ValueKind == JsonValueKind.Null) - { - Assert.IsNull(actual.GetString()); - } - else - { - // Otherwise, get the "title" property - string title = actual.GetProperty("title").GetString(); - Assert.IsTrue(string.IsNullOrEmpty(title)); - } + string title = actual.GetProperty("title").GetString(); + Assert.AreEqual("", title); } [TestMethod] From 89e400061070df6c86bc7310cc5460fac00e7213 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Thu, 9 Oct 2025 12:18:35 -0700 Subject: [PATCH 10/17] fixes testing --- .../GraphQLPaginationTestBase.cs | 277 ++++++++++++------ .../GraphQLQueryTests/GraphQLQueryTestBase.cs | 16 - .../MsSqlGraphQLQueryTests.cs | 14 +- 3 files changed, 199 insertions(+), 108 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index de4efb7e54..f181e85871 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Threading.Tasks; +using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries; @@ -84,98 +85,194 @@ public async Task RequestMaxUsingNegativeOne() } }"; + string expected = ""; + + if (_sqlMetadataProvider.GetDatabaseType() == DatabaseType.MSSQL) + { + expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; + } + else + { + expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; + } + + // Note: The max page size is 21 for MsSql and 20 for all other data sources, so when using -1 // this resultset represents all books in the db. JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - string expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - }, - { - ""id"": 21, - ""title"": """" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs index 7089717097..55a1becb4a 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/GraphQLQueryTestBase.cs @@ -855,22 +855,6 @@ public virtual async Task QueryWithNullResult() Assert.IsNull(actual.GetString()); } - [TestMethod] - public virtual async Task QueryWithEmptyStringResult() - { - string graphQLQueryName = "book_by_pk"; - string graphQLQuery = @"{ - book_by_pk(id: 21) { - title - } - }"; - - JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - - string title = actual.GetProperty("title").GetString(); - Assert.AreEqual("", title); - } - [TestMethod] public async Task QueryWithNullableForeignKey(string dbQuery) { diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs index bfa7059400..f6824d8e51 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs @@ -269,9 +269,19 @@ SELECT title FROM books } [TestMethod] - public async Task QueryWithEmptyStringCell() + public virtual async Task QueryWithEmptyStringResult() { - await QueryWithEmptyStringResult(); + string graphQLQueryName = "book_by_pk"; + string graphQLQuery = @"{ + book_by_pk(id: 21) { + title + } + }"; + + JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); + + string title = actual.GetProperty("title").GetString(); + Assert.AreEqual("", title); } [TestMethod] From aaa3caa521f154568be4283834908f6c017cd764 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Thu, 9 Oct 2025 14:13:01 -0700 Subject: [PATCH 11/17] fix pagination test --- .../GraphQLPaginationTestBase.cs | 276 ++++++++++++------ 1 file changed, 184 insertions(+), 92 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index f181e85871..c29c9b90fb 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -85,8 +85,7 @@ public async Task RequestMaxUsingNegativeOne() } }"; - string expected = ""; - + string expected; if (_sqlMetadataProvider.GetDatabaseType() == DatabaseType.MSSQL) { expected = @"{ @@ -297,96 +296,189 @@ public async Task RequestNoParamFullConnection() }"; JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - string expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - }, - { - ""id"": 21, - ""title"": """" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; + string expected; + if (_sqlMetadataProvider.GetDatabaseType() == DatabaseType.MSSQL) + { + expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; + } + else + { + expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; + } SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } From 448b28fb2459f36ba73c901017e778ef04c4cac8 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Thu, 9 Oct 2025 15:10:48 -0700 Subject: [PATCH 12/17] fixes msSql tests --- .../GraphQLMutationTests/GraphQLMutationTestBase.cs | 6 +++--- .../GraphQLMutationTests/MsSqlGraphQLMutationTests.cs | 2 +- .../SqlTests/RestApiTests/Delete/MsSqlDeleteApiTest.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs index c9207a6672..2aa04cb606 100644 --- a/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs @@ -244,7 +244,7 @@ public async Task TestStoredProcedureMutationForInsertion(string dbQuery) /// Check: If the intended book is deleted from the DB and /// verifies the response. /// - public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyDeletion) + public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyDeletion, int expectedOriginalMaxId, int expectedFinalMaxId) { string graphQLMutationName = "executeDeleteLastInsertedBook"; string graphQLMutation = @" @@ -257,7 +257,7 @@ public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyD string currentDbResponse = await GetDatabaseResultAsync(dbQueryToVerifyDeletion); JsonDocument currentResult = JsonDocument.Parse(currentDbResponse); - Assert.AreEqual(currentResult.RootElement.GetProperty("maxId").GetInt64(), 20); + Assert.AreEqual(currentResult.RootElement.GetProperty("maxId").GetInt64(), expectedOriginalMaxId); JsonElement graphQLResponse = await ExecuteGraphQLRequestAsync(graphQLMutation, graphQLMutationName, isAuthenticated: true); // Stored Procedure didn't return anything @@ -266,7 +266,7 @@ public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyD // check to verify new element is inserted string updatedDbResponse = await GetDatabaseResultAsync(dbQueryToVerifyDeletion); JsonDocument updatedResult = JsonDocument.Parse(updatedDbResponse); - Assert.AreEqual(updatedResult.RootElement.GetProperty("maxId").GetInt64(), 19); + Assert.AreEqual(updatedResult.RootElement.GetProperty("maxId").GetInt64(), expectedFinalMaxId); } public async Task InsertMutationOnTableWithTriggerWithNonAutoGenPK(string dbQuery) diff --git a/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs b/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs index 78337d601f..2de0c0fa39 100644 --- a/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs @@ -259,7 +259,7 @@ FROM [books] AS [table0] WITHOUT_ARRAY_WRAPPER "; - await TestStoredProcedureMutationForDeletion(dbQueryToVerifyDeletion); + await TestStoredProcedureMutationForDeletion(dbQueryToVerifyDeletion, 21, 20); } /// diff --git a/src/Service.Tests/SqlTests/RestApiTests/Delete/MsSqlDeleteApiTest.cs b/src/Service.Tests/SqlTests/RestApiTests/Delete/MsSqlDeleteApiTest.cs index 13f4d31cf2..cf8a1f6fc5 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Delete/MsSqlDeleteApiTest.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Delete/MsSqlDeleteApiTest.cs @@ -29,7 +29,7 @@ public class MsSqlDeleteApiTests : DeleteApiTestBase // This query is used to confirm that the item no longer exists, not the // actual delete query. $"SELECT [id] FROM { _integrationTableName } " + - $"WHERE id = 20 FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" + $"WHERE id = 21 FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" } }; #region Test Fixture Setup From 237c16fb4d49331555dbc387de0684e74f1c98f3 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Tue, 28 Oct 2025 14:58:26 -0700 Subject: [PATCH 13/17] fixes testing --- src/Service.Tests/DatabaseSchema-DwSql.sql | 3 ++- src/Service.Tests/DatabaseSchema-MySql.sql | 3 ++- src/Service.Tests/DatabaseSchema-PostgreSql.sql | 3 ++- .../GraphQLMutationTests/GraphQLMutationTestBase.cs | 6 +++--- .../GraphQLMutationTests/MsSqlGraphQLMutationTests.cs | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Service.Tests/DatabaseSchema-DwSql.sql b/src/Service.Tests/DatabaseSchema-DwSql.sql index 300ef7ff32..daed665949 100644 --- a/src/Service.Tests/DatabaseSchema-DwSql.sql +++ b/src/Service.Tests/DatabaseSchema-DwSql.sql @@ -336,7 +336,8 @@ VALUES (1, 'Awesome book', 1234), (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\YOU', 1234), -(20, 'C:\\LIFE', 1234); +(20, 'C:\\LIFE', 1234), +(21, '', 1234); INSERT INTO book_website_placements(id, book_id, price) VALUES (1, 1, 100), (2, 2, 50), (3, 3, 23), (4, 5, 33); diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index f746bc063a..57d5705dfc 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -388,7 +388,8 @@ INSERT INTO books(id, title, publisher_id) (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\\YOU', 1234), - (20, 'C:\\\\LIFE', 1234); + (20, 'C:\\LIFE', 1234), + (21, '', 1234); INSERT INTO book_website_placements(book_id, price) VALUES (1, 100), (2, 50), (3, 23), (5, 33); INSERT INTO website_users(id, username) VALUES (1, 'George'), (2, NULL), (3, ''), (4, 'book_lover_95'), (5, 'null'); INSERT INTO book_author_link(book_id, author_id) VALUES (1, 123), (2, 124), (3, 123), (3, 124), (4, 123), (4, 124), (5, 126); diff --git a/src/Service.Tests/DatabaseSchema-PostgreSql.sql b/src/Service.Tests/DatabaseSchema-PostgreSql.sql index 14615707b1..523e96c22f 100644 --- a/src/Service.Tests/DatabaseSchema-PostgreSql.sql +++ b/src/Service.Tests/DatabaseSchema-PostgreSql.sql @@ -391,7 +391,8 @@ INSERT INTO books(id, title, publisher_id) (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\YOU', 1234), - (20, 'C:\\LIFE', 1234); + (20, 'C:\\LIFE', 1234), + (21, '', 1234); INSERT INTO book_website_placements(book_id, price) VALUES (1, 100), (2, 50), (3, 23), (5, 33); INSERT INTO website_users(id, username) VALUES (1, 'George'), (2, NULL), (3, ''), (4, 'book_lover_95'), (5, 'null'); INSERT INTO book_author_link(book_id, author_id) VALUES (1, 123), (2, 124), (3, 123), (3, 124), (4, 123), (4, 124), (5, 126);; diff --git a/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs index 2aa04cb606..745d5eade3 100644 --- a/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLMutationTests/GraphQLMutationTestBase.cs @@ -244,7 +244,7 @@ public async Task TestStoredProcedureMutationForInsertion(string dbQuery) /// Check: If the intended book is deleted from the DB and /// verifies the response. /// - public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyDeletion, int expectedOriginalMaxId, int expectedFinalMaxId) + public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyDeletion) { string graphQLMutationName = "executeDeleteLastInsertedBook"; string graphQLMutation = @" @@ -257,7 +257,7 @@ public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyD string currentDbResponse = await GetDatabaseResultAsync(dbQueryToVerifyDeletion); JsonDocument currentResult = JsonDocument.Parse(currentDbResponse); - Assert.AreEqual(currentResult.RootElement.GetProperty("maxId").GetInt64(), expectedOriginalMaxId); + Assert.AreEqual(currentResult.RootElement.GetProperty("maxId").GetInt64(), 21); JsonElement graphQLResponse = await ExecuteGraphQLRequestAsync(graphQLMutation, graphQLMutationName, isAuthenticated: true); // Stored Procedure didn't return anything @@ -266,7 +266,7 @@ public async Task TestStoredProcedureMutationForDeletion(string dbQueryToVerifyD // check to verify new element is inserted string updatedDbResponse = await GetDatabaseResultAsync(dbQueryToVerifyDeletion); JsonDocument updatedResult = JsonDocument.Parse(updatedDbResponse); - Assert.AreEqual(updatedResult.RootElement.GetProperty("maxId").GetInt64(), expectedFinalMaxId); + Assert.AreEqual(updatedResult.RootElement.GetProperty("maxId").GetInt64(), 20); } public async Task InsertMutationOnTableWithTriggerWithNonAutoGenPK(string dbQuery) diff --git a/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs b/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs index 2de0c0fa39..78337d601f 100644 --- a/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLMutationTests/MsSqlGraphQLMutationTests.cs @@ -259,7 +259,7 @@ FROM [books] AS [table0] WITHOUT_ARRAY_WRAPPER "; - await TestStoredProcedureMutationForDeletion(dbQueryToVerifyDeletion, 21, 20); + await TestStoredProcedureMutationForDeletion(dbQueryToVerifyDeletion); } /// From 9ddf1407b64e4ac7b484f812f142ef0a02fac078 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Tue, 28 Oct 2025 15:14:48 -0700 Subject: [PATCH 14/17] fix pagination tests --- .../GraphQLPaginationTestBase.cs | 273 ++++++------------ 1 file changed, 90 insertions(+), 183 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index c29c9b90fb..abf25d2a8c 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -296,189 +296,96 @@ public async Task RequestNoParamFullConnection() }"; JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); - string expected; - if (_sqlMetadataProvider.GetDatabaseType() == DatabaseType.MSSQL) - { - expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - }, - { - ""id"": 21, - ""title"": """" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; - } - else - { - expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; - } + string expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } From ec2c5a06621098d740132d7937d2717504c0a7ba Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Tue, 28 Oct 2025 15:39:13 -0700 Subject: [PATCH 15/17] fixes pagination tests --- .../GraphQLPaginationTestBase.cs | 274 ++++++------------ .../RestApiTests/Delete/DwSqlDeleteApiTest.cs | 2 +- 2 files changed, 92 insertions(+), 184 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index abf25d2a8c..780dfe9c38 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -85,189 +85,96 @@ public async Task RequestMaxUsingNegativeOne() } }"; - string expected; - if (_sqlMetadataProvider.GetDatabaseType() == DatabaseType.MSSQL) - { - expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - }, - { - ""id"": 21, - ""title"": """" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; - } - else - { - expected = @"{ - ""items"": [ - { - ""id"": 1, - ""title"": ""Awesome book"" - }, - { - ""id"": 2, - ""title"": ""Also Awesome book"" - }, - { - ""id"": 3, - ""title"": ""Great wall of china explained"" - }, - { - ""id"": 4, - ""title"": ""US history in a nutshell"" - }, - { - ""id"": 5, - ""title"": ""Chernobyl Diaries"" - }, - { - ""id"": 6, - ""title"": ""The Palace Door"" - }, - { - ""id"": 7, - ""title"": ""The Groovy Bar"" - }, - { - ""id"": 8, - ""title"": ""Time to Eat"" - }, - { - ""id"": 9, - ""title"": ""Policy-Test-01"" - }, - { - ""id"": 10, - ""title"": ""Policy-Test-02"" - }, - { - ""id"": 11, - ""title"": ""Policy-Test-04"" - }, - { - ""id"": 12, - ""title"": ""Time to Eat 2"" - }, - { - ""id"": 13, - ""title"": ""Before Sunrise"" - }, - { - ""id"": 14, - ""title"": ""Before Sunset"" - }, - { - ""id"": 15, - ""title"": ""SQL_CONN"" - }, - { - ""id"": 16, - ""title"": ""SOME%CONN"" - }, - { - ""id"": 17, - ""title"": ""CONN%_CONN"" - }, - { - ""id"": 18, - ""title"": ""[Special Book]"" - }, - { - ""id"": 19, - ""title"": ""ME\\YOU"" - }, - { - ""id"": 20, - ""title"": ""C:\\\\LIFE"" - } - ], - ""endCursor"": null, - ""hasNextPage"": false - }"; - } + string expected = @"{ + ""items"": [ + { + ""id"": 1, + ""title"": ""Awesome book"" + }, + { + ""id"": 2, + ""title"": ""Also Awesome book"" + }, + { + ""id"": 3, + ""title"": ""Great wall of china explained"" + }, + { + ""id"": 4, + ""title"": ""US history in a nutshell"" + }, + { + ""id"": 5, + ""title"": ""Chernobyl Diaries"" + }, + { + ""id"": 6, + ""title"": ""The Palace Door"" + }, + { + ""id"": 7, + ""title"": ""The Groovy Bar"" + }, + { + ""id"": 8, + ""title"": ""Time to Eat"" + }, + { + ""id"": 9, + ""title"": ""Policy-Test-01"" + }, + { + ""id"": 10, + ""title"": ""Policy-Test-02"" + }, + { + ""id"": 11, + ""title"": ""Policy-Test-04"" + }, + { + ""id"": 12, + ""title"": ""Time to Eat 2"" + }, + { + ""id"": 13, + ""title"": ""Before Sunrise"" + }, + { + ""id"": 14, + ""title"": ""Before Sunset"" + }, + { + ""id"": 15, + ""title"": ""SQL_CONN"" + }, + { + ""id"": 16, + ""title"": ""SOME%CONN"" + }, + { + ""id"": 17, + ""title"": ""CONN%_CONN"" + }, + { + ""id"": 18, + ""title"": ""[Special Book]"" + }, + { + ""id"": 19, + ""title"": ""ME\\YOU"" + }, + { + ""id"": 20, + ""title"": ""C:\\\\LIFE"" + }, + { + ""id"": 21, + ""title"": """" + } + ], + ""endCursor"": null, + ""hasNextPage"": false + }"; // Note: The max page size is 21 for MsSql and 20 for all other data sources, so when using -1 // this resultset represents all books in the db. @@ -296,6 +203,7 @@ public async Task RequestNoParamFullConnection() }"; JsonElement actual = await ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false); + string expected = @"{ ""items"": [ { diff --git a/src/Service.Tests/SqlTests/RestApiTests/Delete/DwSqlDeleteApiTest.cs b/src/Service.Tests/SqlTests/RestApiTests/Delete/DwSqlDeleteApiTest.cs index c574db540f..984b252727 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Delete/DwSqlDeleteApiTest.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Delete/DwSqlDeleteApiTest.cs @@ -20,7 +20,7 @@ public class DwSqlDeleteApiTests : DeleteApiTestBase { "DeleteOneWithStoredProcedureTest", $"SELECT [id] FROM { _integrationTableName } " + - $"WHERE id = 20" + $"WHERE id = 21" } }; #region Test Fixture Setup From f82dc60a91da03c82a039975553f437e7c28c808 Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Tue, 28 Oct 2025 16:48:53 -0700 Subject: [PATCH 16/17] fixes mysql test setup --- src/Service.Tests/DatabaseSchema-MySql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index 57d5705dfc..dda93d86d1 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -388,7 +388,7 @@ INSERT INTO books(id, title, publisher_id) (17, 'CONN%_CONN', 1234), (18, '[Special Book]', 1234), (19, 'ME\\YOU', 1234), - (20, 'C:\\LIFE', 1234), + (20, 'C:\\\\LIFE', 1234), (21, '', 1234); INSERT INTO book_website_placements(book_id, price) VALUES (1, 100), (2, 50), (3, 23), (5, 33); INSERT INTO website_users(id, username) VALUES (1, 'George'), (2, NULL), (3, ''), (4, 'book_lover_95'), (5, 'null'); From ae05b1e96d4c0698f5a27a99875dbcd3e162017a Mon Sep 17 00:00:00 2001 From: Giovanna Ribeiro Date: Wed, 29 Oct 2025 10:11:49 -0700 Subject: [PATCH 17/17] fixes formatting --- .../SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs index 780dfe9c38..33db8f8b49 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/GraphQLPaginationTestBase.cs @@ -3,7 +3,6 @@ using System.Text.Json; using System.Threading.Tasks; -using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries;