Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot Reload Validation Test Move & Refactor #2489

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 0 additions & 166 deletions src/Service.Tests/Configuration/ConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4149,125 +4149,6 @@ public async Task TestNoDepthLimitOnGrahQLInNonHostedMode(int? depthLimit)
}
}

/// <summary>
/// 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
/// properly by validating that the DAB engine is still using the same configuration file
/// from before the hot reload.
/// </summary>
[TestMethod]
[TestCategory(TestCategory.MSSQL)]
public void HotReloadValidationFail()
{
// Arrange
string schemaName = "testSchema.json";
string configName = "hotreload-config.json";
if (File.Exists(configName))
{
File.Delete(configName);
}

if (File.Exists(schemaName))
{
File.Delete(schemaName);
}

bool initialRestEnabled = true;
bool updatedRestEnabled = false;

bool initialGQLEnabled = true;
bool updatedGQLEnabled = false;

DataSource dataSource = new(DatabaseType.MSSQL,
ConfigurationTests.GetConnectionStringFromEnvironmentConfig(environment: TestCategory.MSSQL), Options: null);

RuntimeConfig initialConfig = InitRuntimeConfigForHotReload(schemaName, dataSource, initialRestEnabled, initialGQLEnabled);

RuntimeConfig updatedConfig = InitRuntimeConfigForHotReload(schemaName, dataSource, updatedRestEnabled, updatedGQLEnabled);

string schemaConfig = TestHelper.GenerateInvalidSchema();

// Not using mocked filesystem so we pick up real file changes for hot reload
FileSystem fileSystem = new();
RuntimeConfigProvider configProvider = GenerateConfigFileAndConfigProvider(fileSystem, configName, initialConfig);
RuntimeConfig lkgRuntimeConfig = configProvider.GetConfig();

Assert.IsNotNull(lkgRuntimeConfig);

// Act
// Simulate an invalid change to the schema file while the config is updated to a valid state
fileSystem.File.WriteAllText(schemaName, schemaConfig);
fileSystem.File.WriteAllText(configName, updatedConfig.ToJson());

// Give ConfigFileWatcher enough time to hot reload the change
System.Threading.Thread.Sleep(6000);

RuntimeConfig newRuntimeConfig = configProvider.GetConfig();
Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig);

if (File.Exists(configName))
{
File.Delete(configName);
}

if (File.Exists(schemaName))
{
File.Delete(schemaName);
}
}

/// <summary>
/// Creates a hot reload scenario in which the updated configuration file is invalid causing
/// hot reload to fail as the schema can't be used by DAB, then we check that the
/// program is still able to work properly by showing us that it is still using the
/// same configuration file from before the hot reload.
/// </summary>
[TestMethod]
[TestCategory(TestCategory.MSSQL)]
public void HotReloadParsingFail()
{
// Arrange
string schemaName = "dab.draft.schema.json";
string configName = "hotreload-config.json";
if (File.Exists(configName))
{
File.Delete(configName);
}

bool initialRestEnabled = true;

bool initialGQLEnabled = true;

DataSource dataSource = new(DatabaseType.MSSQL,
ConfigurationTests.GetConnectionStringFromEnvironmentConfig(environment: TestCategory.MSSQL), Options: null);

RuntimeConfig initialConfig = InitRuntimeConfigForHotReload(schemaName, dataSource, initialRestEnabled, initialGQLEnabled);

string updatedConfig = TestHelper.GenerateInvalidRuntimeSection();

// Not using mocked filesystem so we pick up real file changes for hot reload
FileSystem fileSystem = new();
RuntimeConfigProvider configProvider = GenerateConfigFileAndConfigProvider(fileSystem, configName, initialConfig);
RuntimeConfig lkgRuntimeConfig = configProvider.GetConfig();

Assert.IsNotNull(lkgRuntimeConfig);

// Act
// Simulate an invalid change to the config file
fileSystem.File.WriteAllText(configName, updatedConfig);

// Give ConfigFileWatcher enough time to hot reload the change
System.Threading.Thread.Sleep(1000);

RuntimeConfig newRuntimeConfig = configProvider.GetConfig();
Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig);

if (File.Exists(configName))
{
File.Delete(configName);
}
}

/// <summary>
/// Helper function to write custom configuration file. with minimal REST/GraphQL global settings
/// using the supplied entities.
Expand Down Expand Up @@ -4718,53 +4599,6 @@ public static RuntimeConfig InitMinimalRuntimeConfig(
);
}

/// <summary>
/// Helper function that initializes a RuntimeConfig with the following
/// variables in order to prepare a file for hot reload
/// </summary>
/// <param name="schema"></param>
/// <param name="dataSource"></param>
/// <param name="restEnabled"></param>
/// <param name="graphQLEnabled"></param>
/// <returns></returns>
public static RuntimeConfig InitRuntimeConfigForHotReload(
string schema,
DataSource dataSource,
bool restEnabled,
bool graphQLEnabled)
{
RuntimeConfig runtimeConfig = new(
Schema: schema,
DataSource: dataSource,
Runtime: new(
Rest: new(restEnabled),
GraphQL: new(graphQLEnabled),
Host: new(null, null, HostMode.Development)
),
Entities: new(new Dictionary<string, Entity>())
);

return runtimeConfig;
}

