From d5efe7e8bfb66bc8a54e5b022527ecd24f2fa8d6 Mon Sep 17 00:00:00 2001 From: aaron burtle Date: Mon, 15 Dec 2025 07:45:43 -0800 Subject: [PATCH 1/5] Hot Reload test fix for non GQL schema changes --- .../HotReload/ConfigurationHotReloadTests.cs | 215 ++++++++++++------ 1 file changed, 146 insertions(+), 69 deletions(-) diff --git a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs index d2ea7708ce..16dbf9ab57 100644 --- a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs +++ b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs @@ -28,6 +28,8 @@ public class ConfigurationHotReloadTests private static StringWriter _writer; private const string CONFIG_FILE_NAME = "hot-reload.dab-config.json"; private const string GQL_QUERY_NAME = "books"; + private const string HOT_RELOAD_SUCCESS_MESSAGE = "Validated hot-reloaded configuration file"; + private const string HOT_RELOAD_FAILURE_MESSAGE = "Unable to hot reload configuration file due to"; private const string GQL_QUERY = @"{ books(first: 100) { @@ -59,6 +61,7 @@ private static void GenerateConfigFile( string restEntityEnabled = "true", string entityBackingColumn = "title", string entityExposedName = "title", + string mcpEnabled = "true", string configFileName = CONFIG_FILE_NAME) { File.WriteAllText(configFileName, @" @@ -82,6 +85,9 @@ private static void GenerateConfigFile( ""path"": """ + gQLPath + @""", ""allow-introspection"": true }, + ""mcp"": { + ""enabled"": " + mcpEnabled + @" + }, ""host"": { ""cors"": { ""origins"": [ @@ -228,12 +234,14 @@ public static void ClassCleanup() /// Hot reload the configuration by saving a new file with different rest and graphQL paths. /// Validate that the response is correct when making a request with the newly hot-reloaded paths. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod("Hot-reload runtime paths.")] public async Task HotReloadConfigRuntimePathsEndToEndTest() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string restBookContents = $"{{\"value\":{_bookDBOContents}}}"; string restPath = "restApi"; string gQLPath = "/gQLApi"; @@ -250,7 +258,12 @@ public async Task HotReloadConfigRuntimePathsEndToEndTest() connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restPath: restPath, gQLPath: gQLPath); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act HttpResponseMessage badPathRestResult = await _testClient.GetAsync($"rest/Book"); @@ -278,18 +291,25 @@ public async Task HotReloadConfigRuntimePathsEndToEndTest() /// set to false. Validate that the response from the server is NOT FOUND when making a request after /// the hot reload. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod("Hot-reload rest enabled.")] public async Task HotReloadConfigRuntimeRestEnabledEndToEndTest() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string restEnabled = "false"; GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restEnabled: restEnabled); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act HttpResponseMessage restResult = await _testClient.GetAsync($"rest/Book"); @@ -303,12 +323,14 @@ public async Task HotReloadConfigRuntimeRestEnabledEndToEndTest() /// set to false. Validate that the response from the server is NOT FOUND when making a request after /// the hot reload. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod("Hot-reload gql enabled.")] public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string gQLEnabled = "false"; string query = GQL_QUERY; object payload = @@ -318,10 +340,16 @@ public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() { Content = JsonContent.Create(payload) }; + GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", gQLEnabled: gQLEnabled); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act HttpResponseMessage gQLResult = await _testClient.SendAsync(request); @@ -337,10 +365,13 @@ public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() /// [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod("Hot-reload gql disabled at entity level.")] - [Ignore] + [Ignore] // This test requires GraphQL schema reload public async Task HotReloadEntityGQLEnabledFlag() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string gQLEntityEnabled = "false"; string query = @"{ book_by_pk(id: 1) { @@ -359,7 +390,12 @@ public async Task HotReloadEntityGQLEnabledFlag() GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", gQLEntityEnabled: gQLEntityEnabled); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act HttpResponseMessage gQLResult = await _testClient.SendAsync(request); @@ -376,10 +412,13 @@ public async Task HotReloadEntityGQLEnabledFlag() /// [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - [Ignore] + [Ignore] // This test requires GraphQL schema reload public async Task HotReloadConfigAddEntity() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string newEntityName = "Author"; string newEntitySource = "authors"; string newEntityGQLSingular = "author"; @@ -391,7 +430,12 @@ public async Task HotReloadConfigAddEntity() sourceObject: newEntitySource, gQLEntitySingular: newEntityGQLSingular, gQLEntityPlural: newEntityGQLPlural); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act string queryWithOldEntity = @"{ @@ -453,17 +497,25 @@ public async Task HotReloadConfigAddEntity() /// results in bad request, while the new mappings results in a correct response as "title" field is no longer valid. [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - [Ignore] + [Ignore] // This test requires GraphQL schema reload public async Task HotReloadConfigUpdateMappings() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + string newMappingFieldName = "bookTitle"; // Update the configuration with new mappings GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", entityBackingColumn: "title", entityExposedName: newMappingFieldName); - System.Threading.Thread.Sleep(2000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); // Act string queryWithOldMapping = @"{ @@ -524,12 +576,14 @@ public async Task HotReloadConfigUpdateMappings() /// By asserting that hot reload worked properly for the session-context it also implies that /// the new connection string with additional parameters is also valid. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] public async Task HotReloadConfigDataSource() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + RuntimeConfig previousRuntimeConfig = _configProvider.GetConfig(); MsSqlOptions previousSessionContext = previousRuntimeConfig.DataSource.GetTypedOptions(); @@ -540,7 +594,12 @@ public async Task HotReloadConfigDataSource() GenerateConfigFile( sessionContext: "false", connectionString: expectedConnectionString); - System.Threading.Thread.Sleep(3000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); RuntimeConfig updatedRuntimeConfig = _configProvider.GetConfig(); MsSqlOptions actualSessionContext = updatedRuntimeConfig.DataSource.GetTypedOptions(); @@ -561,27 +620,34 @@ public async Task HotReloadConfigDataSource() /// Then we assert that the log-level property is properly updated by ensuring it is /// not the same as the previous log-level and asserting it is the expected log-level. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - public void HotReloadLogLevel() + public async Task HotReloadLogLevel() { - // Arange + // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + LogLevel expectedLogLevel = LogLevel.Trace; string expectedFilter = "trace"; RuntimeConfig previousRuntimeConfig = _configProvider.GetConfig(); LogLevel previouslogLevel = previousRuntimeConfig.GetConfiguredLogLevel(); - //Act + // Act GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", logFilter: expectedFilter); - System.Threading.Thread.Sleep(3000); + + // Wait for hot-reload to complete successfully + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); RuntimeConfig updatedRuntimeConfig = _configProvider.GetConfig(); LogLevel actualLogLevel = updatedRuntimeConfig.GetConfiguredLogLevel(); - //Assert + // Assert Assert.AreNotEqual(previouslogLevel, actualLogLevel); Assert.AreEqual(expectedLogLevel, actualLogLevel); } @@ -591,7 +657,6 @@ public void HotReloadLogLevel() /// to an invalid connection string, then it hot reloads once more to the original /// connection string. Lastly, we assert that the first reload fails while the second one succeeds. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] public async Task HotReloadConfigConnectionString() @@ -600,15 +665,12 @@ public async Task HotReloadConfigConnectionString() _writer = new StringWriter(); Console.SetOut(_writer); - string failedKeyWord = "Unable to hot reload configuration file due to"; - string succeedKeyWord = "Validated hot-reloaded configuration file"; - // Act // Hot Reload should fail here GenerateConfigFile( connectionString: $"WrongConnectionString"); - await ConfigurationHotReloadTests.WaitForConditionAsync( - () => _writer.ToString().Contains(failedKeyWord), + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), TimeSpan.FromSeconds(12), TimeSpan.FromMilliseconds(500)); @@ -619,8 +681,8 @@ await ConfigurationHotReloadTests.WaitForConditionAsync( // Hot Reload should succeed here GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}"); - await ConfigurationHotReloadTests.WaitForConditionAsync( - () => _writer.ToString().Contains(succeedKeyWord), + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), TimeSpan.FromSeconds(12), TimeSpan.FromMilliseconds(500)); @@ -630,8 +692,8 @@ await ConfigurationHotReloadTests.WaitForConditionAsync( HttpResponseMessage restResult = await _testClient.GetAsync("/rest/Book"); // Assert - Assert.IsTrue(failedConfigLog.Contains(failedKeyWord)); - Assert.IsTrue(succeedConfigLog.Contains(succeedKeyWord)); + Assert.IsTrue(failedConfigLog.Contains(HOT_RELOAD_FAILURE_MESSAGE)); + Assert.IsTrue(succeedConfigLog.Contains(HOT_RELOAD_SUCCESS_MESSAGE)); Assert.AreEqual(HttpStatusCode.OK, restResult.StatusCode); } @@ -643,7 +705,6 @@ await ConfigurationHotReloadTests.WaitForConditionAsync( /// Then it hot reloads once more to the original database type. We assert that the /// first reload fails while the second one succeeds. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] public async Task HotReloadConfigDatabaseType() @@ -652,16 +713,13 @@ public async Task HotReloadConfigDatabaseType() _writer = new StringWriter(); Console.SetOut(_writer); - string failedKeyWord = "Unable to hot reload configuration file due to"; - string succeedKeyWord = "Validated hot-reloaded configuration file"; - // Act // Hot Reload should fail here GenerateConfigFile( databaseType: DatabaseType.PostgreSQL, connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.POSTGRESQL).Replace("\\", "\\\\")}"); - await ConfigurationHotReloadTests.WaitForConditionAsync( - () => _writer.ToString().Contains(failedKeyWord), + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), TimeSpan.FromSeconds(12), TimeSpan.FromMilliseconds(500)); @@ -673,8 +731,8 @@ await ConfigurationHotReloadTests.WaitForConditionAsync( GenerateConfigFile( databaseType: DatabaseType.MSSQL, connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}"); - await ConfigurationHotReloadTests.WaitForConditionAsync( - () => _writer.ToString().Contains(succeedKeyWord), + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), TimeSpan.FromSeconds(12), TimeSpan.FromMilliseconds(500)); @@ -684,56 +742,59 @@ await ConfigurationHotReloadTests.WaitForConditionAsync( HttpResponseMessage restResult = await _testClient.GetAsync("/rest/Book"); // Assert - Assert.IsTrue(failedConfigLog.Contains(failedKeyWord)); - Assert.IsTrue(succeedConfigLog.Contains(succeedKeyWord)); + Assert.IsTrue(failedConfigLog.Contains(HOT_RELOAD_FAILURE_MESSAGE)); + Assert.IsTrue(succeedConfigLog.Contains(HOT_RELOAD_SUCCESS_MESSAGE)); Assert.AreEqual(HttpStatusCode.OK, restResult.StatusCode); } /// - /// Creates a hot reload scenario in which the schema file is invalid which causes - /// hot reload to fail, then we check that the program is still able to work + /// Creates a hot reload scenario in which the configuration file has validation errors + /// which causes hot reload to fail, then we check that the program is still able to work /// properly by validating that the DAB engine is still using the same configuration file /// from before the hot reload. /// - /// Invalid change that was added is a schema file that is not complete, which should be - /// catched by the validator. + /// Invalid change: Setting both REST, GraphQL, and MCP to disabled, which is not allowed. /// - [Ignore] [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - public void HotReloadValidationFail() + public async Task HotReloadValidationFail() { // Arrange - string schemaName = "hot-reload.draft.schema.json"; - string schemaConfig = TestHelper.GenerateInvalidSchema(); - - if (File.Exists(schemaName)) - { - File.Delete(schemaName); - } + _writer = new StringWriter(); + Console.SetOut(_writer); - File.WriteAllText(schemaName, schemaConfig); RuntimeConfig lkgRuntimeConfig = _configProvider.GetConfig(); Assert.IsNotNull(lkgRuntimeConfig); + // Capture properties to verify config hasn't changed + bool originalRestEnabled = lkgRuntimeConfig.Runtime.Rest.Enabled; + bool originalGraphQLEnabled = lkgRuntimeConfig.Runtime.GraphQL.Enabled; + bool originalMcpEnabled = lkgRuntimeConfig.Runtime.Mcp.Enabled; + // Act - // Simulate an invalid change to the schema file while the config is updated to a valid state + // Generate a config that will fail validation by disabling REST, GraphQL, and MCP (which is not allowed) GenerateConfigFile( - schema: schemaName, connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restEnabled: "false", - gQLEnabled: "false"); - System.Threading.Thread.Sleep(10000); + gQLEnabled: "false", + mcpEnabled: "false"); + + // Wait for hot-reload to fail + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); RuntimeConfig newRuntimeConfig = _configProvider.GetConfig(); - // Assert - Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig); - - if (File.Exists(schemaName)) - { - File.Delete(schemaName); - } + // Assert - Verify the configuration hasn't changed by comparing properties + Assert.IsNotNull(newRuntimeConfig, "RuntimeConfig should not be null after failed hot-reload."); + Assert.AreEqual(originalRestEnabled, newRuntimeConfig.Runtime.Rest.Enabled, + "REST enabled setting should remain unchanged after hot-reload failure."); + Assert.AreEqual(originalGraphQLEnabled, newRuntimeConfig.Runtime.GraphQL.Enabled, + "GraphQL enabled setting should remain unchanged after hot-reload failure."); + Assert.AreEqual(originalMcpEnabled, newRuntimeConfig.Runtime.Mcp.Enabled, + "MCP enabled setting should remain unchanged after hot-reload failure."); } /// @@ -746,23 +807,39 @@ public void HotReloadValidationFail() /// [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - public void HotReloadParsingFail() + public async Task HotReloadParsingFail() { // Arrange + _writer = new StringWriter(); + Console.SetOut(_writer); + RuntimeConfig lkgRuntimeConfig = _configProvider.GetConfig(); Assert.IsNotNull(lkgRuntimeConfig); + // Capture properties to verify config hasn't changed + bool originalRestEnabled = lkgRuntimeConfig.Runtime.Rest.Enabled; + bool originalGraphQLEnabled = lkgRuntimeConfig.Runtime.GraphQL.Enabled; + // Act GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restEnabled: "invalid", gQLEnabled: "invalid"); - System.Threading.Thread.Sleep(5000); + + // Wait for hot-reload to fail (parsing error should trigger failure message) + await WaitForConditionAsync( + () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), + TimeSpan.FromSeconds(12), + TimeSpan.FromMilliseconds(500)); RuntimeConfig newRuntimeConfig = _configProvider.GetConfig(); - // Assert - Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig); + // Assert - Verify the configuration hasn't changed by comparing properties + Assert.IsNotNull(newRuntimeConfig, "RuntimeConfig should not be null after failed hot-reload."); + Assert.AreEqual(originalRestEnabled, newRuntimeConfig.Runtime.Rest.Enabled, + "REST enabled setting should remain unchanged after hot-reload failure."); + Assert.AreEqual(originalGraphQLEnabled, newRuntimeConfig.Runtime.GraphQL.Enabled, + "GraphQL enabled setting should remain unchanged after hot-reload failure."); } /// From f39705ccce28b51bfde8aaa42b476e6d1ef17499 Mon Sep 17 00:00:00 2001 From: aaron burtle Date: Tue, 16 Dec 2025 19:47:49 -0800 Subject: [PATCH 2/5] timeout to 30 seconds --- .../HotReload/ConfigurationHotReloadTests.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs index 16dbf9ab57..b8cd9198fe 100644 --- a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs +++ b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs @@ -30,6 +30,7 @@ public class ConfigurationHotReloadTests private const string GQL_QUERY_NAME = "books"; private const string HOT_RELOAD_SUCCESS_MESSAGE = "Validated hot-reloaded configuration file"; private const string HOT_RELOAD_FAILURE_MESSAGE = "Unable to hot reload configuration file due to"; + private const int HOT_RELOAD_TIMEOUT_SECONDS = 30; private const string GQL_QUERY = @"{ books(first: 100) { @@ -308,7 +309,7 @@ public async Task HotReloadConfigRuntimeRestEnabledEndToEndTest() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -348,7 +349,7 @@ public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -394,7 +395,7 @@ public async Task HotReloadEntityGQLEnabledFlag() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -434,7 +435,7 @@ public async Task HotReloadConfigAddEntity() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -514,7 +515,7 @@ public async Task HotReloadConfigUpdateMappings() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -598,7 +599,7 @@ public async Task HotReloadConfigDataSource() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); RuntimeConfig updatedRuntimeConfig = _configProvider.GetConfig(); @@ -641,7 +642,7 @@ public async Task HotReloadLogLevel() // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); RuntimeConfig updatedRuntimeConfig = _configProvider.GetConfig(); @@ -671,7 +672,7 @@ public async Task HotReloadConfigConnectionString() connectionString: $"WrongConnectionString"); await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Log that shows that hot-reload was not able to validate properly @@ -683,7 +684,7 @@ await WaitForConditionAsync( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}"); await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Log that shows that hot-reload validated properly @@ -720,7 +721,7 @@ public async Task HotReloadConfigDatabaseType() connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.POSTGRESQL).Replace("\\", "\\\\")}"); await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Log that shows that hot-reload was not able to validate properly @@ -733,7 +734,7 @@ await WaitForConditionAsync( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}"); await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Log that shows that hot-reload validated properly @@ -782,7 +783,7 @@ public async Task HotReloadValidationFail() // Wait for hot-reload to fail await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); RuntimeConfig newRuntimeConfig = _configProvider.GetConfig(); @@ -829,7 +830,7 @@ public async Task HotReloadParsingFail() // Wait for hot-reload to fail (parsing error should trigger failure message) await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); RuntimeConfig newRuntimeConfig = _configProvider.GetConfig(); From dc4ef4b2635d0f5795ecc3cd6df42d0e3a05daa3 Mon Sep 17 00:00:00 2001 From: aaron burtle Date: Tue, 16 Dec 2025 19:50:32 -0800 Subject: [PATCH 3/5] format --- .../HotReload/ConfigurationHotReloadTests.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs index b8cd9198fe..d49ecfc96b 100644 --- a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs +++ b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs @@ -259,11 +259,11 @@ public async Task HotReloadConfigRuntimePathsEndToEndTest() connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restPath: restPath, gQLPath: gQLPath); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), - TimeSpan.FromSeconds(12), + TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS), TimeSpan.FromMilliseconds(500)); // Act @@ -305,7 +305,7 @@ public async Task HotReloadConfigRuntimeRestEnabledEndToEndTest() GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restEnabled: restEnabled); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -345,7 +345,7 @@ public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", gQLEnabled: gQLEnabled); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -391,7 +391,7 @@ public async Task HotReloadEntityGQLEnabledFlag() GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", gQLEntityEnabled: gQLEntityEnabled); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -431,7 +431,7 @@ public async Task HotReloadConfigAddEntity() sourceObject: newEntitySource, gQLEntitySingular: newEntityGQLSingular, gQLEntityPlural: newEntityGQLPlural); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -511,7 +511,7 @@ public async Task HotReloadConfigUpdateMappings() connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", entityBackingColumn: "title", entityExposedName: newMappingFieldName); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -595,7 +595,7 @@ public async Task HotReloadConfigDataSource() GenerateConfigFile( sessionContext: "false", connectionString: expectedConnectionString); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -638,7 +638,7 @@ public async Task HotReloadLogLevel() GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", logFilter: expectedFilter); - + // Wait for hot-reload to complete successfully await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_SUCCESS_MESSAGE), @@ -779,7 +779,7 @@ public async Task HotReloadValidationFail() restEnabled: "false", gQLEnabled: "false", mcpEnabled: "false"); - + // Wait for hot-reload to fail await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), @@ -826,7 +826,7 @@ public async Task HotReloadParsingFail() connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", restEnabled: "invalid", gQLEnabled: "invalid"); - + // Wait for hot-reload to fail (parsing error should trigger failure message) await WaitForConditionAsync( () => _writer.ToString().Contains(HOT_RELOAD_FAILURE_MESSAGE), From e01885e9b16e5ac479c6b9724bcd2dd58e6865aa Mon Sep 17 00:00:00 2001 From: aaron burtle Date: Tue, 16 Dec 2025 20:00:13 -0800 Subject: [PATCH 4/5] format --- .../HotReload/ConfigurationHotReloadTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs index d49ecfc96b..c8ababa6c0 100644 --- a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs +++ b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs @@ -341,7 +341,7 @@ public async Task HotReloadConfigRuntimeGQLEnabledEndToEndTest() { Content = JsonContent.Create(payload) }; - + GenerateConfigFile( connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}", gQLEnabled: gQLEnabled); @@ -790,11 +790,11 @@ await WaitForConditionAsync( // Assert - Verify the configuration hasn't changed by comparing properties Assert.IsNotNull(newRuntimeConfig, "RuntimeConfig should not be null after failed hot-reload."); - Assert.AreEqual(originalRestEnabled, newRuntimeConfig.Runtime.Rest.Enabled, + Assert.AreEqual(originalRestEnabled, newRuntimeConfig.Runtime.Rest.Enabled, "REST enabled setting should remain unchanged after hot-reload failure."); - Assert.AreEqual(originalGraphQLEnabled, newRuntimeConfig.Runtime.GraphQL.Enabled, + Assert.AreEqual(originalGraphQLEnabled, newRuntimeConfig.Runtime.GraphQL.Enabled, "GraphQL enabled setting should remain unchanged after hot-reload failure."); - Assert.AreEqual(originalMcpEnabled, newRuntimeConfig.Runtime.Mcp.Enabled, + Assert.AreEqual(originalMcpEnabled, newRuntimeConfig.Runtime.Mcp.Enabled, "MCP enabled setting should remain unchanged after hot-reload failure."); } From a8011643a2e8ef58dcb191cc09ec60e459349b61 Mon Sep 17 00:00:00 2001 From: aaron burtle Date: Tue, 16 Dec 2025 20:43:31 -0800 Subject: [PATCH 5/5] add issue number to comment --- .../Configuration/HotReload/ConfigurationHotReloadTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs index c8ababa6c0..d6daeaac76 100644 --- a/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs +++ b/src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs @@ -366,7 +366,7 @@ await WaitForConditionAsync( /// [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod("Hot-reload gql disabled at entity level.")] - [Ignore] // This test requires GraphQL schema reload + [Ignore] // This test requires GraphQL schema reload. See: issue #3019 public async Task HotReloadEntityGQLEnabledFlag() { // Arrange @@ -413,7 +413,7 @@ await WaitForConditionAsync( /// [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - [Ignore] // This test requires GraphQL schema reload + [Ignore] // This test requires GraphQL schema reload. See: issue #3019 public async Task HotReloadConfigAddEntity() { // Arrange @@ -498,7 +498,7 @@ await WaitForConditionAsync( /// results in bad request, while the new mappings results in a correct response as "title" field is no longer valid. [TestCategory(MSSQL_ENVIRONMENT)] [TestMethod] - [Ignore] // This test requires GraphQL schema reload + [Ignore] // This test requires GraphQL schema reload. See: issue #3019 public async Task HotReloadConfigUpdateMappings() { // Arrange