Files
Joseph Doherty 1fc7792cd1 refactor(configmanager): rename UI project and split test projects
Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project
structure, and split the monolithic test project into Core.Tests,
Cli.Tests, and Ui.Tests to align with the source project organization.
2026-01-28 10:24:36 -05:00

211 lines
7.4 KiB
C#

using JdeScoping.ConfigManager.Core.Services;
namespace JdeScoping.ConfigManager.Core.Tests.Services;
public class AutoDiscoveryServiceTests : IDisposable
{
private readonly IFileSystem _fileSystem;
private readonly AutoDiscoveryService _sut;
private readonly string? _originalEnvVar;
public AutoDiscoveryServiceTests()
{
_fileSystem = Substitute.For<IFileSystem>();
_sut = new AutoDiscoveryService(_fileSystem);
// Save original env var to restore later
_originalEnvVar = Environment.GetEnvironmentVariable("JDESCOPING_CONFIG_PATH");
// Set up default Combine behavior to work like Path.Combine
_fileSystem.Combine(Arg.Any<string[]>()).Returns(callInfo =>
{
var paths = callInfo.Arg<string[]>();
return Path.Combine(paths);
});
}
public void Dispose()
{
// Restore original env var
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", _originalEnvVar);
}
[Fact]
public async Task FindConfigFolderAsync_WhenEnvVarSet_ReturnsEnvPath()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", "/custom/config");
_fileSystem.DirectoryExists("/custom/config").Returns(true);
_fileSystem.FileExists(Arg.Is<string>(s => s.Contains("appsettings.json") && s.Contains("/custom/config"))).Returns(true);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBe("/custom/config");
}
[Fact]
public async Task FindConfigFolderAsync_WhenEnvVarSetButDirectoryInvalid_SearchesOtherLocations()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", "/invalid/config");
_fileSystem.DirectoryExists(Arg.Any<string>()).Returns(false);
_fileSystem.FileExists(Arg.Any<string>()).Returns(false);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBeNull();
}
[Fact]
public async Task FindConfigFolderAsync_WhenEnvVarSetButNoAppSettings_SearchesOtherLocations()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", "/custom/config");
// Directory exists but no appsettings.json
_fileSystem.DirectoryExists("/custom/config").Returns(true);
_fileSystem.FileExists(Arg.Any<string>()).Returns(false);
// All other locations also don't have config
_fileSystem.DirectoryExists(Arg.Is<string>(s => s != "/custom/config")).Returns(false);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBeNull();
}
[Fact]
public async Task FindConfigFolderAsync_WhenNotFound_ReturnsNull()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", null);
_fileSystem.DirectoryExists(Arg.Any<string>()).Returns(false);
_fileSystem.FileExists(Arg.Any<string>()).Returns(false);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBeNull();
}
[Fact]
public async Task FindConfigFolderAsync_WhenExeDirectoryHasConfig_ReturnsExeDirectory()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", null);
var exeDir = AppContext.BaseDirectory;
_fileSystem.DirectoryExists(exeDir).Returns(true);
_fileSystem.FileExists(Arg.Is<string>(s => s.Contains(exeDir) && s.Contains("appsettings.json"))).Returns(true);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBe(exeDir);
}
[Fact]
public async Task FindConfigFolderAsync_WhenHostDirectoryHasConfig_ReturnsHostDirectory()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", null);
var exeDir = AppContext.BaseDirectory;
var hostDir = Path.Combine(exeDir, "..", "JdeScoping.Host");
// Exe directory doesn't have config
_fileSystem.DirectoryExists(exeDir).Returns(false);
// Host directory has config
_fileSystem.DirectoryExists(hostDir).Returns(true);
_fileSystem.FileExists(Arg.Is<string>(s => s.Contains("JdeScoping.Host") && s.Contains("appsettings.json"))).Returns(true);
// Other directories don't have config
_fileSystem.DirectoryExists(Arg.Is<string>(s => s != exeDir && s != hostDir)).Returns(false);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBe(hostDir);
}
[Fact]
public async Task FindConfigFolderAsync_WhenUserConfigDirectoryHasConfig_ReturnsUserConfigDirectory()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", null);
var exeDir = AppContext.BaseDirectory;
var hostDir = Path.Combine(exeDir, "..", "JdeScoping.Host");
// Determine user config path based on OS
string userConfigDir;
if (OperatingSystem.IsWindows())
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
userConfigDir = Path.Combine(localAppData, "JdeScoping");
}
else
{
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
userConfigDir = Path.Combine(home, ".jdescoping");
}
// Exe directory doesn't have config
_fileSystem.DirectoryExists(exeDir).Returns(false);
// Host directory doesn't have config
_fileSystem.DirectoryExists(hostDir).Returns(false);
// User config directory has config
_fileSystem.DirectoryExists(userConfigDir).Returns(true);
_fileSystem.FileExists(Arg.Is<string>(s => s.Contains(userConfigDir) && s.Contains("appsettings.json"))).Returns(true);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert
result.ShouldBe(userConfigDir);
}
[Fact]
public async Task FindConfigFolderAsync_SearchesPrioritizedOrder()
{
// Arrange - all locations have config, but env var should be returned first
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", "/env/config");
var exeDir = AppContext.BaseDirectory;
// Both env var path and exe dir have valid config
_fileSystem.DirectoryExists("/env/config").Returns(true);
_fileSystem.DirectoryExists(exeDir).Returns(true);
_fileSystem.FileExists(Arg.Any<string>()).Returns(true);
// Act
var result = await _sut.FindConfigFolderAsync();
// Assert - should return env var path, not exe dir
result.ShouldBe("/env/config");
}
[Fact]
public async Task FindConfigFolderAsync_WithCancellationToken_DoesNotThrow()
{
// Arrange
Environment.SetEnvironmentVariable("JDESCOPING_CONFIG_PATH", "/custom/config");
_fileSystem.DirectoryExists("/custom/config").Returns(true);
_fileSystem.FileExists(Arg.Any<string>()).Returns(true);
using var cts = new CancellationTokenSource();
// Act
var result = await _sut.FindConfigFolderAsync(cts.Token);
// Assert
result.ShouldBe("/custom/config");
}
}