feat(configmanager): add config set and connection update CLI commands

Add missing CLI commands to match UI capabilities: config set commands for
all configuration sections (datasync, dataaccess, auth, ldap, search,
excelexport) and connection update command. Also adds unit tests for
SecretCommands, ValidateCommand, and TestConnectionCommand.
This commit is contained in:
Joseph Doherty
2026-01-28 15:10:22 -05:00
parent bad0102af1
commit 61694ca50b
11 changed files with 2172 additions and 2 deletions
@@ -0,0 +1,294 @@
using System.CommandLine;
using JdeScoping.ConfigManager.Cli.Commands;
using JdeScoping.ConfigManager.Core.Models;
using JdeScoping.ConfigManager.Core.Services;
using Microsoft.Extensions.DependencyInjection;
namespace JdeScoping.ConfigManager.Cli.Tests.Commands;
[Collection("Console Tests")]
public class TestConnectionCommandTests
{
private readonly IServiceProvider _serviceProvider;
private readonly IConfigFileService _configFileService;
private readonly IAutoDiscoveryService _autoDiscoveryService;
private readonly IConnectionTestService _connectionTestService;
private readonly Option<string?> _configPathOption;
private readonly Option<bool> _verboseOption;
private readonly Option<bool> _quietOption;
public TestConnectionCommandTests()
{
_configFileService = Substitute.For<IConfigFileService>();
_autoDiscoveryService = Substitute.For<IAutoDiscoveryService>();
_connectionTestService = Substitute.For<IConnectionTestService>();
var services = new ServiceCollection();
services.AddSingleton(_configFileService);
services.AddSingleton(_autoDiscoveryService);
services.AddSingleton(_connectionTestService);
_serviceProvider = services.BuildServiceProvider();
_configPathOption = new Option<string?>(["--config-path", "-c"]);
_verboseOption = new Option<bool>(["--verbose", "-v"]);
_quietOption = new Option<bool>(["--quiet", "-q"]);
}
[Fact]
public void CreateSqlCommand_ReturnsCommand()
{
// Act
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.ShouldNotBeNull();
command.Name.ShouldBe("sql");
command.Description.ShouldBe("Test SQL Server connection");
}
[Fact]
public void CreateSqlCommand_HasNameOption()
{
// Act
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.Options.ShouldContain(o => o.Name == "name");
}
[Fact]
public void CreateOracleCommand_ReturnsCommand()
{
// Act
var command = TestConnectionCommand.CreateOracleCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.ShouldNotBeNull();
command.Name.ShouldBe("oracle");
command.Description.ShouldBe("Test Oracle connection");
}
[Fact]
public void CreateOracleCommand_HasNameOption()
{
// Act
var command = TestConnectionCommand.CreateOracleCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.Options.ShouldContain(o => o.Name == "name");
}
[Fact]
public void CreateAllCommand_ReturnsCommand()
{
// Act
var command = TestConnectionCommand.CreateAllCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.ShouldNotBeNull();
command.Name.ShouldBe("all");
command.Description.ShouldBe("Test all configured connections");
}
[Fact]
public async Task SqlCommand_WithNoConfigFolder_ReturnsError()
{
// Arrange
_autoDiscoveryService.FindConfigFolderAsync(Arg.Any<CancellationToken>())
.Returns(Task.FromResult<string?>(null));
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
var rootCommand = new RootCommand { command };
rootCommand.AddGlobalOption(_configPathOption);
rootCommand.AddGlobalOption(_verboseOption);
rootCommand.AddGlobalOption(_quietOption);
// Act
var exitCode = await rootCommand.InvokeAsync(["sql"]);
// Assert - command completes without throwing
command.ShouldNotBeNull();
}
[Fact]
public async Task SqlCommand_WithSuccessfulConnection_ReturnsSuccess()
{
// Arrange
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);
var appSettingsPath = Path.Combine(tempDir, "appsettings.json");
File.WriteAllText(appSettingsPath, "{}");
try
{
_autoDiscoveryService.FindConfigFolderAsync(Arg.Any<CancellationToken>())
.Returns(Task.FromResult<string?>(tempDir));
var config = new ConfigModel
{
ConnectionStrings = new ConnectionStringsSection
{
Entries =
[
new ConnectionStringEntry
{
Name = "LocalCache",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
]
}
};
_configFileService.LoadAppSettingsAsync(appSettingsPath, Arg.Any<CancellationToken>())
.Returns(Task.FromResult(config));
_connectionTestService.TestConnectionAsync(
Arg.Any<string>(),
ConnectionProvider.SqlServer,
Arg.Any<CancellationToken>())
.Returns(Task.FromResult(new ConnectionTestResult
{
Success = true,
Duration = TimeSpan.FromMilliseconds(50)
}));
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
var rootCommand = new RootCommand { command };
rootCommand.AddGlobalOption(_configPathOption);
rootCommand.AddGlobalOption(_verboseOption);
rootCommand.AddGlobalOption(_quietOption);
// Capture console output
var originalOut = Console.Out;
using var writer = new StringWriter();
Console.SetOut(writer);
try
{
// Act
await rootCommand.InvokeAsync(["sql"]);
// Assert
var output = writer.ToString();
output.ShouldContain("Success");
}
finally
{
Console.SetOut(originalOut);
}
}
finally
{
Directory.Delete(tempDir, true);
}
}
[Fact]
public async Task SqlCommand_WithFailedConnection_ReturnsError()
{
// Arrange
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);
var appSettingsPath = Path.Combine(tempDir, "appsettings.json");
File.WriteAllText(appSettingsPath, "{}");
try
{
_autoDiscoveryService.FindConfigFolderAsync(Arg.Any<CancellationToken>())
.Returns(Task.FromResult<string?>(tempDir));
var config = new ConfigModel
{
ConnectionStrings = new ConnectionStringsSection
{
Entries =
[
new ConnectionStringEntry
{
Name = "LocalCache",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
]
}
};
_configFileService.LoadAppSettingsAsync(appSettingsPath, Arg.Any<CancellationToken>())
.Returns(Task.FromResult(config));
_connectionTestService.TestConnectionAsync(
Arg.Any<string>(),
ConnectionProvider.SqlServer,
Arg.Any<CancellationToken>())
.Returns(Task.FromResult(new ConnectionTestResult
{
Success = false,
Message = "Connection refused"
}));
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
var rootCommand = new RootCommand { command };
rootCommand.AddGlobalOption(_configPathOption);
rootCommand.AddGlobalOption(_verboseOption);
rootCommand.AddGlobalOption(_quietOption);
// Capture console error output
var originalErr = Console.Error;
using var writer = new StringWriter();
Console.SetError(writer);
try
{
// Act
await rootCommand.InvokeAsync(["sql"]);
// Assert
var output = writer.ToString();
output.ShouldContain("Failed");
}
finally
{
Console.SetError(originalErr);
}
}
finally
{
Directory.Delete(tempDir, true);
}
}
[Fact]
public void SqlCommand_NameOptionExists()
{
// Arrange & Act
var command = TestConnectionCommand.CreateSqlCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
var nameOption = command.Options.FirstOrDefault(o => o.Name == "name");
nameOption.ShouldNotBeNull();
nameOption.Description.ShouldBe("Name of the connection string to test");
}
[Fact]
public void AllCommand_HasCorrectDescription()
{
// Arrange & Act
var command = TestConnectionCommand.CreateAllCommand(
_serviceProvider, _configPathOption, _verboseOption, _quietOption);
// Assert
command.Description.ShouldBe("Test all configured connections");
}
}