feat(configmanager): integrate SecureStore for credential management

Add SecureStore integration to ConfigManager for secure handling of connection
strings and sensitive configuration values. Includes store/secret management
UI, encrypted .store file support, and comprehensive test coverage.
This commit is contained in:
Joseph Doherty
2026-01-20 02:51:16 -05:00
parent d49330e697
commit 94d5a864e0
44 changed files with 6220 additions and 4 deletions
@@ -218,4 +218,180 @@ public class TreeNodeViewModelTests
// Assert
propertyChangedRaised.ShouldBeFalse();
}
#region SecureStore Node Type Tests
[Theory]
[InlineData(TreeNodeType.SecureStoresFolder)]
[InlineData(TreeNodeType.SecureStore)]
[InlineData(TreeNodeType.Secret)]
public void Constructor_WithSecureStoreNodeTypes_SetsNodeTypeCorrectly(TreeNodeType nodeType)
{
// Arrange & Act
var sut = new TreeNodeViewModel("Test", "icon", nodeType);
// Assert
sut.NodeType.ShouldBe(nodeType);
}
[Fact]
public void IsUnlocked_DefaultsToFalse()
{
// Arrange & Act
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Assert
sut.IsUnlocked.ShouldBeFalse();
}
[Fact]
public void IsUnlocked_WhenSet_RaisesPropertyChanged()
{
// Arrange
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
var propertyChangedRaised = false;
sut.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TreeNodeViewModel.IsUnlocked))
propertyChangedRaised = true;
};
// Act
sut.IsUnlocked = true;
// Assert
propertyChangedRaised.ShouldBeTrue();
}
[Fact]
public void IsUnlocked_WhenSet_RaisesPropertyChangedForLockIcon()
{
// Arrange
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
var lockIconChanged = false;
sut.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TreeNodeViewModel.LockIcon))
lockIconChanged = true;
};
// Act
sut.IsUnlocked = true;
// Assert
lockIconChanged.ShouldBeTrue();
}
[Fact]
public void LockIcon_WhenLocked_ReturnsLockedIcon()
{
// Arrange & Act
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Assert
sut.LockIcon.ShouldBe("🔒");
}
[Fact]
public void LockIcon_WhenUnlocked_ReturnsUnlockedIcon()
{
// Arrange
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Act
sut.IsUnlocked = true;
// Assert
sut.LockIcon.ShouldBe("🔓");
}
[Fact]
public void IsLocked_WhenUnlocked_ReturnsFalse()
{
// Arrange
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Act
sut.IsUnlocked = true;
// Assert
sut.IsLocked.ShouldBeFalse();
}
[Fact]
public void IsLocked_WhenLocked_ReturnsTrue()
{
// Arrange & Act
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Assert
sut.IsLocked.ShouldBeTrue();
}
[Fact]
public void IsUnlocked_WhenSet_RaisesPropertyChangedForIsLocked()
{
// Arrange
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
var isLockedChanged = false;
sut.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TreeNodeViewModel.IsLocked))
isLockedChanged = true;
};
// Act
sut.IsUnlocked = true;
// Assert
isLockedChanged.ShouldBeTrue();
}
[Fact]
public void StorePath_CanBeSetViaInitializer()
{
// Arrange & Act
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore)
{
StorePath = "/path/to/store.secrets"
};
// Assert
sut.StorePath.ShouldBe("/path/to/store.secrets");
}
[Fact]
public void StorePath_DefaultsToNull()
{
// Arrange & Act
var sut = new TreeNodeViewModel("Store", "🔒", TreeNodeType.SecureStore);
// Assert
sut.StorePath.ShouldBeNull();
}
[Fact]
public void SecretKey_CanBeSetViaInitializer()
{
// Arrange & Act
var sut = new TreeNodeViewModel("MySecret", "🔑", TreeNodeType.Secret)
{
SecretKey = "ConnectionStrings:Database"
};
// Assert
sut.SecretKey.ShouldBe("ConnectionStrings:Database");
}
[Fact]
public void SecretKey_DefaultsToNull()
{
// Arrange & Act
var sut = new TreeNodeViewModel("MySecret", "🔑", TreeNodeType.Secret);
// Assert
sut.SecretKey.ShouldBeNull();
}
#endregion
}