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:
+399
@@ -0,0 +1,399 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
public class NewStoreDialogViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_DefaultsToUseKeyFile()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
|
||||
// Assert
|
||||
sut.UseKeyFile.ShouldBeTrue();
|
||||
sut.UsePassword.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "",
|
||||
KeyFilePath = "/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenEmpty_ValidationErrorReturnsStorePathRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "",
|
||||
KeyFilePath = "/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.StorePathRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenWhitespace_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = " ",
|
||||
KeyFilePath = "/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.StorePathRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndEmpty_ValidationErrorReturnsKeyFilePathRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndProvided_IsValidReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = "/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UsePassword = true,
|
||||
Password = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndEmpty_ValidationErrorReturnsPasswordRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UsePassword = true,
|
||||
Password = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.PasswordRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfirmPassword_WhenRequiredAndDoesNotMatch_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UsePassword = true,
|
||||
Password = "password123",
|
||||
ConfirmPassword = "differentPassword"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfirmPassword_WhenRequiredAndDoesNotMatch_ValidationErrorReturnsPasswordsDoNotMatch()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UsePassword = true,
|
||||
Password = "password123",
|
||||
ConfirmPassword = "differentPassword"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.PasswordsDoNotMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndMatchesConfirmPassword_IsValidReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure",
|
||||
UsePassword = true,
|
||||
Password = "password123",
|
||||
ConfirmPassword = "password123"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseKeyFile_WhenSetToTrue_SetsUsePasswordToFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
UsePassword = true
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UseKeyFile = true;
|
||||
|
||||
// Assert
|
||||
sut.UseKeyFile.ShouldBeTrue();
|
||||
sut.UsePassword.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsePassword_WhenSetToTrue_SetsUseKeyFileToFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
UseKeyFile = true
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UsePassword = true;
|
||||
|
||||
// Assert
|
||||
sut.UsePassword.ShouldBeTrue();
|
||||
sut.UseKeyFile.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValid_WhenNeitherKeyFileNorPasswordSelected_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel
|
||||
{
|
||||
StorePath = "/path/to/store.secure"
|
||||
};
|
||||
// Manually set both to false (shouldn't happen in UI, but test edge case)
|
||||
sut.UseKeyFile = false;
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.StorePath))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.StorePath = "/new/path";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenChanged_RaisesIsValidPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var isValidPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.IsValid))
|
||||
isValidPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.StorePath = "/new/path";
|
||||
|
||||
// Assert
|
||||
isValidPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_WhenChanged_RaisesValidationErrorPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var validationErrorPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.ValidationError))
|
||||
validationErrorPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.StorePath = "/new/path";
|
||||
|
||||
// Assert
|
||||
validationErrorPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.KeyFilePath))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.KeyFilePath = "/new/key/path";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.Password))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Password = "newpassword";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfirmPassword_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.ConfirmPassword))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.ConfirmPassword = "newpassword";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseKeyFile_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel { UseKeyFile = false };
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.UseKeyFile))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UseKeyFile = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsePassword_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(NewStoreDialogViewModel.UsePassword))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UsePassword = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Commands_AreInitialized()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new NewStoreDialogViewModel();
|
||||
|
||||
// Assert
|
||||
sut.BrowseStorePathCommand.ShouldNotBeNull();
|
||||
sut.BrowseKeyFilePathCommand.ShouldNotBeNull();
|
||||
sut.GenerateKeyFileCommand.ShouldNotBeNull();
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
public class SecretEditDialogViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultConstructor_SetsIsNewSecretToTrue()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
|
||||
// Assert
|
||||
sut.IsNewSecret.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructor_SetsKeyToEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
|
||||
// Assert
|
||||
sut.Key.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructor_SetsValueToEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
|
||||
// Assert
|
||||
sut.Value.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EditConstructor_SetsKeyFromParameter()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel("myKey", "myValue");
|
||||
|
||||
// Assert
|
||||
sut.Key.ShouldBe("myKey");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EditConstructor_SetsValueFromParameter()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel("myKey", "myValue");
|
||||
|
||||
// Assert
|
||||
sut.Value.ShouldBe("myValue");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EditConstructor_SetsIsNewSecretToFalse()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretEditDialogViewModel("myKey", "myValue");
|
||||
|
||||
// Assert
|
||||
sut.IsNewSecret.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenEmpty_ValidationErrorReturnsKeyRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenWhitespace_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = " "
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenProvided_IsValidReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = "validKey"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsKeyEditable_WhenIsNewSecretIsTrue_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
|
||||
// Act & Assert
|
||||
sut.IsKeyEditable.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsKeyEditable_WhenIsNewSecretIsFalse_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel("existingKey", "existingValue");
|
||||
|
||||
// Act & Assert
|
||||
sut.IsKeyEditable.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DialogTitle_WhenIsNewSecretIsTrue_ReturnsAddSecret()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
|
||||
// Act & Assert
|
||||
sut.DialogTitle.ShouldBe("Add Secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DialogTitle_WhenIsNewSecretIsFalse_ReturnsEditSecret()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel("existingKey", "existingValue");
|
||||
|
||||
// Act & Assert
|
||||
sut.DialogTitle.ShouldBe("Edit Secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNewSecret_WhenChanged_RaisesIsKeyEditablePropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var isKeyEditablePropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.IsKeyEditable))
|
||||
isKeyEditablePropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.IsNewSecret = false;
|
||||
|
||||
// Assert
|
||||
isKeyEditablePropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNewSecret_WhenChanged_RaisesDialogTitlePropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var dialogTitlePropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.DialogTitle))
|
||||
dialogTitlePropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.IsNewSecret = false;
|
||||
|
||||
// Assert
|
||||
dialogTitlePropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.Key))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Key = "newKey";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenChanged_RaisesIsValidPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var isValidPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.IsValid))
|
||||
isValidPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Key = "newKey";
|
||||
|
||||
// Assert
|
||||
isValidPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenChanged_RaisesValidationErrorPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var validationErrorPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.ValidationError))
|
||||
validationErrorPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Key = "newKey";
|
||||
|
||||
// Assert
|
||||
validationErrorPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.Value))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Value = "newValue";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_CanBeEmpty_IsValidStillReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = "validKey",
|
||||
Value = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_CanBeNull_IsValidStillReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel
|
||||
{
|
||||
Key = "validKey",
|
||||
Value = null!
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNewSecret_WhenSetToSameValue_DoesNotRaisePropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel();
|
||||
var propertyChangedCount = 0;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.IsNewSecret))
|
||||
propertyChangedCount++;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.IsNewSecret = true; // Same value as default
|
||||
|
||||
// Assert
|
||||
propertyChangedCount.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_WhenSetToSameValue_DoesNotRaisePropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretEditDialogViewModel { Key = "testKey" };
|
||||
var propertyChangedCount = 0;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretEditDialogViewModel.Key))
|
||||
propertyChangedCount++;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Key = "testKey"; // Same value
|
||||
|
||||
// Assert
|
||||
propertyChangedCount.ShouldBe(0);
|
||||
}
|
||||
}
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
public class UnlockStoreDialogViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_SetsStorePathFromParameter()
|
||||
{
|
||||
// Arrange
|
||||
var storePath = "/path/to/store.secure";
|
||||
|
||||
// Act
|
||||
var sut = new UnlockStoreDialogViewModel(storePath);
|
||||
|
||||
// Assert
|
||||
sut.StorePath.ShouldBe(storePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullStorePath()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() => new UnlockStoreDialogViewModel(null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_DefaultsToUseKeyFile()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
|
||||
// Assert
|
||||
sut.UseKeyFile.ShouldBeTrue();
|
||||
sut.UsePassword.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StorePath_IsReadOnly()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
|
||||
// Assert - StorePath property has no setter, verified by checking it returns what was passed
|
||||
sut.StorePath.ShouldBe("/path/to/store.secure");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndEmpty_ValidationErrorReturnsKeyFilePathRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndWhitespace_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = " "
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndFileDoesNotExist_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = "/nonexistent/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenRequiredAndFileDoesNotExist_ValidationErrorReturnsKeyFileNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true,
|
||||
KeyFilePath = "/nonexistent/path/to/key.key"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFileNotFound);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndEmpty_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UsePassword = true,
|
||||
Password = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndEmpty_ValidationErrorReturnsPasswordRequired()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UsePassword = true,
|
||||
Password = ""
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.PasswordRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndWhitespace_IsValidReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UsePassword = true,
|
||||
Password = " "
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
sut.ValidationError.ShouldBe(SecureStoreStrings.PasswordRequired);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenRequiredAndProvided_IsValidReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UsePassword = true,
|
||||
Password = "password123"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeTrue();
|
||||
sut.ValidationError.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseKeyFile_WhenSetToTrue_SetsUsePasswordToFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UsePassword = true
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UseKeyFile = true;
|
||||
|
||||
// Assert
|
||||
sut.UseKeyFile.ShouldBeTrue();
|
||||
sut.UsePassword.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsePassword_WhenSetToTrue_SetsUseKeyFileToFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
|
||||
{
|
||||
UseKeyFile = true
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UsePassword = true;
|
||||
|
||||
// Assert
|
||||
sut.UsePassword.ShouldBeTrue();
|
||||
sut.UseKeyFile.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValid_WhenNeitherKeyFileNorPasswordSelected_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
// Manually set both to false (shouldn't happen in UI, but test edge case)
|
||||
sut.UseKeyFile = false;
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValid.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.KeyFilePath))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.KeyFilePath = "/new/key/path";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenChanged_RaisesIsValidPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
var isValidPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.IsValid))
|
||||
isValidPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.KeyFilePath = "/new/key/path";
|
||||
|
||||
// Assert
|
||||
isValidPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KeyFilePath_WhenChanged_RaisesValidationErrorPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
var validationErrorPropertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.ValidationError))
|
||||
validationErrorPropertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.KeyFilePath = "/new/key/path";
|
||||
|
||||
// Assert
|
||||
validationErrorPropertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.Password))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Password = "newpassword";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseKeyFile_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure") { UseKeyFile = false };
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.UseKeyFile))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UseKeyFile = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsePassword_WhenChanged_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(UnlockStoreDialogViewModel.UsePassword))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.UsePassword = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BrowseKeyFilePathCommand_IsInitialized()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
|
||||
|
||||
// Assert
|
||||
sut.BrowseKeyFilePathCommand.ShouldNotBeNull();
|
||||
}
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
public class SecretFormViewModelTests
|
||||
{
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
public SecretFormViewModelTests()
|
||||
{
|
||||
_clipboardService = Substitute.For<IClipboardService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_SetsPropertiesCorrectly()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret-value-123",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.Key.ShouldBe("API_KEY");
|
||||
sut.Value.ShouldBe("secret-value-123");
|
||||
sut.IsValueVisible.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithNullValue_SetsEmptyString()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
null!,
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.Value.ShouldBe(string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_Setter_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
string? changedValue = null;
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"initial",
|
||||
_clipboardService,
|
||||
v => changedValue = v,
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.Value = "new-value";
|
||||
|
||||
// Assert
|
||||
changedValue.ShouldBe("new-value");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_Setter_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"initial",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretFormViewModel.Value))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Value = "new-value";
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_Setter_RaisesDisplayValuePropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"initial",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
var displayValueChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretFormViewModel.DisplayValue))
|
||||
displayValueChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.Value = "new-value";
|
||||
|
||||
// Assert
|
||||
displayValueChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValueVisible_Toggle_Works()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert - Initial state
|
||||
sut.IsValueVisible.ShouldBeFalse();
|
||||
|
||||
// Act - Toggle on
|
||||
sut.IsValueVisible = true;
|
||||
sut.IsValueVisible.ShouldBeTrue();
|
||||
|
||||
// Act - Toggle off
|
||||
sut.IsValueVisible = false;
|
||||
sut.IsValueVisible.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValueVisible_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretFormViewModel.IsValueVisible))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.IsValueVisible = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValueVisible_RaisesDisplayValueAndVisibilityButtonTextPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
var displayValueChangedRaised = false;
|
||||
var visibilityButtonTextChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecretFormViewModel.DisplayValue))
|
||||
displayValueChangedRaised = true;
|
||||
if (e.PropertyName == nameof(SecretFormViewModel.VisibilityButtonText))
|
||||
visibilityButtonTextChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.IsValueVisible = true;
|
||||
|
||||
// Assert
|
||||
displayValueChangedRaised.ShouldBeTrue();
|
||||
visibilityButtonTextChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayValue_ShowsMaskedValue_WhenNotVisible()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret123",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.IsValueVisible.ShouldBeFalse();
|
||||
sut.DisplayValue.ShouldBe("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"); // 9 bullet points
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayValue_ShowsActualValue_WhenVisible()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret123",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.IsValueVisible = true;
|
||||
|
||||
// Assert
|
||||
sut.DisplayValue.ShouldBe("secret123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayValue_LimitsMaskedLength_ToTwentyCharacters()
|
||||
{
|
||||
// Arrange
|
||||
var longValue = new string('x', 50);
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
longValue,
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.DisplayValue.Length.ShouldBe(20);
|
||||
sut.DisplayValue.ShouldBe(new string('\u2022', 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayValue_HandlesEmptyValue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.DisplayValue.ShouldBe("");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisibilityButtonText_ShowsShow_WhenHidden()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.VisibilityButtonText.ShouldBe("Show");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisibilityButtonText_ShowsHide_WhenVisible()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.IsValueVisible = true;
|
||||
|
||||
// Assert
|
||||
sut.VisibilityButtonText.ShouldBe("Hide");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleVisibilityCommand_TogglesVisibility()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert - Toggle on
|
||||
sut.ToggleVisibilityCommand.Execute(null);
|
||||
sut.IsValueVisible.ShouldBeTrue();
|
||||
|
||||
// Act & Assert - Toggle off
|
||||
sut.ToggleVisibilityCommand.Execute(null);
|
||||
sut.IsValueVisible.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CopyToClipboardCommand_CopiesValueToClipboard()
|
||||
{
|
||||
// Arrange
|
||||
_clipboardService.SetTextAsync(Arg.Any<string>()).Returns(Task.CompletedTask);
|
||||
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret-to-copy",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.CopyToClipboardCommand.Execute(null);
|
||||
|
||||
// Small delay to allow async command to complete
|
||||
await Task.Delay(50);
|
||||
|
||||
// Assert
|
||||
await _clipboardService.Received(1).SetTextAsync("secret-to-copy");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteCommand_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
var deleteCalled = false;
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"secret",
|
||||
_clipboardService,
|
||||
_ => { },
|
||||
() => deleteCalled = true);
|
||||
|
||||
// Act
|
||||
sut.DeleteCommand.Execute(null);
|
||||
|
||||
// Assert
|
||||
deleteCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullKey()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecretFormViewModel(null!, "value", _clipboardService, _ => { }, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullClipboardService()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecretFormViewModel("key", "value", null!, _ => { }, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullValueChangedCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecretFormViewModel("key", "value", _clipboardService, null!, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullDeleteCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecretFormViewModel("key", "value", _clipboardService, _ => { }, null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Key_IsReadOnly()
|
||||
{
|
||||
// Assert - Verify Key property is get-only
|
||||
var keyProperty = typeof(SecretFormViewModel).GetProperty(nameof(SecretFormViewModel.Key));
|
||||
keyProperty!.CanWrite.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Value_DoesNotInvokeCallback_WhenValueUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var callbackCount = 0;
|
||||
var sut = new SecretFormViewModel(
|
||||
"API_KEY",
|
||||
"same-value",
|
||||
_clipboardService,
|
||||
_ => callbackCount++,
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.Value = "same-value"; // Same as initial value
|
||||
|
||||
// Assert
|
||||
callbackCount.ShouldBe(0);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
public class SecureStoreLockedFormViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_SetsPropertiesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var lastModified = DateTime.Now;
|
||||
|
||||
// Act
|
||||
var sut = new SecureStoreLockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
lastModified,
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.StoreName.ShouldBe("test.secrets");
|
||||
sut.StorePath.ShouldBe("/path/to/test.secrets");
|
||||
sut.LastModified.ShouldBe(lastModified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithNullLastModified_SetsNullLastModified()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecureStoreLockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
null,
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.LastModified.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnlockCommand_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
var unlockCalled = false;
|
||||
var sut = new SecureStoreLockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
null,
|
||||
() => unlockCalled = true);
|
||||
|
||||
// Act
|
||||
sut.UnlockCommand.Execute(null);
|
||||
|
||||
// Assert
|
||||
unlockCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnlockCommand_CanExecute_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreLockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
null,
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.UnlockCommand.CanExecute(null).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullStoreName()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreLockedFormViewModel(null!, "/path", null, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullStorePath()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreLockedFormViewModel("test", null!, null, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullUnlockCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreLockedFormViewModel("test", "/path", null, null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Properties_AreReadOnly()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreLockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
DateTime.Now,
|
||||
() => { });
|
||||
|
||||
// Assert - Verify properties are get-only (no setters)
|
||||
var storeNameProperty = typeof(SecureStoreLockedFormViewModel).GetProperty(nameof(SecureStoreLockedFormViewModel.StoreName));
|
||||
var storePathProperty = typeof(SecureStoreLockedFormViewModel).GetProperty(nameof(SecureStoreLockedFormViewModel.StorePath));
|
||||
var lastModifiedProperty = typeof(SecureStoreLockedFormViewModel).GetProperty(nameof(SecureStoreLockedFormViewModel.LastModified));
|
||||
|
||||
storeNameProperty!.CanWrite.ShouldBeFalse();
|
||||
storePathProperty!.CanWrite.ShouldBeFalse();
|
||||
lastModifiedProperty!.CanWrite.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
public class SecureStoreUnlockedFormViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_SetsPropertiesCorrectly()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
5,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.StoreName.ShouldBe("test.secrets");
|
||||
sut.StorePath.ShouldBe("/path/to/test.secrets");
|
||||
sut.SecretCount.ShouldBe(5);
|
||||
sut.HasUnsavedChanges.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithUnsavedChanges_SetsHasUnsavedChanges()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
3,
|
||||
true,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Assert
|
||||
sut.HasUnsavedChanges.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasUnsavedChanges_RaisesPropertyChanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecureStoreUnlockedFormViewModel.HasUnsavedChanges))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.HasUnsavedChanges = true;
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasUnsavedChanges_DoesNotRaisePropertyChanged_WhenValueUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
var propertyChangedRaised = false;
|
||||
sut.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(SecureStoreUnlockedFormViewModel.HasUnsavedChanges))
|
||||
propertyChangedRaised = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
sut.HasUnsavedChanges = false; // Same as initial value
|
||||
|
||||
// Assert
|
||||
propertyChangedRaised.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LockCommand_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
var lockCalled = false;
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => lockCalled = true,
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.LockCommand.Execute(null);
|
||||
|
||||
// Assert
|
||||
lockCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSecretCommand_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
var addSecretCalled = false;
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => addSecretCalled = true,
|
||||
() => { });
|
||||
|
||||
// Act
|
||||
sut.AddSecretCommand.Execute(null);
|
||||
|
||||
// Assert
|
||||
addSecretCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCommand_InvokesCallback()
|
||||
{
|
||||
// Arrange
|
||||
var saveCalled = false;
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
true, // Must have unsaved changes for save to be enabled
|
||||
() => { },
|
||||
() => { },
|
||||
() => saveCalled = true);
|
||||
|
||||
// Act
|
||||
sut.SaveCommand.Execute(null);
|
||||
|
||||
// Assert
|
||||
saveCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCommand_CanExecute_ReturnsFalse_WhenNoUnsavedChanges()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.SaveCommand.CanExecute(null).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCommand_CanExecute_ReturnsTrue_WhenHasUnsavedChanges()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
true,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.SaveCommand.CanExecute(null).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCommand_CanExecute_Updates_WhenHasUnsavedChangesChanges()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Initial state - can't save
|
||||
sut.SaveCommand.CanExecute(null).ShouldBeFalse();
|
||||
|
||||
// Act
|
||||
sut.HasUnsavedChanges = true;
|
||||
|
||||
// Assert
|
||||
sut.SaveCommand.CanExecute(null).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCommand_RaisesCanExecuteChanged_WhenHasUnsavedChangesChanges()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
var canExecuteChangedRaised = false;
|
||||
sut.SaveCommand.CanExecuteChanged += (s, e) => canExecuteChangedRaised = true;
|
||||
|
||||
// Act
|
||||
sut.HasUnsavedChanges = true;
|
||||
|
||||
// Assert
|
||||
canExecuteChangedRaised.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LockCommand_CanExecute_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.LockCommand.CanExecute(null).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSecretCommand_CanExecute_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SecureStoreUnlockedFormViewModel(
|
||||
"test.secrets",
|
||||
"/path/to/test.secrets",
|
||||
0,
|
||||
false,
|
||||
() => { },
|
||||
() => { },
|
||||
() => { });
|
||||
|
||||
// Act & Assert
|
||||
sut.AddSecretCommand.CanExecute(null).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullStoreName()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreUnlockedFormViewModel(null!, "/path", 0, false, () => { }, () => { }, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullStorePath()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreUnlockedFormViewModel("test", null!, 0, false, () => { }, () => { }, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullLockCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreUnlockedFormViewModel("test", "/path", 0, false, null!, () => { }, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullAddSecretCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreUnlockedFormViewModel("test", "/path", 0, false, () => { }, null!, () => { }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ThrowsOnNullSaveCallback()
|
||||
{
|
||||
// Act & Assert
|
||||
Should.Throw<ArgumentNullException>(() =>
|
||||
new SecureStoreUnlockedFormViewModel("test", "/path", 0, false, () => { }, () => { }, null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadOnlyProperties_CannotBeModified()
|
||||
{
|
||||
// Assert - Verify StoreName, StorePath, and SecretCount are get-only
|
||||
var storeNameProperty = typeof(SecureStoreUnlockedFormViewModel).GetProperty(nameof(SecureStoreUnlockedFormViewModel.StoreName));
|
||||
var storePathProperty = typeof(SecureStoreUnlockedFormViewModel).GetProperty(nameof(SecureStoreUnlockedFormViewModel.StorePath));
|
||||
var secretCountProperty = typeof(SecureStoreUnlockedFormViewModel).GetProperty(nameof(SecureStoreUnlockedFormViewModel.SecretCount));
|
||||
|
||||
storeNameProperty!.CanWrite.ShouldBeFalse();
|
||||
storePathProperty!.CanWrite.ShouldBeFalse();
|
||||
secretCountProperty!.CanWrite.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -15,6 +16,8 @@ public class MainWindowViewModelTests
|
||||
private readonly IBackupService _backupService;
|
||||
private readonly IAutoDiscoveryService _autoDiscoveryService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISecureStoreManager _secureStoreManager;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly ILogger<MainWindowViewModel> _logger;
|
||||
|
||||
public MainWindowViewModelTests()
|
||||
@@ -25,6 +28,8 @@ public class MainWindowViewModelTests
|
||||
_backupService = Substitute.For<IBackupService>();
|
||||
_autoDiscoveryService = Substitute.For<IAutoDiscoveryService>();
|
||||
_dialogService = Substitute.For<IDialogService>();
|
||||
_secureStoreManager = Substitute.For<ISecureStoreManager>();
|
||||
_clipboardService = Substitute.For<IClipboardService>();
|
||||
_logger = Substitute.For<ILogger<MainWindowViewModel>>();
|
||||
|
||||
_validationService.ValidateAppSettings(Arg.Any<ConfigModel>())
|
||||
@@ -274,7 +279,7 @@ public class MainWindowViewModelTests
|
||||
sut.LoadConfigForTesting(config, null);
|
||||
|
||||
// Assert
|
||||
sut.TreeNodes.Count.ShouldBe(2); // Settings and Pipelines folders
|
||||
sut.TreeNodes.Count.ShouldBe(3); // Settings, Pipelines, and Secure Stores folders
|
||||
sut.TreeNodes[0].Name.ShouldBe("Settings");
|
||||
sut.TreeNodes[0].Children.Count.ShouldBe(6); // DataSync, DataAccess, Auth, Ldap, Search, ExcelExport
|
||||
}
|
||||
@@ -311,6 +316,8 @@ public class MainWindowViewModelTests
|
||||
_backupService,
|
||||
_autoDiscoveryService,
|
||||
_dialogService,
|
||||
_secureStoreManager,
|
||||
_clipboardService,
|
||||
_logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user