From bbdac3380b98395e6cd6fbfc5ea35ca57adeb55a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:15:29 +0000 Subject: [PATCH 1/7] Initial plan From f1f46f36e23ea02d0da309b33d45b3f105f6b075 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:24:28 +0000 Subject: [PATCH 2/7] Make settings path deterministic and report it when config is missing Co-authored-by: isourabh <2982389+isourabh@users.noreply.github.com> --- .../ConfigurationManagerUnitTests.cs | 117 ++++++++++++++++++ MSStore.CLI/MicrosoftStoreCLI.cs | 2 +- MSStore.CLI/Services/ConfigurationManager.cs | 50 +++++++- README.md | 6 + 4 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs diff --git a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs new file mode 100644 index 0000000..4dd4382 --- /dev/null +++ b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Text.Json; +using MSStore.CLI.Services; + +namespace MSStore.CLI.UnitTests +{ + [TestClass] + public class ConfigurationManagerUnitTests + { + public TestContext TestContext { get; set; } = null!; + + private string _settingsDirectory = null!; + private string? _originalSettingsDirectory; + + [TestInitialize] + public void Initialize() + { + _originalSettingsDirectory = Environment.GetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable); + _settingsDirectory = Path.Combine(Path.GetTempPath(), $"msstore-cli-tests-{Guid.NewGuid()}"); + Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, _settingsDirectory); + } + + [TestCleanup] + public void Cleanup() + { + Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, _originalSettingsDirectory); + + if (Directory.Exists(_settingsDirectory)) + { + Directory.Delete(_settingsDirectory, true); + } + } + + private static ConfigurationManager CreateConfigurationManager() + => new(ConfigurationsSourceGenerationContext.Default.Configurations, "settings.json", null); + + [TestMethod] + public void ConfigurationManager_ShouldUseSettingsDirectoryEnvironmentVariable() + { + var configurationManager = CreateConfigurationManager(); + + configurationManager.ConfigPath.Should().Be(Path.Combine(_settingsDirectory, "settings.json")); + } + + [TestMethod] + public void ConfigurationManager_ConfigPathShouldAlwaysBeRooted() + { + Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, null); + + var configurationManager = CreateConfigurationManager(); + + Path.IsPathRooted(configurationManager.ConfigPath).Should().BeTrue(); + } + + [TestMethod] + public async Task ConfigurationManager_LoadShouldNotCreateSettingsFileIfItDoesNotExist() + { + var configurationManager = CreateConfigurationManager(); + + var config = await configurationManager.LoadAsync(false, CancellationToken.None); + + config.SellerId.Should().BeNull(); + File.Exists(configurationManager.ConfigPath).Should().BeFalse(); + } + + [TestMethod] + public async Task ConfigurationManager_LoadShouldReturnSavedSettings() + { + var configurationManager = CreateConfigurationManager(); + + await configurationManager.SaveAsync( + new Configurations + { + SellerId = 12345, + TenantId = new Guid("41261775-DB6D-4B44-9A36-7EB8565C7D22"), + ClientId = new Guid("3F0BCAEF-6334-48CF-837F-81CB0F1F2C45") + }, + CancellationToken.None); + + var config = await new ConfigurationManager(ConfigurationsSourceGenerationContext.Default.Configurations, "settings.json", null) + .LoadAsync(false, CancellationToken.None); + + config.SellerId.Should().Be(12345); + config.TenantId.Should().Be(new Guid("41261775-DB6D-4B44-9A36-7EB8565C7D22")); + config.ClientId.Should().Be(new Guid("3F0BCAEF-6334-48CF-837F-81CB0F1F2C45")); + } + + [TestMethod] + public async Task ConfigurationManager_LoadShouldThrowIfSettingsAreInvalidAndShouldNotClearThem() + { + var configurationManager = CreateConfigurationManager(); + + Directory.CreateDirectory(_settingsDirectory); + await File.WriteAllTextAsync(configurationManager.ConfigPath, "not a json", TestContext.CancellationToken); + + await Assert.ThrowsExactlyAsync(() => configurationManager.LoadAsync(false, CancellationToken.None)); + + (await File.ReadAllTextAsync(configurationManager.ConfigPath, TestContext.CancellationToken)).Should().Be("not a json"); + } + + [TestMethod] + public async Task ConfigurationManager_LoadShouldClearInvalidSettingsIfRequested() + { + var configurationManager = CreateConfigurationManager(); + + Directory.CreateDirectory(_settingsDirectory); + await File.WriteAllTextAsync(configurationManager.ConfigPath, "not a json", TestContext.CancellationToken); + + var config = await configurationManager.LoadAsync(true, CancellationToken.None); + + config.SellerId.Should().BeNull(); + (await File.ReadAllTextAsync(configurationManager.ConfigPath, TestContext.CancellationToken)).Should().NotBe("not a json"); + } + } +} diff --git a/MSStore.CLI/MicrosoftStoreCLI.cs b/MSStore.CLI/MicrosoftStoreCLI.cs index 1efbdbf..f454860 100644 --- a/MSStore.CLI/MicrosoftStoreCLI.cs +++ b/MSStore.CLI/MicrosoftStoreCLI.cs @@ -118,7 +118,7 @@ internal static async Task InitAsync(IAnsiConsole ansiConsole, IConfigurat if (config.SellerId == null) { - logger.LogCritical("SellerId is not set."); + logger.LogCritical("SellerId is not set. No configuration was found at '{SettingsPath}'. Please, run the 'reconfigure' command.", configurationManager.ConfigPath); return false; } diff --git a/MSStore.CLI/Services/ConfigurationManager.cs b/MSStore.CLI/Services/ConfigurationManager.cs index 0b19aef..b0cea62 100644 --- a/MSStore.CLI/Services/ConfigurationManager.cs +++ b/MSStore.CLI/Services/ConfigurationManager.cs @@ -19,7 +19,40 @@ namespace MSStore.CLI.Services internal class ConfigurationManager(JsonTypeInfo jsonTypeInfo, string fileName, ILogger>? logger) : IConfigurationManager where T : new() { - private static readonly string SettingsDirectory = Path.Combine(GetSystemLocalApplicationDataPath(), "Microsoft", "MSStore.CLI"); + /// + /// Environment variable that overrides the directory where the CLI stores its settings files. + /// Useful when the user's local application data folder cannot be resolved, or is not stable + /// between invocations (containers without a passwd entry, ephemeral HOME directories, etc). + /// + internal static readonly string SettingsDirectoryEnvironmentVariable = "MSSTORE_SETTINGS_DIRECTORY"; + + /// + /// Resolves the directory where the settings files live. The returned path is always rooted, so that + /// it can never be interpreted relative to the current working directory, which would make the settings + /// files resolve to different locations depending on where the CLI happens to be invoked from. + /// + /// The rooted settings directory path. + private static string GetSettingsDirectory() + { + var settingsDirectoryOverride = Environment.GetEnvironmentVariable(SettingsDirectoryEnvironmentVariable); + if (!string.IsNullOrWhiteSpace(settingsDirectoryOverride)) + { + return Path.GetFullPath(settingsDirectoryOverride); + } + + var localApplicationDataPath = GetSystemLocalApplicationDataPath(); + + if (string.IsNullOrEmpty(localApplicationDataPath) || !Path.IsPathRooted(localApplicationDataPath)) + { + // The system could not tell us where the local application data folder is (for instance, on Unix, + // when neither XDG_DATA_HOME, nor HOME, nor the passwd entry are available). Falling back to a + // relative path would make the settings file depend on the current working directory, so a + // rooted, invocation-independent location is used instead. + localApplicationDataPath = Path.Combine(Path.GetTempPath(), $".msstore-cli-{Environment.UserName}"); + } + + return Path.GetFullPath(Path.Combine(localApplicationDataPath, "Microsoft", "MSStore.CLI")); + } private static string GetSystemLocalApplicationDataPath() { @@ -43,7 +76,7 @@ private static string GetSystemLocalApplicationDataPath() return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); } - private readonly string _settingsPath = Path.Combine(SettingsDirectory, fileName); + private readonly string _settingsPath = Path.Combine(GetSettingsDirectory(), fileName); private readonly JsonTypeInfo _jsonTypeInfo = jsonTypeInfo ?? throw new ArgumentNullException(nameof(jsonTypeInfo)); private readonly ILogger? _logger = logger; @@ -56,7 +89,9 @@ public async Task LoadAsync(bool clearInvalidConfig, CancellationToken ct) EnsureDirectoryExists(); if (!File.Exists(_settingsPath)) { - return await ClearAsync(ct); + _logger?.LogInformation("Settings file not found at '{SettingsPath}'. Using the default settings.", _settingsPath); + + return new T(); } using var file = File.Open(_settingsPath, FileMode.Open); @@ -88,6 +123,7 @@ public async Task ClearAsync(CancellationToken ct) public async Task SaveAsync(T config, CancellationToken ct) { + EnsureDirectoryExists(); using var file = File.Open(_settingsPath, FileMode.OpenOrCreate); file.SetLength(0); file.Position = 0; @@ -96,14 +132,16 @@ public async Task SaveAsync(T config, CancellationToken ct) private void EnsureDirectoryExists() { - if (Directory.Exists(SettingsDirectory)) + var settingsDirectory = Path.GetDirectoryName(_settingsPath)!; + + if (Directory.Exists(settingsDirectory)) { return; } - _logger?.LogInformation("Creating settings directory: {SettingsDirectory}", SettingsDirectory); + _logger?.LogInformation("Creating settings directory: {SettingsDirectory}", settingsDirectory); - _ = Directory.CreateDirectory(SettingsDirectory); + _ = Directory.CreateDirectory(settingsDirectory); } } } diff --git a/README.md b/README.md index f01df15..c7acac4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ The Microsoft Store Developer Command Line Interface is a cross-platform (Window ## Helpful links * [Documentation](https://aka.ms/msstoredevcli/docs) - Microsoft's official documentation on regards to available commands, installation steps, how to properly setup CI/CD environments, and general guidance. +## Settings location + +The CLI stores its (non-secret) configuration in a `settings.json` file, inside the `Microsoft/MSStore.CLI` folder of the user's local application data folder (`%LOCALAPPDATA%` on Windows, `~/Library/Application Support` on MacOS, and `$XDG_DATA_HOME`/`~/.local/share` on Linux). Secrets are never stored in this file, they always go to the operating system's credential store. + +If that folder cannot be resolved, or is not stable between invocations (containers without a `passwd` entry, or CI setups that use an ephemeral `HOME`, for example), set the `MSSTORE_SETTINGS_DIRECTORY` environment variable to an absolute path, and the CLI will read and write its settings files there. + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a From 5f0f7e39d63ea8983312b812b4466d54cb2ecd0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:28:17 +0000 Subject: [PATCH 3/7] Sanitize the user name used in the temporary settings folder fallback Co-authored-by: isourabh <2982389+isourabh@users.noreply.github.com> --- MSStore.CLI/Services/ConfigurationManager.cs | 22 +++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/MSStore.CLI/Services/ConfigurationManager.cs b/MSStore.CLI/Services/ConfigurationManager.cs index b0cea62..40f769e 100644 --- a/MSStore.CLI/Services/ConfigurationManager.cs +++ b/MSStore.CLI/Services/ConfigurationManager.cs @@ -48,12 +48,32 @@ private static string GetSettingsDirectory() // when neither XDG_DATA_HOME, nor HOME, nor the passwd entry are available). Falling back to a // relative path would make the settings file depend on the current working directory, so a // rooted, invocation-independent location is used instead. - localApplicationDataPath = Path.Combine(Path.GetTempPath(), $".msstore-cli-{Environment.UserName}"); + localApplicationDataPath = Path.Combine(Path.GetTempPath(), GetTemporarySettingsFolderName()); } return Path.GetFullPath(Path.Combine(localApplicationDataPath, "Microsoft", "MSStore.CLI")); } + /// + /// Builds the name of the folder used, inside the temporary folder, when the local application data + /// folder cannot be resolved. The user name is appended, when it is usable as a folder name, so that + /// different users on the same machine do not share the same settings folder. + /// + /// The temporary settings folder name. + private static string GetTemporarySettingsFolderName() + { + const string FolderName = ".msstore-cli"; + + var userName = Environment.UserName; + + if (string.IsNullOrWhiteSpace(userName) || userName.AsSpan().IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + return FolderName; + } + + return $"{FolderName}-{userName}"; + } + private static string GetSystemLocalApplicationDataPath() { #if !WINDOWS From 131283574105294a8a5502377f4863737a14b51c Mon Sep 17 00:00:00 2001 From: isourabh Date: Thu, 10 Sep 2026 18:01:53 +0530 Subject: [PATCH 4/7] Update log message for missing SellerId configuration Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- MSStore.CLI/MicrosoftStoreCLI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.CLI/MicrosoftStoreCLI.cs b/MSStore.CLI/MicrosoftStoreCLI.cs index f454860..1bca1cc 100644 --- a/MSStore.CLI/MicrosoftStoreCLI.cs +++ b/MSStore.CLI/MicrosoftStoreCLI.cs @@ -118,7 +118,7 @@ internal static async Task InitAsync(IAnsiConsole ansiConsole, IConfigurat if (config.SellerId == null) { - logger.LogCritical("SellerId is not set. No configuration was found at '{SettingsPath}'. Please, run the 'reconfigure' command.", configurationManager.ConfigPath); + logger.LogCritical("SellerId is not set. Settings file path: '{SettingsPath}'. Please, run the 'reconfigure' command.", configurationManager.ConfigPath); return false; } From 7e73c3fe4e49cdecdc161557b957042e65ee9c24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 12:37:26 +0000 Subject: [PATCH 5/7] Ignore relative settings directory overrides and keep LoadAsync read-only Co-authored-by: isourabh <2982389+isourabh@users.noreply.github.com> --- .../ConfigurationManagerUnitTests.cs | 22 ++++++++++++++++ MSStore.CLI/Services/ConfigurationManager.cs | 26 ++++++++++++++----- README.md | 2 +- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs index 4dd4382..123cb2f 100644 --- a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs +++ b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs @@ -54,6 +54,28 @@ public void ConfigurationManager_ConfigPathShouldAlwaysBeRooted() Path.IsPathRooted(configurationManager.ConfigPath).Should().BeTrue(); } + [TestMethod] + public void ConfigurationManager_ShouldIgnoreRelativeSettingsDirectoryEnvironmentVariable() + { + Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, Path.Combine("relative", "settings")); + + var configurationManager = CreateConfigurationManager(); + + Path.IsPathRooted(configurationManager.ConfigPath).Should().BeTrue(); + configurationManager.ConfigPath.Should().NotContain("relative"); + } + + [TestMethod] + public async Task ConfigurationManager_LoadShouldNotCreateSettingsDirectoryIfItDoesNotExist() + { + var configurationManager = CreateConfigurationManager(); + + var config = await configurationManager.LoadAsync(false, CancellationToken.None); + + config.SellerId.Should().BeNull(); + Directory.Exists(_settingsDirectory).Should().BeFalse(); + } + [TestMethod] public async Task ConfigurationManager_LoadShouldNotCreateSettingsFileIfItDoesNotExist() { diff --git a/MSStore.CLI/Services/ConfigurationManager.cs b/MSStore.CLI/Services/ConfigurationManager.cs index 40f769e..c39d6a6 100644 --- a/MSStore.CLI/Services/ConfigurationManager.cs +++ b/MSStore.CLI/Services/ConfigurationManager.cs @@ -21,8 +21,9 @@ internal class ConfigurationManager(JsonTypeInfo jsonTypeInfo, string file { /// /// Environment variable that overrides the directory where the CLI stores its settings files. - /// Useful when the user's local application data folder cannot be resolved, or is not stable - /// between invocations (containers without a passwd entry, ephemeral HOME directories, etc). + /// Must be set to an absolute path. Useful when the user's local application data folder cannot be + /// resolved, or is not stable between invocations (containers without a passwd entry, ephemeral + /// HOME directories, etc). /// internal static readonly string SettingsDirectoryEnvironmentVariable = "MSSTORE_SETTINGS_DIRECTORY"; @@ -31,13 +32,25 @@ internal class ConfigurationManager(JsonTypeInfo jsonTypeInfo, string file /// it can never be interpreted relative to the current working directory, which would make the settings /// files resolve to different locations depending on where the CLI happens to be invoked from. /// + /// Logger used to report an unusable override. /// The rooted settings directory path. - private static string GetSettingsDirectory() + private static string GetSettingsDirectory(ILogger? logger) { var settingsDirectoryOverride = Environment.GetEnvironmentVariable(SettingsDirectoryEnvironmentVariable); if (!string.IsNullOrWhiteSpace(settingsDirectoryOverride)) { - return Path.GetFullPath(settingsDirectoryOverride); + // A relative override would put the settings files at a different place for each working + // directory the CLI is invoked from, which is exactly what this resolution avoids, so it is + // ignored rather than honored. + if (Path.IsPathRooted(settingsDirectoryOverride)) + { + return Path.GetFullPath(settingsDirectoryOverride); + } + + logger?.LogWarning( + "Ignoring the {EnvironmentVariable} environment variable: '{SettingsDirectory}' is not an absolute path.", + SettingsDirectoryEnvironmentVariable, + settingsDirectoryOverride); } var localApplicationDataPath = GetSystemLocalApplicationDataPath(); @@ -96,7 +109,7 @@ private static string GetSystemLocalApplicationDataPath() return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); } - private readonly string _settingsPath = Path.Combine(GetSettingsDirectory(), fileName); + private readonly string _settingsPath = Path.Combine(GetSettingsDirectory(logger), fileName); private readonly JsonTypeInfo _jsonTypeInfo = jsonTypeInfo ?? throw new ArgumentNullException(nameof(jsonTypeInfo)); private readonly ILogger? _logger = logger; @@ -106,7 +119,8 @@ public async Task LoadAsync(bool clearInvalidConfig, CancellationToken ct) { try { - EnsureDirectoryExists(); + // No directory is created here on purpose: loading the configuration must not require write + // access, so that a missing settings file can always fall back to the default settings. if (!File.Exists(_settingsPath)) { _logger?.LogInformation("Settings file not found at '{SettingsPath}'. Using the default settings.", _settingsPath); diff --git a/README.md b/README.md index c7acac4..81669ca 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The Microsoft Store Developer Command Line Interface is a cross-platform (Window The CLI stores its (non-secret) configuration in a `settings.json` file, inside the `Microsoft/MSStore.CLI` folder of the user's local application data folder (`%LOCALAPPDATA%` on Windows, `~/Library/Application Support` on MacOS, and `$XDG_DATA_HOME`/`~/.local/share` on Linux). Secrets are never stored in this file, they always go to the operating system's credential store. -If that folder cannot be resolved, or is not stable between invocations (containers without a `passwd` entry, or CI setups that use an ephemeral `HOME`, for example), set the `MSSTORE_SETTINGS_DIRECTORY` environment variable to an absolute path, and the CLI will read and write its settings files there. +If that folder cannot be resolved, or is not stable between invocations (containers without a `passwd` entry, or CI setups that use an ephemeral `HOME`, for example), set the `MSSTORE_SETTINGS_DIRECTORY` environment variable to an absolute path, and the CLI will read and write its settings files there. Relative paths are ignored, as they would make the settings location depend on the directory the CLI is invoked from. ## Contributing From c1b9df6d71c12cd3999102ca5727dc5861f21be0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 12:39:49 +0000 Subject: [PATCH 6/7] Merge the duplicated load-does-not-write tests Co-authored-by: isourabh <2982389+isourabh@users.noreply.github.com> --- .../ConfigurationManagerUnitTests.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs index 123cb2f..7f98979 100644 --- a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs +++ b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs @@ -66,7 +66,7 @@ public void ConfigurationManager_ShouldIgnoreRelativeSettingsDirectoryEnvironmen } [TestMethod] - public async Task ConfigurationManager_LoadShouldNotCreateSettingsDirectoryIfItDoesNotExist() + public async Task ConfigurationManager_LoadShouldNotWriteAnythingIfSettingsFileDoesNotExist() { var configurationManager = CreateConfigurationManager(); @@ -74,16 +74,6 @@ public async Task ConfigurationManager_LoadShouldNotCreateSettingsDirectoryIfItD config.SellerId.Should().BeNull(); Directory.Exists(_settingsDirectory).Should().BeFalse(); - } - - [TestMethod] - public async Task ConfigurationManager_LoadShouldNotCreateSettingsFileIfItDoesNotExist() - { - var configurationManager = CreateConfigurationManager(); - - var config = await configurationManager.LoadAsync(false, CancellationToken.None); - - config.SellerId.Should().BeNull(); File.Exists(configurationManager.ConfigPath).Should().BeFalse(); } From abf1124b37e08b5e8548d063b83ec3c8ad61ab53 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Sep 2026 13:11:30 +0000 Subject: [PATCH 7/7] Assert the relative override is not honored instead of substring matching Co-authored-by: isourabh <2982389+isourabh@users.noreply.github.com> --- MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs index 7f98979..c822332 100644 --- a/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs +++ b/MSStore.CLI.UnitTests/ConfigurationManagerUnitTests.cs @@ -57,12 +57,18 @@ public void ConfigurationManager_ConfigPathShouldAlwaysBeRooted() [TestMethod] public void ConfigurationManager_ShouldIgnoreRelativeSettingsDirectoryEnvironmentVariable() { - Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, Path.Combine("relative", "settings")); + var relativeSettingsDirectory = Path.Combine("relative", "settings"); + + Environment.SetEnvironmentVariable(ConfigurationManager.SettingsDirectoryEnvironmentVariable, relativeSettingsDirectory); var configurationManager = CreateConfigurationManager(); + // The path that would have been used if the relative override had been honored, which is anchored + // at the current working directory and would therefore move with it. + var honoredOverridePath = Path.Combine(Path.GetFullPath(relativeSettingsDirectory), "settings.json"); + Path.IsPathRooted(configurationManager.ConfigPath).Should().BeTrue(); - configurationManager.ConfigPath.Should().NotContain("relative"); + configurationManager.ConfigPath.Should().NotBe(honoredOverridePath); } [TestMethod]