94d5a864e0
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.
398 lines
10 KiB
C#
398 lines
10 KiB
C#
using JdeScoping.ConfigManager.ViewModels;
|
|
|
|
namespace JdeScoping.ConfigManager.Tests.ViewModels;
|
|
|
|
public class TreeNodeViewModelTests
|
|
{
|
|
[Fact]
|
|
public void Constructor_SetsProperties()
|
|
{
|
|
// Arrange & Act
|
|
var node = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection);
|
|
|
|
// Assert
|
|
node.Name.ShouldBe("DataSync");
|
|
node.Icon.ShouldBe("⟳");
|
|
node.NodeType.ShouldBe(TreeNodeType.SettingsSection);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsModified_WhenSet_RaisesPropertyChanged()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
var propertyChangedRaised = false;
|
|
node.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsModified))
|
|
propertyChangedRaised = true;
|
|
};
|
|
|
|
// Act
|
|
node.IsModified = true;
|
|
|
|
// Assert
|
|
propertyChangedRaised.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsExpanded_WhenSet_RaisesPropertyChanged()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
var propertyChangedRaised = false;
|
|
node.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsExpanded))
|
|
propertyChangedRaised = true;
|
|
};
|
|
|
|
// Act
|
|
node.IsExpanded = true;
|
|
|
|
// Assert
|
|
propertyChangedRaised.ShouldBeTrue();
|
|
node.IsExpanded.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsSelected_WhenSet_RaisesPropertyChanged()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
var propertyChangedRaised = false;
|
|
node.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsSelected))
|
|
propertyChangedRaised = true;
|
|
};
|
|
|
|
// Act
|
|
node.IsSelected = true;
|
|
|
|
// Assert
|
|
propertyChangedRaised.ShouldBeTrue();
|
|
node.IsSelected.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationState_WhenSetToValid_StatusIconIsCheckmark()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
|
|
// Act
|
|
node.ValidationState = ValidationState.Valid;
|
|
|
|
// Assert
|
|
node.StatusIcon.ShouldBe("✓");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationState_WhenSetToWarning_StatusIconIsWarning()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
|
|
// Act
|
|
node.ValidationState = ValidationState.Warning;
|
|
|
|
// Assert
|
|
node.StatusIcon.ShouldBe("⚠");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationState_WhenSetToError_StatusIconIsX()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
|
|
// Act
|
|
node.ValidationState = ValidationState.Error;
|
|
|
|
// Assert
|
|
node.StatusIcon.ShouldBe("✗");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationState_WhenSetToUnknown_StatusIconIsEmpty()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
|
|
// Act
|
|
node.ValidationState = ValidationState.Unknown;
|
|
|
|
// Assert
|
|
node.StatusIcon.ShouldBe("");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationState_WhenChanged_RaisesPropertyChangedForStatusIcon()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
var statusIconPropertyChanged = false;
|
|
node.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(TreeNodeViewModel.StatusIcon))
|
|
statusIconPropertyChanged = true;
|
|
};
|
|
|
|
// Act
|
|
node.ValidationState = ValidationState.Valid;
|
|
|
|
// Assert
|
|
statusIconPropertyChanged.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void SectionKey_CanBeSetViaInitializer()
|
|
{
|
|
// Arrange & Act
|
|
var node = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection)
|
|
{
|
|
SectionKey = "DataSync"
|
|
};
|
|
|
|
// Assert
|
|
node.SectionKey.ShouldBe("DataSync");
|
|
}
|
|
|
|
[Fact]
|
|
public void Children_IsInitialized()
|
|
{
|
|
// Arrange & Act
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
|
|
// Assert
|
|
node.Children.ShouldNotBeNull();
|
|
node.Children.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void Children_CanAddChildNodes()
|
|
{
|
|
// Arrange
|
|
var parent = new TreeNodeViewModel("Settings", "📁", TreeNodeType.Folder);
|
|
var child = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection);
|
|
|
|
// Act
|
|
parent.Children.Add(child);
|
|
|
|
// Assert
|
|
parent.Children.Count.ShouldBe(1);
|
|
parent.Children[0].ShouldBe(child);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(TreeNodeType.Folder)]
|
|
[InlineData(TreeNodeType.SettingsSection)]
|
|
[InlineData(TreeNodeType.Pipeline)]
|
|
public void NodeType_CanBeSetForAllTypes(TreeNodeType nodeType)
|
|
{
|
|
// Arrange & Act
|
|
var node = new TreeNodeViewModel("Test", "📁", nodeType);
|
|
|
|
// Assert
|
|
node.NodeType.ShouldBe(nodeType);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsModified_WhenSetToSameValue_DoesNotRaisePropertyChanged()
|
|
{
|
|
// Arrange
|
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
|
node.IsModified = true;
|
|
|
|
var propertyChangedRaised = false;
|
|
node.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsModified))
|
|
propertyChangedRaised = true;
|
|
};
|
|
|
|
// Act
|
|
node.IsModified = true; // Same value
|
|
|
|
// 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
|
|
}
|