diff --git a/.devcontainer/setup-sqlserver.sh b/.devcontainer/setup-sqlserver.sh index 6b4ee480ed..fc962c3053 100755 --- a/.devcontainer/setup-sqlserver.sh +++ b/.devcontainer/setup-sqlserver.sh @@ -29,11 +29,11 @@ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" # Write test config file so the test suite can find the connection string. CONFIG_DIR="${REPO_ROOT}/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities" -CONFIG_DEFAULT_FILE="${CONFIG_DIR}/config.default.json" -CONFIG_FILE="${CONFIG_DIR}/config.json" +CONFIG_DEFAULT_FILE="${CONFIG_DIR}/config.default.jsonc" +CONFIG_FILE="${CONFIG_DIR}/config.jsonc" echo "Writing test config to ${CONFIG_FILE} (based on ${CONFIG_DEFAULT_FILE})..." TCP_CONN_STR="Data Source=tcp:${SQL_HOST},${SQL_PORT};Database=Northwind;User Id=sa;Password=${SA_PASSWORD};Encrypt=false;TrustServerCertificate=true" -# config.default.json contains JS-style comments (// ...) which are not valid JSON. +# config.default.jsonc contains JS-style comments (// ...) which are not valid JSON. # Strip single-line comments before feeding to jq. sed 's|//.*||' "${CONFIG_DEFAULT_FILE}" \ | jq --arg cs "${TCP_CONN_STR}" \ diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md index 20fe03b1e8..bd9db72c4a 100644 --- a/.github/instructions/testing.instructions.md +++ b/.github/instructions/testing.instructions.md @@ -14,8 +14,8 @@ src/Microsoft.Data.SqlClient/tests/ ├── UnitTests/ # Unit tests with minimal dependencies └── tools/ └── Microsoft.Data.SqlClient.TestUtilities/ - ├── config.default.json # Template configuration - └── config.json # Local test configuration (git-ignored) + ├── config.default.jsonc # Template configuration + └── config.jsonc # Local test configuration (git-ignored) ``` ## Test Categories @@ -34,14 +34,14 @@ src/Microsoft.Data.SqlClient/tests/ ### Manual Tests (`ManualTests/`) - Full integration tests with SQL Server -- Require `config.json` setup +- Require `config.jsonc` setup - Test real database operations - Include Always Encrypted, Entra ID tests ## Test Configuration -### Setting Up `config.json` -Copy `config.default.json` to `config.json` and configure: +### Setting Up `config.jsonc` +Copy `config.default.jsonc` to `config.jsonc` and configure: ```json { diff --git a/.gitignore b/.gitignore index 37b3509b0d..06d3a36078 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,7 @@ node_modules/ # Config Json file **/config.json +**/config.jsonc # Generated Milestone PR metadata files .milestone-prs/ diff --git a/TESTGUIDE.md b/TESTGUIDE.md index aab773a080..791d4a7bcc 100644 --- a/TESTGUIDE.md +++ b/TESTGUIDE.md @@ -195,22 +195,22 @@ conditional tests are skipped. ## Manual Test Configuration Edit the source configuration file at `src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/ -config.json`. The test utilities project copies that file to the test output directory, where the manual tests load it +config.jsonc`. The test utilities project copies that file to the test output directory, where the manual tests load it by default. The template file is: -[src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json](src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json) +[src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc](src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc) -`config.json` is git-ignored. If it does not exist, the test utilities project copies `config.default.json` to -`config.json` before compile. You can also create it manually: +`config.jsonc` is git-ignored. If it does not exist, the test utilities project copies `config.default.jsonc` to +`config.jsonc` before compile. You can also create it manually: ```bash -cp src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json \ - src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.json +cp src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc \ + src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.jsonc ``` -Update `config.json` for your environment before running manual tests. The most important values for a basic run are `TCPConnectionString` and `NPConnectionString`. +Update `config.jsonc` for your environment before running manual tests. The most important values for a basic run are `TCPConnectionString` and `NPConnectionString`. ```jsonc { @@ -230,16 +230,16 @@ For SQL Server in a Linux container, WSL, or another host where SQL authenticati } ``` -You can override the config file path with the `MDS_TEST_CONFIG` environment variable: +You can override the config file path with the `TEST_MDS_CONFIG` environment variable: ```bash -MDS_TEST_CONFIG=/path/to/config.json dotnet build -t:TestSqlClientManual -p:TestSet=2 +TEST_MDS_CONFIG=/path/to/config.jsonc dotnet build -t:TestSqlClientManual -p:TestSet=2 ``` On PowerShell: ```powershell -$env:MDS_TEST_CONFIG = "C:\path\to\config.json" +$env:TEST_MDS_CONFIG = "C:\path\to\config.jsonc" dotnet build -t:TestSqlClientManual -p:TestSet=2 ``` @@ -263,7 +263,6 @@ dotnet build -t:TestSqlClientManual -p:TestSet=2 | `SupportsIntegratedSecurity` | Whether the user running tests has integrated-security access to the target SQL Server. | `true` or `false`. | | `LocalDbAppName` | Optional LocalDB instance name. Empty disables LocalDB testing. | `MSSQLLocalDB` or another local instance. | | `LocalDbSharedInstanceName` | Optional shared LocalDB instance name. | Used only when testing shared LocalDB. | -| `SupportsFileStream` | Whether FileStream tests are supported by the target. | `true` or `false`. | | `FileStreamDirectory` | Directory used for FileStream database setup. | Use an escaped absolute path in JSON. | | `UseManagedSNIOnWindows` | Enables Managed SNI on Windows test coverage. | `true` or `false`. | | `DNSCachingConnString` | Optional connection string for DNS caching tests. | Used with DNS caching server settings. | @@ -271,7 +270,6 @@ dotnet build -t:TestSqlClientManual -p:TestSet=2 | `DNSCachingServerTR` | Optional DNS caching tenant-ring server. | Feature-specific tests only. | | `IsDNSCachingSupportedCR` | Enables DNS caching control-ring tests. | `true` or `false`. | | `IsDNSCachingSupportedTR` | Enables DNS caching tenant-ring tests. | `true` or `false`. | -| `IsAzureSynapse` | Marks the target as Azure Synapse. | Some SQL Server-specific tests are skipped when `true`. | | `EnclaveAzureDatabaseConnString` | Optional Azure SQL database connection string for enclave tests. | Feature-specific tests only. | | `ManagedIdentitySupported` | Whether managed identity tests should run. | Defaults to `true`. Set `false` if unavailable. | | `UserManagedIdentityClientId` | Optional client ID for user-assigned managed identity tests. | Feature-specific tests only. | diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 5eed840236..1b1ea7a2e7 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -234,7 +234,7 @@ jobs: referenceType: Project - ${{ if ne(parameters.configProperties, '{}') }}: - - template: /eng/pipelines/common/templates/steps/update-config-file-step.yml@self # update config.json file + - template: /eng/pipelines/common/templates/steps/update-config-file-step.yml@self # update config.jsonc file parameters: debug: ${{ parameters.debug }} saPassword: ${{ parameters.saPassword }} diff --git a/eng/pipelines/common/templates/steps/update-config-file-step.yml b/eng/pipelines/common/templates/steps/update-config-file-step.yml index b0612fa248..fd57828df0 100644 --- a/eng/pipelines/common/templates/steps/update-config-file-step.yml +++ b/eng/pipelines/common/templates/steps/update-config-file-step.yml @@ -143,9 +143,9 @@ steps: Write-Host "##vso[task.setvariable variable=Password;isSecret=true]$password" displayName: Set Connection String Password - # All properties should be added here, and this template should be used for any manipulation of the config.json file. + # All properties should be added here, and this template should be used for any manipulation of the config.jsonc file. - pwsh: | - $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + $jdata = Get-Content -Raw "config.default.jsonc" | ConvertFrom-Json foreach ($p in $jdata) { $p.TCPConnectionString="${{parameters.TCPConnectionString }}" @@ -197,13 +197,13 @@ steps: $p.EnclaveEnabled=[System.Convert]::ToBoolean("${{parameters.EnclaveEnabled }}") $p.WorkloadIdentityFederationServiceConnectionId="${{parameters.WorkloadIdentityFederationServiceConnectionId }}" } - $jdata | ConvertTo-Json | Set-Content "config.json" + $jdata | ConvertTo-Json | Set-Content "config.jsonc" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities - displayName: 'Update config.json' + displayName: 'Update config.jsonc' - ${{ if eq(parameters.debug, true) }}: - pwsh: | - $jdata = Get-Content -Raw "config.json" | ConvertFrom-Json + $jdata = Get-Content -Raw "config.jsonc" | ConvertFrom-Json foreach ($p in $jdata) { foreach ($prop in $p.PSObject.Properties) @@ -212,4 +212,4 @@ steps: } } workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities - displayName: '[Debug] Emit config.json' + displayName: '[Debug] Emit config.jsonc' diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml index f5f2269494..6c4399c9cb 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-core.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml @@ -371,7 +371,7 @@ stages: useManagedSNI: ${{parameters.useManagedSNI }} configSqlFor: local operatingSystem: Windows - # config.json properties + # config.jsonc properties configProperties: TCPConnectionString: $(SQL_TCP_CONN_STRING) NPConnectionString: $(SQL_NP_CONN_STRING) diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml index 42097daa42..eb9e36152a 100644 --- a/eng/pipelines/jobs/test-azure-package-ci-job.yml +++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml @@ -261,7 +261,7 @@ jobs: debug: ${{ parameters.debug }} saPassword: ${{ parameters.saPassword }} - # The config.json file has many options, but only some of them are + # The config.jsonc file has many options, but only some of them are # used by the Azure package tests. We only specify the ones that are # necessary here. diff --git a/eng/pipelines/kerberos/sqlclient-kerberos.yml b/eng/pipelines/kerberos/sqlclient-kerberos.yml index f43ee0a280..a8c4463c13 100644 --- a/eng/pipelines/kerberos/sqlclient-kerberos.yml +++ b/eng/pipelines/kerberos/sqlclient-kerberos.yml @@ -141,16 +141,16 @@ stages: # requires compile-time parameters. - pwsh: | $managedSni = [System.Convert]::ToBoolean($env:MANAGED_SNI) - $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + $jdata = Get-Content -Raw "config.default.jsonc" | ConvertFrom-Json foreach ($p in $jdata) { $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING $p.SupportsIntegratedSecurity = $true $p.UseManagedSNIOnWindows = $managedSni } - $jdata | ConvertTo-Json | Set-Content "config.json" + $jdata | ConvertTo-Json | Set-Content "config.jsonc" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities - displayName: Update test config.json + displayName: Update test config.jsonc env: REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) @@ -234,7 +234,7 @@ stages: # --- Update test configuration (with Kerberos credentials) --- - pwsh: | - $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + $jdata = Get-Content -Raw "config.default.jsonc" | ConvertFrom-Json foreach ($p in $jdata) { $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING @@ -242,9 +242,9 @@ stages: } $jdata | Add-Member -NotePropertyName "KerberosDomainUser" -NotePropertyValue $env:KERBEROS_DOMAIN_USER -Force $jdata | Add-Member -NotePropertyName "KerberosDomainPassword" -NotePropertyValue $env:KERBEROS_DOMAIN_PASSWORD -Force - $jdata | ConvertTo-Json | Set-Content "config.json" + $jdata | ConvertTo-Json | Set-Content "config.jsonc" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities - displayName: Update test config.json (Kerberos) + displayName: Update test config.jsonc (Kerberos) env: REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) diff --git a/eng/pipelines/stress/stress-tests-job.yml b/eng/pipelines/stress/stress-tests-job.yml index ac82002ece..2633445214 100644 --- a/eng/pipelines/stress/stress-tests-job.yml +++ b/eng/pipelines/stress/stress-tests-job.yml @@ -164,7 +164,7 @@ jobs: "@ # Write the JSON content to the config file. - $content | Out-File -FilePath "config.json" + $content | Out-File -FilePath "config.jsonc" # Authenticate with NuGet feeds so that upstream packages (e.g. runtime host packs) can be # fetched through the ADO Artifacts feed. This is required on hosted pool agents (macOS) @@ -208,7 +208,7 @@ jobs: inputs: command: run projects: $(project) - arguments: $(runArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) + arguments: $(runArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.jsonc -- $(testArguments) # Run the stress tests for each .NET Framework runtime. - ${{ each runtime in parameters.netFrameworkTestRuntimes }}: @@ -219,4 +219,4 @@ jobs: inputs: command: run projects: $(project) - arguments: $(runArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) + arguments: $(runArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.jsonc -- $(testArguments) diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs index f153096d9f..cec4721b95 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs +++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs @@ -10,9 +10,9 @@ namespace Microsoft.Data.SqlClient.Extensions.Azure.Test; /// /// This class reads configuration information from environment variables and -/// the config.json file for use by our tests. +/// the config.jsonc file for use by our tests. /// -/// Environment variables take precedence over config.json settings. Note that +/// Environment variables take precedence over config.jsonc settings. Note that /// variable names are case-sensitive on non-Windows platforms. /// /// The following variables are supported: @@ -30,7 +30,7 @@ namespace Microsoft.Data.SqlClient.Extensions.Azure.Test; /// TEST_MDS_CONFIG: /// The path to the config file to use instead of the default. If not /// supplied, the config file is assumed to be located next to the test -/// assembly and is named config.json. +/// assembly and is named config.jsonc. /// internal static class Config { @@ -86,13 +86,13 @@ internal static bool IsAzureSqlServer() => /// static Config() { - // Read from the config.json file. If the TEST_MDS_CONFIG environment + // Read from the config.jsonc file. If the TEST_MDS_CONFIG environment // variable is set, use it. Otherwise, assume the config file is in the - // working directory and named config.json. + // working directory and named config.jsonc. string configPath = GetEnvVar("TEST_MDS_CONFIG"); if (configPath.IsEmpty()) { - configPath = "config.json"; + configPath = "config.jsonc"; } try @@ -109,10 +109,10 @@ static Config() JsonElement root = doc.RootElement; // See the sample config file for information about these settings: // - // src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json + // src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc // // The sample file is copied to the build output directory as - // config.json by the TestUtilities project file. + // config.jsonc by the TestUtilities project file. // IntegratedSecuritySupported = GetBool(root, "SupportsIntegratedSecurity"); ManagedIdentitySupported = GetBool(root, "ManagedIdentitySupported"); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj index 83dd4a8351..d47ec611de 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj @@ -361,6 +361,7 @@ + @@ -388,6 +389,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs index 52bf5c30f4..6b229d5bc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs @@ -19,7 +19,7 @@ public static class SqlDbManager private const string DB_Northwind = "Northwind"; private const string DB_Master = "master"; private const string NorthWindScriptPath = @"../../../../../tools/testsql/createNorthwindDb.sql"; - private const string ConfigPath = @"../Microsoft.Data.SqlClient.TestUtilities/config.json"; + private const string ConfigPath = @"../Microsoft.Data.SqlClient.TestUtilities/config.jsonc"; private const string TCPConnectionString = "TCPConnectionString"; private const string NPConnectionString = "NPConnectionString"; @@ -68,7 +68,7 @@ public static void Run(string[] args) CreateDatabase(dbName, context); Console.WriteLine($"Database [{dbName}] created successfully in {builder.DataSource}"); } - // Update Config.json accordingly + // Update config.jsonc accordingly builder.InitialCatalog = dbName; UpdateConfig(activeConnString.Key, builder); } @@ -96,7 +96,7 @@ public static void Run(string[] args) } if (args[0] == "CreateDatabase") { - // Update config.json with Initial Catalog = for "Active Connection Strings" + // Update config.jsonc with Initial Catalog = for "Active Connection Strings" Config.UpdateConfig(s_configJson, ConfigPath); } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index 24c9c4f884..6b1dda8ae0 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -4,71 +4,73 @@ using System; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; + +#nullable enable namespace Microsoft.Data.SqlClient.TestUtilities { public class Config { - public string TCPConnectionString = null; - public string NPConnectionString = null; - public string TCPConnectionStringHGSVBS = null; - public string TCPConnectionStringNoneVBS = null; - public string TCPConnectionStringAASSGX = null; + private static readonly JsonSerializerOptions JsonSerializerOptions = new() + { + AllowTrailingCommas = true, + IncludeFields = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + public string? TCPConnectionString = null; + public string? NPConnectionString = null; + public string? TCPConnectionStringHGSVBS = null; + public string? TCPConnectionStringNoneVBS = null; + public string? TCPConnectionStringAASSGX = null; public bool EnclaveEnabled = false; public bool TracingEnabled = false; - public string AADAuthorityURL = null; - public string AADPasswordConnectionString = null; - public string AADServicePrincipalId = null; - public string AADServicePrincipalSecret = null; - public string AzureKeyVaultURL = null; - public string AzureKeyVaultTenantId = null; + public string? AADAuthorityURL = null; + public string? AADPasswordConnectionString = null; + public string? AADServicePrincipalId = null; + public string? AADServicePrincipalSecret = null; + public string? AzureKeyVaultURL = null; + public string? AzureKeyVaultTenantId = null; public bool SupportsIntegratedSecurity = false; - public string LocalDbAppName = null; - public string LocalDbSharedInstanceName = null; - public string FileStreamDirectory = null; + public string? LocalDbAppName = null; + public string? LocalDbSharedInstanceName = null; + public string? FileStreamDirectory = null; public bool UseManagedSNIOnWindows = false; - public string DNSCachingConnString = null; - public string DNSCachingServerCR = null; // this is for the control ring - public string DNSCachingServerTR = null; // this is for the tenant ring + public string? DNSCachingConnString = null; + public string? DNSCachingServerCR = null; // this is for the control ring + public string? DNSCachingServerTR = null; // this is for the tenant ring public bool IsDNSCachingSupportedCR = false; // this is for the control ring public bool IsDNSCachingSupportedTR = false; // this is for the tenant ring - public string EnclaveAzureDatabaseConnString = null; + public string? EnclaveAzureDatabaseConnString = null; public bool ManagedIdentitySupported = true; - public string UserManagedIdentityClientId = null; - public string PowerShellPath = null; - public string AliasName = null; - public string KerberosDomainPassword = null; - public string KerberosDomainUser = null; + public string? UserManagedIdentityClientId = null; + public string? PowerShellPath = null; + public string? AliasName = null; + public string? KerberosDomainPassword = null; + public string? KerberosDomainUser = null; public bool IsManagedInstance = false; - public static Config Load(string configPath = @"config.json") + public static Config Load(string configPath) { - // Allow an override of the config path via an environment variable. - configPath = Environment.GetEnvironmentVariable("TEST_MDS_CONFIG") ?? configPath; + Config config = LoadInternal(Environment.GetEnvironmentVariable("TEST_MDS_CONFIG")) ?? + LoadInternal(configPath) ?? + throw new FileNotFoundException("Could not find test configuration file."); - Config config; - try - { - using (StreamReader r = new StreamReader(configPath)) - { - config = JsonConvert.DeserializeObject(r.ReadToEnd()) - ?? throw new InvalidOperationException($"Failed to deserialize config from '{configPath}'."); - } - } - catch - { - throw; - } + // Allow environment variables to override individual config values. + SetFromEnv("MDS_TCPConnectionString", ref config.TCPConnectionString); - static void SetFromEnv(string envVar, ref string configValue) - { - string envValue = Environment.GetEnvironmentVariable(envVar); - if (!string.IsNullOrEmpty(envValue)) - { - configValue = envValue; - } - } + return config; + } + + public static Config Load() + { + // Load config from environment variable first, jsonc file second, json file last. + Config config = LoadInternal(Environment.GetEnvironmentVariable("TEST_MDS_CONFIG")) ?? + LoadInternal("config.jsonc") ?? + LoadInternal("config.json") ?? + throw new FileNotFoundException("Could not find test configuration file."); // Allow environment variables to override individual config values. SetFromEnv("MDS_TCPConnectionString", ref config.TCPConnectionString); @@ -76,10 +78,39 @@ static void SetFromEnv(string envVar, ref string configValue) return config; } - public static void UpdateConfig(Config updatedConfig, string configPath = @"config.json") + public static void UpdateConfig(Config updatedConfig, string configPath = @"config.jsonc") { - string config = JsonConvert.SerializeObject(updatedConfig); + string config = JsonSerializer.Serialize(updatedConfig, JsonSerializerOptions); File.WriteAllText(configPath, config); } + + private static Config? LoadInternal(string? configPath) + { + if (configPath is null) + { + return null; + } + + try + { + using StreamReader sr = new StreamReader(configPath); + return JsonSerializer.Deserialize(sr.ReadToEnd(), JsonSerializerOptions) ?? + throw new InvalidOperationException($"Failed to deserialize config from '{configPath}'"); + } + catch (Exception e) when (e is FileNotFoundException or DirectoryNotFoundException) + { + // File did not exist at the path given. We will try a different location. + return null; + } + } + + private static void SetFromEnv(string envVar, ref string? configValue) + { + string? envValue = Environment.GetEnvironmentVariable(envVar); + if (!string.IsNullOrEmpty(envValue)) + { + configValue = envValue; + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj index c50a251e4f..05444fd022 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj @@ -10,13 +10,16 @@ $(TestSigningKeyPath) + - + - - PreserveNewest - - + + + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc similarity index 100% rename from src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json rename to src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc