From 0dd593fdc9ffa53a925e993b64455a16715f7e2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:57:07 +0000 Subject: [PATCH 01/10] Initial plan From c5db24fb2221b8518dfa0a451bc04615ac59a3f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:02:17 +0000 Subject: [PATCH 02/10] Exclude stored procedures from health checks and add test Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 86 +++++++++++++++++++ src/Service/HealthCheck/HealthCheckHelper.cs | 3 +- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index 1eac7416e3..7075d03e7b 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -564,6 +564,92 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti return runtimeConfig; } + /// + /// Validates that stored procedures are excluded from health checks. + /// Stored procedures should not be included because they require parameters + /// and are not guaranteed to be deterministic. + /// + [TestMethod] + [TestCategory(TestCategory.MSSQL)] + public async Task ComprehensiveHealthEndpoint_ExcludesStoredProcedures() + { + // Create a table entity (should be included in health checks) + Entity tableEntity = new( + Health: new(enabled: true), + Source: new("books", EntitySourceType.Table, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("book", "bookLists", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + + // Create a stored procedure entity (should be excluded from health checks) + Entity storedProcEntity = new( + Health: new(enabled: true), + Source: new("GetSeriesActors", EntitySourceType.StoredProcedure, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("getSeriesActors", "getSeriesActorsList", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + + Dictionary entityMap = new() + { + { "Book", tableEntity }, + { "GetSeriesActors", storedProcEntity } + }; + + RuntimeConfig runtimeConfig = CreateRuntimeConfig(entityMap, enableGlobalRest: true, enableGlobalGraphql: true, enabledGlobalMcp: true, enableGlobalHealth: true, enableDatasourceHealth: true, hostMode: HostMode.Development); + WriteToCustomConfigFile(runtimeConfig); + + string[] args = new[] + { + $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" + }; + + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + HttpRequestMessage healthRequest = new(HttpMethod.Get, $"{BASE_DAB_URL}/health"); + HttpResponseMessage response = await client.SendAsync(healthRequest); + + Assert.AreEqual(expected: HttpStatusCode.OK, actual: response.StatusCode, message: "Received unexpected HTTP code from health check endpoint."); + + string responseBody = await response.Content.ReadAsStringAsync(); + Dictionary responseProperties = JsonSerializer.Deserialize>(responseBody); + + // Validate checks array exists + Assert.IsTrue(responseProperties.TryGetValue("checks", out JsonElement checksElement), "Expected 'checks' property in response."); + Assert.AreEqual(JsonValueKind.Array, checksElement.ValueKind, "Expected 'checks' to be an array."); + + // Verify that 'Book' entity is present in the health checks (table entities should be included) + bool hasBookEntity = checksElement.EnumerateArray().Any(check => + { + if (check.TryGetProperty("name", out JsonElement nameElement)) + { + return nameElement.GetString() == "Book"; + } + + return false; + }); + Assert.IsTrue(hasBookEntity, "Expected 'Book' table entity to be included in health checks."); + + // Verify that 'GetSeriesActors' stored procedure is NOT present in the health checks + bool hasStoredProcEntity = checksElement.EnumerateArray().Any(check => + { + if (check.TryGetProperty("name", out JsonElement nameElement)) + { + return nameElement.GetString() == "GetSeriesActors"; + } + + return false; + }); + Assert.IsFalse(hasStoredProcEntity, "Expected 'GetSeriesActors' stored procedure entity to be excluded from health checks."); + } + } + private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) { File.WriteAllText( diff --git a/src/Service/HealthCheck/HealthCheckHelper.cs b/src/Service/HealthCheck/HealthCheckHelper.cs index 9b7bbd272c..ab19756195 100644 --- a/src/Service/HealthCheck/HealthCheckHelper.cs +++ b/src/Service/HealthCheck/HealthCheckHelper.cs @@ -199,10 +199,11 @@ private async Task UpdateDataSourceHealthCheckResultsAsync(ComprehensiveHealthCh // Updates the Entity Health Check Results in the response. // Goes through the entities one by one and executes the rest and graphql checks (if enabled). + // Stored procedures are excluded from health checks because they require parameters and are not guaranteed to be deterministic. private async Task UpdateEntityHealthCheckResultsAsync(ComprehensiveHealthCheckReport report, RuntimeConfig runtimeConfig) { List> enabledEntities = runtimeConfig.Entities.Entities - .Where(e => e.Value.IsEntityHealthEnabled) + .Where(e => e.Value.IsEntityHealthEnabled && e.Value.Source.Type != EntitySourceType.StoredProcedure) .ToList(); if (enabledEntities.Count == 0) From 34815e4791bde2ffd465424ce0903a3644aa0b76 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:05:59 +0000 Subject: [PATCH 03/10] Update test to not require database connection Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 91 +++++++++---------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index 7075d03e7b..f947942606 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -565,13 +565,12 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti } /// - /// Validates that stored procedures are excluded from health checks. - /// Stored procedures should not be included because they require parameters - /// and are not guaranteed to be deterministic. + /// Validates that stored procedure entities can be identified and filtered correctly. + /// This unit test validates the logic that stored procedures should be excluded from + /// health checks because they require parameters and are not guaranteed to be deterministic. /// [TestMethod] - [TestCategory(TestCategory.MSSQL)] - public async Task ComprehensiveHealthEndpoint_ExcludesStoredProcedures() + public void StoredProcedureEntities_ShouldBeFilteredFromHealthChecks() { // Create a table entity (should be included in health checks) Entity tableEntity = new( @@ -584,6 +583,17 @@ public async Task ComprehensiveHealthEndpoint_ExcludesStoredProcedures() Relationships: null, Mappings: null); + // Create a view entity (should be included in health checks) + Entity viewEntity = new( + Health: new(enabled: true), + Source: new("book_views", EntitySourceType.View, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("bookView", "bookViews", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + // Create a stored procedure entity (should be excluded from health checks) Entity storedProcEntity = new( Health: new(enabled: true), @@ -595,59 +605,44 @@ public async Task ComprehensiveHealthEndpoint_ExcludesStoredProcedures() Relationships: null, Mappings: null); + // Create entity with health disabled (should be excluded) + Entity tableEntityHealthDisabled = new( + Health: new(enabled: false), + Source: new("disabled_table", EntitySourceType.Table, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("disabledTable", "disabledTables", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + Dictionary entityMap = new() { { "Book", tableEntity }, - { "GetSeriesActors", storedProcEntity } + { "BookView", viewEntity }, + { "GetSeriesActors", storedProcEntity }, + { "DisabledTable", tableEntityHealthDisabled } }; - RuntimeConfig runtimeConfig = CreateRuntimeConfig(entityMap, enableGlobalRest: true, enableGlobalGraphql: true, enabledGlobalMcp: true, enableGlobalHealth: true, enableDatasourceHealth: true, hostMode: HostMode.Development); - WriteToCustomConfigFile(runtimeConfig); + // Apply the same filter logic used in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync + List> enabledEntities = entityMap + .Where(e => e.Value.IsEntityHealthEnabled && e.Value.Source.Type != EntitySourceType.StoredProcedure) + .ToList(); - string[] args = new[] - { - $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" - }; + // Verify the filter results + Assert.AreEqual(2, enabledEntities.Count, "Expected 2 entities to be included in health checks (table and view only)."); - using (TestServer server = new(Program.CreateWebHostBuilder(args))) - using (HttpClient client = server.CreateClient()) - { - HttpRequestMessage healthRequest = new(HttpMethod.Get, $"{BASE_DAB_URL}/health"); - HttpResponseMessage response = await client.SendAsync(healthRequest); - - Assert.AreEqual(expected: HttpStatusCode.OK, actual: response.StatusCode, message: "Received unexpected HTTP code from health check endpoint."); + // Verify that 'Book' table entity is included + Assert.IsTrue(enabledEntities.Any(e => e.Key == "Book"), "Expected 'Book' table entity to be included in health checks."); - string responseBody = await response.Content.ReadAsStringAsync(); - Dictionary responseProperties = JsonSerializer.Deserialize>(responseBody); + // Verify that 'BookView' view entity is included + Assert.IsTrue(enabledEntities.Any(e => e.Key == "BookView"), "Expected 'BookView' view entity to be included in health checks."); - // Validate checks array exists - Assert.IsTrue(responseProperties.TryGetValue("checks", out JsonElement checksElement), "Expected 'checks' property in response."); - Assert.AreEqual(JsonValueKind.Array, checksElement.ValueKind, "Expected 'checks' to be an array."); + // Verify that 'GetSeriesActors' stored procedure is NOT included + Assert.IsFalse(enabledEntities.Any(e => e.Key == "GetSeriesActors"), "Expected 'GetSeriesActors' stored procedure entity to be excluded from health checks."); - // Verify that 'Book' entity is present in the health checks (table entities should be included) - bool hasBookEntity = checksElement.EnumerateArray().Any(check => - { - if (check.TryGetProperty("name", out JsonElement nameElement)) - { - return nameElement.GetString() == "Book"; - } - - return false; - }); - Assert.IsTrue(hasBookEntity, "Expected 'Book' table entity to be included in health checks."); - - // Verify that 'GetSeriesActors' stored procedure is NOT present in the health checks - bool hasStoredProcEntity = checksElement.EnumerateArray().Any(check => - { - if (check.TryGetProperty("name", out JsonElement nameElement)) - { - return nameElement.GetString() == "GetSeriesActors"; - } - - return false; - }); - Assert.IsFalse(hasStoredProcEntity, "Expected 'GetSeriesActors' stored procedure entity to be excluded from health checks."); - } + // Verify that 'DisabledTable' is NOT included (health is disabled) + Assert.IsFalse(enabledEntities.Any(e => e.Key == "DisabledTable"), "Expected 'DisabledTable' entity to be excluded from health checks (health disabled)."); } private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) From e54683bf30274d21800f046f741a5f98ccab6792 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:07:33 +0000 Subject: [PATCH 04/10] Address code review feedback - simplify XML documentation Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> --- src/Service.Tests/Configuration/HealthEndpointTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index f947942606..a4aa80f8ea 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -565,9 +565,8 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti } /// - /// Validates that stored procedure entities can be identified and filtered correctly. - /// This unit test validates the logic that stored procedures should be excluded from - /// health checks because they require parameters and are not guaranteed to be deterministic. + /// Verifies that stored procedure entities are excluded from health checks + /// while table and view entities are included. /// [TestMethod] public void StoredProcedureEntities_ShouldBeFilteredFromHealthChecks() From c1c1e0d9063fab720d50543a00110a5453e058df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:30:46 +0000 Subject: [PATCH 05/10] Simplify unit test using DataRow for better readability Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 80 +++---------------- 1 file changed, 12 insertions(+), 68 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index a4aa80f8ea..cdbbb6ae38 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -565,83 +565,27 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti } /// - /// Verifies that stored procedure entities are excluded from health checks - /// while table and view entities are included. + /// Verifies health checks include only tables and views, not stored procedures. /// [TestMethod] - public void StoredProcedureEntities_ShouldBeFilteredFromHealthChecks() + [DataRow(EntitySourceType.Table, true, DisplayName = "Table entities are included in health checks")] + [DataRow(EntitySourceType.View, true, DisplayName = "View entities are included in health checks")] + [DataRow(EntitySourceType.StoredProcedure, false, DisplayName = "Stored procedures are excluded from health checks")] + public void HealthChecks_IncludeOnlyTablesAndViews(EntitySourceType sourceType, bool shouldBeIncluded) { - // Create a table entity (should be included in health checks) - Entity tableEntity = new( - Health: new(enabled: true), - Source: new("books", EntitySourceType.Table, null, null), - Fields: null, - Rest: new(Enabled: true), - GraphQL: new("book", "bookLists", true), - Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, - Relationships: null, - Mappings: null); - - // Create a view entity (should be included in health checks) - Entity viewEntity = new( - Health: new(enabled: true), - Source: new("book_views", EntitySourceType.View, null, null), - Fields: null, + Entity entity = new( + Source: new("test", sourceType, null, null), + GraphQL: new("test", "tests"), Rest: new(Enabled: true), - GraphQL: new("bookView", "bookViews", true), Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Mappings: null, Relationships: null, - Mappings: null); - - // Create a stored procedure entity (should be excluded from health checks) - Entity storedProcEntity = new( - Health: new(enabled: true), - Source: new("GetSeriesActors", EntitySourceType.StoredProcedure, null, null), - Fields: null, - Rest: new(Enabled: true), - GraphQL: new("getSeriesActors", "getSeriesActorsList", true), - Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, - Relationships: null, - Mappings: null); - - // Create entity with health disabled (should be excluded) - Entity tableEntityHealthDisabled = new( - Health: new(enabled: false), - Source: new("disabled_table", EntitySourceType.Table, null, null), - Fields: null, - Rest: new(Enabled: true), - GraphQL: new("disabledTable", "disabledTables", true), - Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, - Relationships: null, - Mappings: null); - - Dictionary entityMap = new() - { - { "Book", tableEntity }, - { "BookView", viewEntity }, - { "GetSeriesActors", storedProcEntity }, - { "DisabledTable", tableEntityHealthDisabled } - }; + Fields: null); // Apply the same filter logic used in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync - List> enabledEntities = entityMap - .Where(e => e.Value.IsEntityHealthEnabled && e.Value.Source.Type != EntitySourceType.StoredProcedure) - .ToList(); - - // Verify the filter results - Assert.AreEqual(2, enabledEntities.Count, "Expected 2 entities to be included in health checks (table and view only)."); - - // Verify that 'Book' table entity is included - Assert.IsTrue(enabledEntities.Any(e => e.Key == "Book"), "Expected 'Book' table entity to be included in health checks."); - - // Verify that 'BookView' view entity is included - Assert.IsTrue(enabledEntities.Any(e => e.Key == "BookView"), "Expected 'BookView' view entity to be included in health checks."); - - // Verify that 'GetSeriesActors' stored procedure is NOT included - Assert.IsFalse(enabledEntities.Any(e => e.Key == "GetSeriesActors"), "Expected 'GetSeriesActors' stored procedure entity to be excluded from health checks."); + bool isIncluded = entity.IsEntityHealthEnabled && entity.Source.Type != EntitySourceType.StoredProcedure; - // Verify that 'DisabledTable' is NOT included (health is disabled) - Assert.IsFalse(enabledEntities.Any(e => e.Key == "DisabledTable"), "Expected 'DisabledTable' entity to be excluded from health checks (health disabled)."); + Assert.AreEqual(shouldBeIncluded, isIncluded); } private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) From b7d59f6071cdd337c7316323c85156fdb0af7695 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:38:58 +0000 Subject: [PATCH 06/10] Simplify test to single assertion for stored procedure exclusion Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index cdbbb6ae38..f146ec5017 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -565,16 +565,13 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti } /// - /// Verifies health checks include only tables and views, not stored procedures. + /// Verifies stored procedures are excluded from health checks. /// [TestMethod] - [DataRow(EntitySourceType.Table, true, DisplayName = "Table entities are included in health checks")] - [DataRow(EntitySourceType.View, true, DisplayName = "View entities are included in health checks")] - [DataRow(EntitySourceType.StoredProcedure, false, DisplayName = "Stored procedures are excluded from health checks")] - public void HealthChecks_IncludeOnlyTablesAndViews(EntitySourceType sourceType, bool shouldBeIncluded) + public void HealthChecks_ExcludeStoredProcedures() { - Entity entity = new( - Source: new("test", sourceType, null, null), + Entity storedProcedure = new( + Source: new("test", EntitySourceType.StoredProcedure, null, null), GraphQL: new("test", "tests"), Rest: new(Enabled: true), Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, @@ -582,10 +579,10 @@ public void HealthChecks_IncludeOnlyTablesAndViews(EntitySourceType sourceType, Relationships: null, Fields: null); - // Apply the same filter logic used in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync - bool isIncluded = entity.IsEntityHealthEnabled && entity.Source.Type != EntitySourceType.StoredProcedure; - - Assert.AreEqual(shouldBeIncluded, isIncluded); + // Apply the filter logic used in HealthCheckHelper - stored procedures should be excluded + bool isIncludedInHealthCheck = storedProcedure.IsEntityHealthEnabled && storedProcedure.Source.Type != EntitySourceType.StoredProcedure; + + Assert.IsFalse(isIncludedInHealthCheck); } private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) From 2cd0254c63c6d2ae7ea233e755b1564644084cd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:49:27 +0000 Subject: [PATCH 07/10] Improve test to use RuntimeConfig with mixed entity types Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index f146ec5017..8f5d0c7b2e 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -565,24 +565,54 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti } /// - /// Verifies stored procedures are excluded from health checks. + /// Verifies that when filtering entities for health checks, + /// stored procedures are excluded while tables and views are included. + /// This matches the filter logic in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync. /// [TestMethod] public void HealthChecks_ExcludeStoredProcedures() { - Entity storedProcedure = new( - Source: new("test", EntitySourceType.StoredProcedure, null, null), - GraphQL: new("test", "tests"), + // Create a config with table, view, and stored procedure entities + RuntimeConfig runtimeConfig = SetupCustomConfigFile( + enableGlobalHealth: true, + enableGlobalRest: true, + enableGlobalGraphql: true, + enabledGlobalMcp: true, + enableDatasourceHealth: true, + enableEntityHealth: true, + enableEntityRest: true, + enableEntityGraphQL: true); + + // Add a stored procedure entity to the existing config + Entity storedProcEntity = new( + Source: new("GetData", EntitySourceType.StoredProcedure, null, null), + GraphQL: new("getData", "getDataList"), Rest: new(Enabled: true), Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, Mappings: null, Relationships: null, Fields: null); - // Apply the filter logic used in HealthCheckHelper - stored procedures should be excluded - bool isIncludedInHealthCheck = storedProcedure.IsEntityHealthEnabled && storedProcedure.Source.Type != EntitySourceType.StoredProcedure; - - Assert.IsFalse(isIncludedInHealthCheck); + Dictionary entitiesWithStoredProc = new(runtimeConfig.Entities.Entities) + { + { "GetData", storedProcEntity } + }; + + RuntimeConfig configWithStoredProc = runtimeConfig with + { + Entities = new RuntimeEntities(entitiesWithStoredProc) + }; + + // Apply the same filter used in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync + List> healthCheckEntities = configWithStoredProc.Entities.Entities + .Where(e => e.Value.IsEntityHealthEnabled && e.Value.Source.Type != EntitySourceType.StoredProcedure) + .ToList(); + + // Verify stored procedure is excluded and table is included + Assert.IsFalse(healthCheckEntities.Any(e => e.Value.Source.Type == EntitySourceType.StoredProcedure), + "Stored procedures should be excluded from health checks"); + Assert.IsTrue(healthCheckEntities.Any(e => e.Value.Source.Type == EntitySourceType.Table), + "Tables should be included in health checks"); } private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) From 07d643b759831fcb115e55bd979762a7238fca05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:30:52 +0000 Subject: [PATCH 08/10] Update test comment to accurately reflect tested entity types Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> --- src/Service.Tests/Configuration/HealthEndpointTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index 8f5d0c7b2e..f541d3b15b 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -566,13 +566,13 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti /// /// Verifies that when filtering entities for health checks, - /// stored procedures are excluded while tables and views are included. + /// stored procedures are excluded while tables are included. /// This matches the filter logic in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync. /// [TestMethod] public void HealthChecks_ExcludeStoredProcedures() { - // Create a config with table, view, and stored procedure entities + // Create a config with table and stored procedure entities RuntimeConfig runtimeConfig = SetupCustomConfigFile( enableGlobalHealth: true, enableGlobalRest: true, From 7ec7372097cfd2304685fbd5b1021f7e0814ba76 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 02:57:28 +0000 Subject: [PATCH 09/10] Remove unit test as requested - it duplicates service logic Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index f541d3b15b..1eac7416e3 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -564,57 +564,6 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti return runtimeConfig; } - /// - /// Verifies that when filtering entities for health checks, - /// stored procedures are excluded while tables are included. - /// This matches the filter logic in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync. - /// - [TestMethod] - public void HealthChecks_ExcludeStoredProcedures() - { - // Create a config with table and stored procedure entities - RuntimeConfig runtimeConfig = SetupCustomConfigFile( - enableGlobalHealth: true, - enableGlobalRest: true, - enableGlobalGraphql: true, - enabledGlobalMcp: true, - enableDatasourceHealth: true, - enableEntityHealth: true, - enableEntityRest: true, - enableEntityGraphQL: true); - - // Add a stored procedure entity to the existing config - Entity storedProcEntity = new( - Source: new("GetData", EntitySourceType.StoredProcedure, null, null), - GraphQL: new("getData", "getDataList"), - Rest: new(Enabled: true), - Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, - Mappings: null, - Relationships: null, - Fields: null); - - Dictionary entitiesWithStoredProc = new(runtimeConfig.Entities.Entities) - { - { "GetData", storedProcEntity } - }; - - RuntimeConfig configWithStoredProc = runtimeConfig with - { - Entities = new RuntimeEntities(entitiesWithStoredProc) - }; - - // Apply the same filter used in HealthCheckHelper.UpdateEntityHealthCheckResultsAsync - List> healthCheckEntities = configWithStoredProc.Entities.Entities - .Where(e => e.Value.IsEntityHealthEnabled && e.Value.Source.Type != EntitySourceType.StoredProcedure) - .ToList(); - - // Verify stored procedure is excluded and table is included - Assert.IsFalse(healthCheckEntities.Any(e => e.Value.Source.Type == EntitySourceType.StoredProcedure), - "Stored procedures should be excluded from health checks"); - Assert.IsTrue(healthCheckEntities.Any(e => e.Value.Source.Type == EntitySourceType.Table), - "Tables should be included in health checks"); - } - private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) { File.WriteAllText( From f71da2c7ed22d53484534997de09b5796fdb489e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:02:29 +0000 Subject: [PATCH 10/10] Add integration test to verify stored procedures are excluded from health checks Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> --- .../Configuration/HealthEndpointTests.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/Service.Tests/Configuration/HealthEndpointTests.cs b/src/Service.Tests/Configuration/HealthEndpointTests.cs index 1eac7416e3..2c86fa9622 100644 --- a/src/Service.Tests/Configuration/HealthEndpointTests.cs +++ b/src/Service.Tests/Configuration/HealthEndpointTests.cs @@ -564,6 +564,94 @@ private static RuntimeConfig CreateRuntimeConfig(Dictionary enti return runtimeConfig; } + /// + /// Verifies that stored procedures are excluded from health check results. + /// Creates a config with both a table entity and a stored procedure entity, + /// then validates that only the table entity appears in the health endpoint response. + /// + [TestMethod] + [TestCategory(TestCategory.MSSQL)] + public async Task HealthEndpoint_ExcludesStoredProcedures() + { + // Create a table entity + Entity tableEntity = new( + Health: new(enabled: true), + Source: new("books", EntitySourceType.Table, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("book", "bookLists", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + + // Create a stored procedure entity + Entity storedProcEntity = new( + Health: new(enabled: true), + Source: new("GetData", EntitySourceType.StoredProcedure, null, null), + Fields: null, + Rest: new(Enabled: true), + GraphQL: new("getData", "getDataList", true), + Permissions: new[] { ConfigurationTests.GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) }, + Relationships: null, + Mappings: null); + + Dictionary entityMap = new() + { + { "Book", tableEntity }, + { "GetData", storedProcEntity } + }; + + RuntimeConfig runtimeConfig = CreateRuntimeConfig( + entityMap, + enableGlobalRest: true, + enableGlobalGraphql: true, + enabledGlobalMcp: true, + enableGlobalHealth: true, + enableDatasourceHealth: true, + hostMode: HostMode.Development); + + WriteToCustomConfigFile(runtimeConfig); + + string[] args = new[] + { + $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}" + }; + + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + HttpRequestMessage healthRequest = new(HttpMethod.Get, $"{BASE_DAB_URL}/health"); + HttpResponseMessage response = await client.SendAsync(healthRequest); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Health endpoint should return OK"); + + string responseBody = await response.Content.ReadAsStringAsync(); + Dictionary responseProperties = JsonSerializer.Deserialize>(responseBody); + + // Get the checks array + Assert.IsTrue(responseProperties.TryGetValue("checks", out JsonElement checksElement), "Response should contain 'checks' property"); + Assert.AreEqual(JsonValueKind.Array, checksElement.ValueKind, "Checks should be an array"); + + // Get all entity names from the health check results + List entityNamesInHealthCheck = new(); + foreach (JsonElement check in checksElement.EnumerateArray()) + { + if (check.TryGetProperty("name", out JsonElement nameElement)) + { + entityNamesInHealthCheck.Add(nameElement.GetString()); + } + } + + // Verify that the table entity (Book) appears in health checks + Assert.IsTrue(entityNamesInHealthCheck.Contains("Book"), + "Table entity 'Book' should be included in health check results"); + + // Verify that the stored procedure entity (GetData) does NOT appear in health checks + Assert.IsFalse(entityNamesInHealthCheck.Contains("GetData"), + "Stored procedure entity 'GetData' should be excluded from health check results"); + } + } + private static void WriteToCustomConfigFile(RuntimeConfig runtimeConfig) { File.WriteAllText(