Files
jdescopingtool/NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/ConnectionStringsFormViewModelTests.cs
T
Joseph Doherty 5ee710d330 feat(configmanager): add dedicated SQL Server port control
Add SqlServerPort property to connection string editor for explicit port
configuration, replacing the need to embed port in server name (e.g.,
localhost,1434). The port control generates the comma-separated format in
the connection string but does not parse it back when loading existing
connection strings.
2026-01-28 09:24:49 -05:00

664 lines
23 KiB
C#

using JdeScoping.ConfigManager.Models;
using JdeScoping.ConfigManager.Services;
using JdeScoping.ConfigManager.Services.SecureStore;
using JdeScoping.ConfigManager.ViewModels.Forms;
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
public class ConnectionStringsFormViewModelTests
{
private readonly ISecureStoreManager _secureStoreManager;
private readonly IDialogService _dialogService;
private readonly IConnectionTestService _connectionTestService;
public ConnectionStringsFormViewModelTests()
{
_secureStoreManager = Substitute.For<ISecureStoreManager>();
_dialogService = Substitute.For<IDialogService>();
_connectionTestService = Substitute.For<IConnectionTestService>();
// Setup default behavior - SecureStore is not open by default in tests
_secureStoreManager.IsStoreOpen.Returns(false);
}
[Fact]
public void Constructor_InitializesFromModel()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "Connection1",
Provider = ConnectionProvider.SqlServer,
Server = "server1"
},
new ConnectionStringEntry
{
Name = "Connection2",
Provider = ConnectionProvider.Oracle,
Host = "oracle-host"
}
}
};
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.Connections.Count.ShouldBe(2);
sut.Connections[0].Name.ShouldBe("Connection1");
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.SqlServer);
sut.Connections[0].Server.ShouldBe("server1");
sut.Connections[1].Name.ShouldBe("Connection2");
sut.Connections[1].Provider.ShouldBe(ConnectionProvider.Oracle);
sut.Connections[1].Host.ShouldBe("oracle-host");
}
[Fact]
public void Constructor_LoadsAndParsesSqlServerConnectionStringFromSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "LotFinder" }
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
_secureStoreManager.GetSecret("LotFinder")
.Returns("Server=localhost,1434;Database=ScopingTool;User Id=scopingapp;Password=pass;TrustServerCertificate=true");
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert - port stays embedded in server name, not parsed into SqlServerPort
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.SqlServer);
sut.Connections[0].Server.ShouldBe("localhost,1434");
sut.Connections[0].SqlServerPort.ShouldBeNull();
sut.Connections[0].Database.ShouldBe("ScopingTool");
sut.Connections[0].UserId.ShouldBe("scopingapp");
sut.Connections[0].Password.ShouldBe("pass");
sut.Connections[0].TrustServerCertificate.ShouldBeTrue();
}
[Fact]
public void Constructor_LoadsAndParsesOracleConnectionStringFromSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "CMS" }
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
_secureStoreManager.GetSecret("CMS")
.Returns("HOST=ha-iman;Service Name=imanprd;Fetch Array Size=1280000;Port=1522;User ID=app_teamcenter;Password=pass;");
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.Oracle);
sut.Connections[0].Host.ShouldBe("ha-iman");
sut.Connections[0].ServiceName.ShouldBe("imanprd");
sut.Connections[0].Port.ShouldBe(1522);
sut.Connections[0].UserId.ShouldBe("app_teamcenter");
sut.Connections[0].Password.ShouldBe("pass");
}
[Fact]
public void Constructor_ThrowsOnNullModel()
{
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ConnectionStringsFormViewModel(null!, _secureStoreManager, () => { }, _dialogService, _connectionTestService));
}
[Fact]
public void Constructor_ThrowsOnNullSecureStoreManager()
{
// Arrange
var model = new ConnectionStringsSection();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ConnectionStringsFormViewModel(model, null!, () => { }, _dialogService, _connectionTestService));
}
[Fact]
public void Constructor_ThrowsOnNullOnChanged()
{
// Arrange
var model = new ConnectionStringsSection();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ConnectionStringsFormViewModel(model, _secureStoreManager, null!, _dialogService, _connectionTestService));
}
[Fact]
public void Constructor_ThrowsOnNullConnectionTestService()
{
// Arrange
var model = new ConnectionStringsSection();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, null!));
}
[Fact]
public void AddConnection_CreatesNewEntryAndSelectsIt()
{
// Arrange
var model = new ConnectionStringsSection();
var changedInvoked = false;
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => changedInvoked = true, _dialogService, _connectionTestService);
// Act
sut.AddConnectionCommand.Execute(null);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Name.ShouldBe("NewConnection");
sut.SelectedConnection.ShouldBe(sut.Connections[0]);
model.Entries.Count.ShouldBe(1);
changedInvoked.ShouldBeTrue();
}
[Fact]
public void HasSelection_IsFalseWhenNothingSelected()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "Conn1" }
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert - no selection by default
sut.SelectedConnection.ShouldBeNull();
sut.HasSelection.ShouldBeFalse();
}
[Fact]
public void HasSelection_IsTrueWhenConnectionSelected()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "Conn1" }
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Act
sut.SelectedConnection = sut.Connections[0];
// Assert
sut.HasSelection.ShouldBeTrue();
}
[Fact]
public void ConnectionCount_ReflectsCollectionSize()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "Conn1" },
new ConnectionStringEntry { Name = "Conn2" },
new ConnectionStringEntry { Name = "Conn3" }
}
};
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.ConnectionCount.ShouldBe(3);
// Act - add another
sut.AddConnectionCommand.Execute(null);
// Assert
sut.ConnectionCount.ShouldBe(4);
}
[Fact]
public void Constructor_InitializesConnectionsFromModel()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "TestConnection",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb",
UserId = "sa",
Password = "secret"
}
}
};
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Name.ShouldBe("TestConnection");
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.SqlServer);
sut.Connections[0].Server.ShouldBe("localhost");
sut.Connections[0].Database.ShouldBe("TestDb");
}
[Fact]
public void AddConnectionCommand_AddsNewConnection()
{
// Arrange
var model = new ConnectionStringsSection();
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
var initialCount = sut.Connections.Count;
// Act
sut.AddConnectionCommand.Execute(null);
// Assert
sut.Connections.Count.ShouldBe(initialCount + 1);
sut.Connections.Last().Name.ShouldBe("NewConnection");
sut.Connections.Last().Provider.ShouldBe(ConnectionProvider.Generic);
}
[Fact]
public async Task DeleteConnectionCommand_WhenConfirmed_RemovesConnection()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "ToDelete" }
}
};
_dialogService.ShowConfirmationAsync(Arg.Any<string>(), Arg.Any<string>())
.Returns(true);
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.DeleteConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
sut.Connections.Count.ShouldBe(0);
model.Entries.Count.ShouldBe(0);
}
[Fact]
public async Task DeleteConnectionCommand_WhenCancelled_KeepsConnection()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "ToKeep" }
}
};
_dialogService.ShowConfirmationAsync(Arg.Any<string>(), Arg.Any<string>())
.Returns(false);
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.DeleteConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Name.ShouldBe("ToKeep");
}
[Fact]
public async Task ValidateConnectionCommand_WithEmptyConnectionString_ShowsError()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "EmptyConnection",
Provider = ConnectionProvider.Generic,
RawConnectionString = ""
}
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.ValidateConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
await _dialogService.Received().ShowMessageAsync(
"Validation Failed",
Arg.Is<string>(s => s.Contains("empty connection string")));
}
[Fact]
public async Task ValidateConnectionCommand_WithValidConnectionString_ShowsSuccess()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "ValidConnection",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.ValidateConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
await _dialogService.Received().ShowMessageAsync(
"Validation Passed",
Arg.Is<string>(s => s.Contains("valid connection string")));
}
[Fact]
public async Task TestConnectionCommand_WhenSuccessful_ShowsSuccessMessage()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "TestConn",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
}
};
_connectionTestService.TestConnectionAsync(Arg.Any<string>(), Arg.Any<ConnectionProvider>(), Arg.Any<CancellationToken>())
.Returns(new ConnectionTestResult { Success = true, Duration = TimeSpan.FromMilliseconds(50) });
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.TestConnectionCommand.Execute(null);
await Task.Delay(150);
// Assert
await _dialogService.Received().ShowMessageAsync(
"Connection Successful",
Arg.Is<string>(s => s.Contains("Successfully connected")));
}
[Fact]
public async Task TestConnectionCommand_WhenFailed_ShowsErrorMessage()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "FailConn",
Provider = ConnectionProvider.SqlServer,
Server = "badserver",
Database = "TestDb"
}
}
};
_connectionTestService.TestConnectionAsync(Arg.Any<string>(), Arg.Any<ConnectionProvider>(), Arg.Any<CancellationToken>())
.Returns(new ConnectionTestResult { Success = false, Message = "Connection refused" });
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.TestConnectionCommand.Execute(null);
await Task.Delay(150);
// Assert
await _dialogService.Received().ShowMessageAsync(
"Connection Failed",
Arg.Is<string>(s => s.Contains("Failed to connect") && s.Contains("Connection refused")));
}
[Fact]
public async Task TestConnectionCommand_SetsIsTesting_DuringExecution()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "TestConn",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
}
};
var taskCompletionSource = new TaskCompletionSource<ConnectionTestResult>();
_connectionTestService.TestConnectionAsync(Arg.Any<string>(), Arg.Any<ConnectionProvider>(), Arg.Any<CancellationToken>())
.Returns(taskCompletionSource.Task);
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act - Start the command
sut.TestConnectionCommand.Execute(null);
await Task.Delay(50);
// Assert - IsTesting should be true during execution
sut.IsTesting.ShouldBeTrue();
// Complete the task
taskCompletionSource.SetResult(new ConnectionTestResult { Success = true });
await Task.Delay(100);
// Assert - IsTesting should be false after completion
sut.IsTesting.ShouldBeFalse();
}
[Fact]
public void SelectedConnection_WhenChanged_RaisesHasSelectionPropertyChanged()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "Conn1" }
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
var hasSelectionChangedRaised = false;
sut.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(ConnectionStringsFormViewModel.HasSelection))
hasSelectionChangedRaised = true;
};
// Act
sut.SelectedConnection = sut.Connections[0];
// Assert
hasSelectionChangedRaised.ShouldBeTrue();
}
[Fact]
public void OnEntryChanged_SavesValueToSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "TestConn",
Provider = ConnectionProvider.SqlServer,
Server = "localhost",
Database = "TestDb"
}
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
var onChangedCalled = false;
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => onChangedCalled = true, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act - Change a property on the selected connection
sut.SelectedConnection.Database = "NewDatabase";
// Assert
onChangedCalled.ShouldBeTrue();
_secureStoreManager.Received().SetSecret("TestConn", Arg.Any<string>());
}
[Fact]
public async Task DeleteConnectionCommand_RemovesFromSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "ToDelete" }
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
_dialogService.ShowConfirmationAsync(Arg.Any<string>(), Arg.Any<string>())
.Returns(true);
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.DeleteConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
_secureStoreManager.Received().RemoveSecret("ToDelete");
}
[Fact]
public void AddConnectionCommand_CreatesSecureStoreEntry()
{
// Arrange
var model = new ConnectionStringsSection();
_secureStoreManager.IsStoreOpen.Returns(true);
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Act
sut.AddConnectionCommand.Execute(null);
// Assert
_secureStoreManager.Received().SetSecret("NewConnection", string.Empty);
}
[Fact]
public async Task TestConnectionCommand_WithEmptyConnectionString_ShowsMessage()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry
{
Name = "EmptyConn",
Provider = ConnectionProvider.Generic,
RawConnectionString = ""
}
}
};
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
sut.SelectedConnection = sut.Connections[0];
// Act
sut.TestConnectionCommand.Execute(null);
await Task.Delay(100);
// Assert
await _dialogService.Received().ShowMessageAsync(
"Test Connection",
Arg.Is<string>(s => s.Contains("connection string is empty")));
await _connectionTestService.DidNotReceive().TestConnectionAsync(
Arg.Any<string>(),
Arg.Any<ConnectionProvider>(),
Arg.Any<CancellationToken>());
}
[Fact]
public void AvailableProviders_ContainsAllProviders()
{
// Assert
ConnectionStringsFormViewModel.AvailableProviders.Count.ShouldBe(
Enum.GetValues<ConnectionProvider>().Length);
ConnectionStringsFormViewModel.AvailableProviders.ShouldContain(ConnectionProvider.SqlServer);
ConnectionStringsFormViewModel.AvailableProviders.ShouldContain(ConnectionProvider.Oracle);
ConnectionStringsFormViewModel.AvailableProviders.ShouldContain(ConnectionProvider.Generic);
}
[Fact]
public void EncryptOptions_ContainsExpectedValues()
{
// Assert
ConnectionStringsFormViewModel.EncryptOptions.Count.ShouldBe(3);
ConnectionStringsFormViewModel.EncryptOptions.ShouldContain("True");
ConnectionStringsFormViewModel.EncryptOptions.ShouldContain("False");
ConnectionStringsFormViewModel.EncryptOptions.ShouldContain("Strict");
}
}