/// <summary>
/// Helper function that generates a config file that is going to be observed
/// for hot reload and initialize a ConfigProvider which will be used to check
/// if the hot reload was validated or not.
/// </summary>
/// <param name="fileSystem"></param>
/// <param name="configName"></param>
/// <param name="runtimeConfig"></param>
/// <returns></returns>
public static RuntimeConfigProvider GenerateConfigFileAndConfigProvider(FileSystem fileSystem, string configName, RuntimeConfig runtimeConfig)
{
fileSystem.File.WriteAllText(configName, runtimeConfig.ToJson());
FileSystemRuntimeConfigLoader configLoader = new(fileSystem, handler: null, configName, string.Empty);
RuntimeConfigProvider configProvider = new(configLoader);

return configProvider;
}

/// <summary>
/// Gets PermissionSetting object allowed to perform all actions.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class ConfigurationHotReloadTests
internal static string _bookDBOContents;

private static void GenerateConfigFile(
string schema = "",
DatabaseType databaseType = DatabaseType.MSSQL,
string sessionContext = "true",
string connectionString = "",
Expand All @@ -60,7 +61,7 @@ private static void GenerateConfigFile(
{
File.WriteAllText(configFileName, @"
{
""$schema"": """",
""$schema"": """ + schema + @""",
""data-source"": {
""database-type"": """ + databaseType + @""",
""options"": {
Expand Down Expand Up @@ -443,7 +444,77 @@ await ConfigurationHotReloadTests.WaitForConditionAsync(
}

/// <summary>
///
/// 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
/// properly by validating that the DAB engine is still using the same configuration file
/// from before the hot reload.
/// </summary>
[TestCategory(MSSQL_ENVIRONMENT)]
RubenCerna2079 marked this conversation as resolved.
Show resolved Hide resolved
[TestMethod]
public void HotReloadValidationFail()
{
// Arrange
string schemaName = "hot-reload.draft.schema.json";
string schemaConfig = TestHelper.GenerateInvalidSchema();

if (File.Exists(schemaName))
{
File.Delete(schemaName);
}

File.WriteAllText(schemaName, schemaConfig);
RuntimeConfig lkgRuntimeConfig = _configProvider.GetConfig();
Assert.IsNotNull(lkgRuntimeConfig);

// Act
// Simulate an invalid change to the schema file while the config is updated to a valid state
GenerateConfigFile(
schema: schemaName,
connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}",
restEnabled: "false",
gQLEnabled: "false");
System.Threading.Thread.Sleep(10000);

RuntimeConfig newRuntimeConfig = _configProvider.GetConfig();

// Assert
Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig);

if (File.Exists(schemaName))
{
File.Delete(schemaName);
}
}

/// <summary>
/// Creates a hot reload scenario in which the updated configuration file is invalid causing
/// hot reload to fail, then we check that the program is still able to work properly by
/// showing us that it is still using the same configuration file from before the hot reload.
/// </summary>
[TestCategory(MSSQL_ENVIRONMENT)]
[TestMethod]
public void HotReloadParsingFail()
{
// Arrange
RuntimeConfig lkgRuntimeConfig = _configProvider.GetConfig();
Assert.IsNotNull(lkgRuntimeConfig);

// Act
GenerateConfigFile(
connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}",
restEnabled: "invalid",
gQLEnabled: "invalid");
System.Threading.Thread.Sleep(5000);

RuntimeConfig newRuntimeConfig = _configProvider.GetConfig();

// Assert
Assert.AreEqual(expected: lkgRuntimeConfig, actual: newRuntimeConfig);
}

/// <summary>
/// Helper function that waits and checks multiple times if the condition is completed before the time interval,
/// if at any point to condition is completed then the program will continue with no delays, else it will fail.
/// </summary>
private static async Task WaitForConditionAsync(Func<bool> condition, TimeSpan timeout, TimeSpan pollingInterval)
{
Expand Down
24 changes: 0 additions & 24 deletions src/Service.Tests/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,6 @@ public static RuntimeConfig AddMissingEntitiesToConfig(RuntimeConfig config, str
return config with { Entities = new(entities) };
}

/// <summary>
/// This function generates an invalid config file that is used by the tests
/// to ensure that parsing of the config file fails during a hot reload event
/// </summary>
/// <returns></returns>
public static string GenerateInvalidRuntimeSection()
{
string runtimeString = @"
{
""$schema"": ""https://github.com/Azure/data-api-builder/releases/download/vmajor.minor.patch/dab.draft.schema.json"",
""data-source"": {
""database-type"": ""mssql"",
""connection-string"": ""Server=test;Database=test;User ID=test;"",
""options"": {
""set-session-context"": true
}
},
""runtime"": {
""rest"": {
""enabled"":
}";
return runtimeString;
}

/// <summary>
/// This function generates an invalid schema file that is used by the tests
/// to ensure that validation of the config file fails during a hot reload event
Expand Down