refactor(configmanager): rename UI project and split test projects

Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project
structure, and split the monolithic test project into Core.Tests,
Cli.Tests, and Ui.Tests to align with the source project organization.
This commit is contained in:
Joseph Doherty
2026-01-28 10:24:36 -05:00
parent 7c4781dfe3
commit 1fc7792cd1
131 changed files with 267 additions and 212 deletions
@@ -0,0 +1,241 @@
using JdeScoping.ConfigManager.Core.Services;
using JdeScoping.ConfigManager.Ui.ViewModels.Dialogs;
namespace JdeScoping.ConfigManager.Ui.Tests.ViewModels.Dialogs;
public class DiffPreviewDialogViewModelTests
{
[Fact]
public void Constructor_WithEmptyDiff_InitializesCorrectly()
{
// Arrange
var diff = new DiffResult
{
HasChanges = false,
Lines = [],
Insertions = 0,
Deletions = 0
};
// Act
var sut = new DiffPreviewDialogViewModel(diff);
// Assert
sut.Lines.Count.ShouldBe(0);
sut.HasChanges.ShouldBeFalse();
sut.Insertions.ShouldBe(0);
sut.Deletions.ShouldBe(0);
sut.Result.ShouldBeFalse();
}
[Fact]
public void Constructor_WithChanges_PopulatesLines()
{
// Arrange
var diff = new DiffResult
{
HasChanges = true,
Lines =
[
new DiffLine { OldLineNumber = 1, NewLineNumber = 1, Text = "unchanged line", Type = DiffLineType.Unchanged },
new DiffLine { OldLineNumber = 2, NewLineNumber = null, Text = "removed line", Type = DiffLineType.Removed },
new DiffLine { OldLineNumber = null, NewLineNumber = 2, Text = "added line", Type = DiffLineType.Added }
],
Insertions = 1,
Deletions = 1
};
// Act
var sut = new DiffPreviewDialogViewModel(diff);
// Assert
sut.Lines.Count.ShouldBe(3);
sut.HasChanges.ShouldBeTrue();
sut.Insertions.ShouldBe(1);
sut.Deletions.ShouldBe(1);
}
[Fact]
public void SaveCommand_SetsResultTrue_AndInvokesRequestClose()
{
// Arrange
var diff = CreateEmptyDiff();
var sut = new DiffPreviewDialogViewModel(diff);
var closeInvoked = false;
sut.RequestClose = () => closeInvoked = true;
// Act
sut.SaveCommand.Execute(null);
// Assert
sut.Result.ShouldBeTrue();
closeInvoked.ShouldBeTrue();
}
[Fact]
public void CancelCommand_SetsResultFalse_AndInvokesRequestClose()
{
// Arrange
var diff = CreateEmptyDiff();
var sut = new DiffPreviewDialogViewModel(diff);
var closeInvoked = false;
sut.RequestClose = () => closeInvoked = true;
// Act
sut.CancelCommand.Execute(null);
// Assert
sut.Result.ShouldBeFalse();
closeInvoked.ShouldBeTrue();
}
[Fact]
public void SaveCommand_DoesNotThrow_WhenRequestCloseIsNull()
{
// Arrange
var diff = CreateEmptyDiff();
var sut = new DiffPreviewDialogViewModel(diff);
sut.RequestClose = null;
// Act & Assert - Should not throw
Should.NotThrow(() => sut.SaveCommand.Execute(null));
sut.Result.ShouldBeTrue();
}
[Fact]
public void CancelCommand_DoesNotThrow_WhenRequestCloseIsNull()
{
// Arrange
var diff = CreateEmptyDiff();
var sut = new DiffPreviewDialogViewModel(diff);
sut.RequestClose = null;
// Act & Assert - Should not throw
Should.NotThrow(() => sut.CancelCommand.Execute(null));
sut.Result.ShouldBeFalse();
}
[Fact]
public void Constructor_ThrowsOnNullDiff()
{
// Act & Assert
Should.Throw<ArgumentNullException>(() => new DiffPreviewDialogViewModel(null!));
}
[Fact]
public void Result_InitialValue_IsFalse()
{
// Arrange
var diff = CreateEmptyDiff();
// Act
var sut = new DiffPreviewDialogViewModel(diff);
// Assert
sut.Result.ShouldBeFalse();
}
private static DiffResult CreateEmptyDiff()
{
return new DiffResult
{
HasChanges = false,
Lines = [],
Insertions = 0,
Deletions = 0
};
}
}
public class DiffLineViewModelTests
{
[Fact]
public void Constructor_UnchangedLine_SetsPropertiesCorrectly()
{
// Arrange
var line = new DiffLine
{
OldLineNumber = 5,
NewLineNumber = 5,
Text = "unchanged content",
Type = DiffLineType.Unchanged
};
// Act
var sut = new DiffLineViewModel(line);
// Assert
sut.OldLineNumber.ShouldBe("5");
sut.NewLineNumber.ShouldBe("5");
sut.Text.ShouldBe("unchanged content");
sut.Type.ShouldBe(DiffLineType.Unchanged);
sut.Background.ShouldBe("Transparent");
sut.BorderColor.ShouldBe("Transparent");
}
[Fact]
public void Constructor_AddedLine_SetsGreenStyling()
{
// Arrange
var line = new DiffLine
{
OldLineNumber = null,
NewLineNumber = 10,
Text = "new line",
Type = DiffLineType.Added
};
// Act
var sut = new DiffLineViewModel(line);
// Assert
sut.OldLineNumber.ShouldBe("");
sut.NewLineNumber.ShouldBe("10");
sut.Type.ShouldBe(DiffLineType.Added);
sut.Background.ShouldBe("#1A3DD68C");
sut.BorderColor.ShouldBe("#3DD68C");
}
[Fact]
public void Constructor_RemovedLine_SetsRedStyling()
{
// Arrange
var line = new DiffLine
{
OldLineNumber = 7,
NewLineNumber = null,
Text = "deleted line",
Type = DiffLineType.Removed
};
// Act
var sut = new DiffLineViewModel(line);
// Assert
sut.OldLineNumber.ShouldBe("7");
sut.NewLineNumber.ShouldBe("");
sut.Type.ShouldBe(DiffLineType.Removed);
sut.Background.ShouldBe("#1AFF6B6B");
sut.BorderColor.ShouldBe("#FF6B6B");
}
[Fact]
public void Constructor_NullLineNumbers_ReturnsEmptyStrings()
{
// Arrange
var line = new DiffLine
{
OldLineNumber = null,
NewLineNumber = null,
Text = "text",
Type = DiffLineType.Unchanged
};
// Act
var sut = new DiffLineViewModel(line);
// Assert
sut.OldLineNumber.ShouldBe("");
sut.NewLineNumber.ShouldBe("");
}
}
@@ -0,0 +1,181 @@
using JdeScoping.ConfigManager.Core.Constants;
using JdeScoping.ConfigManager.Ui.ViewModels.Dialogs;
namespace JdeScoping.ConfigManager.Ui.Tests.ViewModels.Dialogs;
public class NewStoreDialogViewModelTests
{
[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_WhenEmpty_IsValidReturnsFalse()
{
// Arrange
var sut = new NewStoreDialogViewModel
{
StorePath = "/path/to/store.secure",
KeyFilePath = ""
};
// Act & Assert
sut.IsValid.ShouldBeFalse();
}
[Fact]
public void KeyFilePath_WhenEmpty_ValidationErrorReturnsKeyFilePathRequired()
{
// Arrange
var sut = new NewStoreDialogViewModel
{
StorePath = "/path/to/store.secure",
KeyFilePath = ""
};
// Act & Assert
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
}
[Fact]
public void KeyFilePath_WhenProvided_IsValidReturnsTrue()
{
// Arrange
var sut = new NewStoreDialogViewModel
{
StorePath = "/path/to/store.secure",
KeyFilePath = "/path/to/key.key"
};
// Act & Assert
sut.IsValid.ShouldBeTrue();
sut.ValidationError.ShouldBeNull();
}
[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 Commands_AreInitialized()
{
// Arrange & Act
var sut = new NewStoreDialogViewModel();
// Assert
sut.BrowseStorePathCommand.ShouldNotBeNull();
sut.BrowseKeyFilePathCommand.ShouldNotBeNull();
sut.GenerateKeyFileCommand.ShouldNotBeNull();
}
}
@@ -0,0 +1,343 @@
using JdeScoping.ConfigManager.Core.Constants;
using JdeScoping.ConfigManager.Ui.ViewModels.Dialogs;
namespace JdeScoping.ConfigManager.Ui.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);
}
}
@@ -0,0 +1,170 @@
using JdeScoping.ConfigManager.Core.Constants;
using JdeScoping.ConfigManager.Ui.ViewModels.Dialogs;
namespace JdeScoping.ConfigManager.Ui.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 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_WhenEmpty_IsValidReturnsFalse()
{
// Arrange
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
{
KeyFilePath = ""
};
// Act & Assert
sut.IsValid.ShouldBeFalse();
}
[Fact]
public void KeyFilePath_WhenEmpty_ValidationErrorReturnsKeyFilePathRequired()
{
// Arrange
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
{
KeyFilePath = ""
};
// Act & Assert
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
}
[Fact]
public void KeyFilePath_WhenWhitespace_IsValidReturnsFalse()
{
// Arrange
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
{
KeyFilePath = " "
};
// Act & Assert
sut.IsValid.ShouldBeFalse();
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFilePathRequired);
}
[Fact]
public void KeyFilePath_WhenFileDoesNotExist_IsValidReturnsFalse()
{
// Arrange
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
{
KeyFilePath = "/nonexistent/path/to/key.key"
};
// Act & Assert
sut.IsValid.ShouldBeFalse();
}
[Fact]
public void KeyFilePath_WhenFileDoesNotExist_ValidationErrorReturnsKeyFileNotFound()
{
// Arrange
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure")
{
KeyFilePath = "/nonexistent/path/to/key.key"
};
// Act & Assert
sut.ValidationError.ShouldBe(SecureStoreStrings.KeyFileNotFound);
}
[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 BrowseKeyFilePathCommand_IsInitialized()
{
// Arrange & Act
var sut = new UnlockStoreDialogViewModel("/path/to/store.secure");
// Assert
sut.BrowseKeyFilePathCommand.ShouldNotBeNull();
}
}
@@ -0,0 +1,193 @@
using JdeScoping.ConfigManager.Core.Services;
using JdeScoping.ConfigManager.Ui.ViewModels.Dialogs;
namespace JdeScoping.ConfigManager.Ui.Tests.ViewModels.Dialogs;
public class ValidationResultsDialogViewModelTests
{
[Fact]
public void Constructor_WithEmptyResults_HasNoItems()
{
// Arrange
var appSettingsResult = new ValidationResult();
var pipelinesResult = new ValidationResult();
// Act
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
// Assert
sut.Items.Count.ShouldBe(0);
sut.ErrorCount.ShouldBe(0);
sut.WarningCount.ShouldBe(0);
sut.IsValid.ShouldBeTrue();
}
[Fact]
public void Constructor_WithErrors_PopulatesItemsCorrectly()
{
// Arrange
var appSettingsResult = new ValidationResult();
appSettingsResult.AddError("Missing connection string");
appSettingsResult.AddError("Invalid timeout value");
var pipelinesResult = new ValidationResult();
// Act
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
// Assert
sut.Items.Count.ShouldBe(2);
sut.ErrorCount.ShouldBe(2);
sut.WarningCount.ShouldBe(0);
sut.IsValid.ShouldBeFalse();
}
[Fact]
public void Constructor_WithWarnings_PopulatesItemsCorrectly()
{
// Arrange
var appSettingsResult = new ValidationResult();
appSettingsResult.AddWarning("Deprecated setting used");
var pipelinesResult = new ValidationResult();
pipelinesResult.AddWarning("Pipeline has no transformers");
// Act
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
// Assert
sut.Items.Count.ShouldBe(2);
sut.ErrorCount.ShouldBe(0);
sut.WarningCount.ShouldBe(2);
sut.IsValid.ShouldBeFalse(); // Both errors and warnings make it invalid
}
[Fact]
public void Constructor_WithMixedResults_PopulatesAllItems()
{
// Arrange
var appSettingsResult = new ValidationResult();
appSettingsResult.AddError("Error in appsettings");
appSettingsResult.AddWarning("Warning in appsettings");
var pipelinesResult = new ValidationResult();
pipelinesResult.AddError("Error in pipelines");
pipelinesResult.AddWarning("Warning in pipelines");
// Act
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
// Assert
sut.Items.Count.ShouldBe(4);
sut.ErrorCount.ShouldBe(2);
sut.WarningCount.ShouldBe(2);
sut.IsValid.ShouldBeFalse();
}
[Fact]
public void Constructor_SetsCorrectSourceOnItems()
{
// Arrange
var appSettingsResult = new ValidationResult();
appSettingsResult.AddError("App error");
var pipelinesResult = new ValidationResult();
pipelinesResult.AddError("Pipeline error");
// Act
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
// Assert
sut.Items.ShouldContain(i => i.Source == "appsettings.json" && i.Message == "App error");
sut.Items.ShouldContain(i => i.Source == "pipelines.json" && i.Message == "Pipeline error");
}
[Fact]
public void CloseCommand_InvokesRequestClose()
{
// Arrange
var appSettingsResult = new ValidationResult();
var pipelinesResult = new ValidationResult();
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
var closeInvoked = false;
sut.RequestClose = () => closeInvoked = true;
// Act
sut.CloseCommand.Execute(null);
// Assert
closeInvoked.ShouldBeTrue();
}
[Fact]
public void CloseCommand_DoesNotThrow_WhenRequestCloseIsNull()
{
// Arrange
var appSettingsResult = new ValidationResult();
var pipelinesResult = new ValidationResult();
var sut = new ValidationResultsDialogViewModel(appSettingsResult, pipelinesResult);
sut.RequestClose = null;
// Act & Assert - Should not throw
Should.NotThrow(() => sut.CloseCommand.Execute(null));
}
[Fact]
public void Constructor_ThrowsOnNullAppSettingsResult()
{
// Arrange
var pipelinesResult = new ValidationResult();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ValidationResultsDialogViewModel(null!, pipelinesResult));
}
[Fact]
public void Constructor_ThrowsOnNullPipelinesResult()
{
// Arrange
var appSettingsResult = new ValidationResult();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new ValidationResultsDialogViewModel(appSettingsResult, null!));
}
}
public class ValidationItemViewModelTests
{
[Fact]
public void Constructor_SetsPropertiesCorrectly()
{
// Act
var sut = new ValidationItemViewModel("Test message", "test.json", ValidationItemType.Error);
// Assert
sut.Message.ShouldBe("Test message");
sut.Source.ShouldBe("test.json");
sut.Type.ShouldBe(ValidationItemType.Error);
}
[Fact]
public void Constructor_ErrorType_SetsErrorStyling()
{
// Act
var sut = new ValidationItemViewModel("Error", "test.json", ValidationItemType.Error);
// Assert
sut.Icon.ShouldBe("\u2717"); // X mark
sut.IconColor.ShouldBe("#FF6B6B");
sut.Background.ShouldBe("#1AFF6B6B");
sut.BorderColor.ShouldBe("#FF6B6B");
}
[Fact]
public void Constructor_WarningType_SetsWarningStyling()
{
// Act
var sut = new ValidationItemViewModel("Warning", "test.json", ValidationItemType.Warning);
// Assert
sut.Icon.ShouldBe("\u26A0"); // Warning sign
sut.IconColor.ShouldBe("#FFB84D");
sut.Background.ShouldBe("#1AFFB84D");
sut.BorderColor.ShouldBe("#FFB84D");
}
}