diff --git a/src/c#/DrivelutionTest/Core/DriverUpdaterFactoryTests.cs b/src/c#/DrivelutionTest/Core/DriverUpdaterFactoryTests.cs new file mode 100644 index 00000000..75feab57 --- /dev/null +++ b/src/c#/DrivelutionTest/Core/DriverUpdaterFactoryTests.cs @@ -0,0 +1,212 @@ +using GeneralUpdate.Drivelution.Core; +using GeneralUpdate.Drivelution.Abstractions; +using GeneralUpdate.Drivelution.Abstractions.Configuration; +using Serilog; +using Serilog.Core; + +namespace DrivelutionTest.Core; + +/// +/// Tests for DriverUpdaterFactory class. +/// Validates platform detection, factory creation, and platform-specific implementations. +/// +public class DriverUpdaterFactoryTests +{ + /// + /// Tests that Create method returns a non-null instance. + /// + [Fact] + public void Create_WithoutParameters_ReturnsNonNullInstance() + { + // Arrange & Act + var updater = DriverUpdaterFactory.Create(); + + // Assert + Assert.NotNull(updater); + Assert.IsAssignableFrom(updater); + } + + /// + /// Tests that Create method accepts custom logger. + /// + [Fact] + public void Create_WithCustomLogger_ReturnsInstance() + { + // Arrange + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + + // Act + var updater = DriverUpdaterFactory.Create(logger); + + // Assert + Assert.NotNull(updater); + Assert.IsAssignableFrom(updater); + } + + /// + /// Tests that Create method accepts custom options. + /// + [Fact] + public void Create_WithCustomOptions_ReturnsInstance() + { + // Arrange + var options = new DriverUpdateOptions + { + LogLevel = "Debug", + LogFilePath = "./logs/test.log" + }; + + // Act + var updater = DriverUpdaterFactory.Create(null, options); + + // Assert + Assert.NotNull(updater); + } + + /// + /// Tests that GetCurrentPlatform returns a valid platform name. + /// + [Fact] + public void GetCurrentPlatform_ReturnsValidPlatformName() + { + // Act + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + + // Assert + Assert.NotNull(platform); + Assert.Contains(platform, new[] { "Windows", "Linux", "MacOS", "Unknown" }); + } + + /// + /// Tests that IsPlatformSupported returns a boolean value. + /// + [Fact] + public void IsPlatformSupported_ReturnsBooleanValue() + { + // Act + var isSupported = DriverUpdaterFactory.IsPlatformSupported(); + + // Assert + // Windows and Linux should be supported + Assert.True(isSupported || DriverUpdaterFactory.GetCurrentPlatform() == "MacOS" || DriverUpdaterFactory.GetCurrentPlatform() == "Unknown"); + } + + /// + /// Tests that CreateValidator returns a non-null instance. + /// + [Fact] + public void CreateValidator_WithoutLogger_ReturnsNonNullInstance() + { + // Skip on MacOS and Unknown platforms + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + if (platform == "MacOS" || platform == "Unknown") + { + return; + } + + // Act + var validator = DriverUpdaterFactory.CreateValidator(); + + // Assert + Assert.NotNull(validator); + Assert.IsAssignableFrom(validator); + } + + /// + /// Tests that CreateBackup returns a non-null instance. + /// + [Fact] + public void CreateBackup_WithoutLogger_ReturnsNonNullInstance() + { + // Skip on MacOS and Unknown platforms + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + if (platform == "MacOS" || platform == "Unknown") + { + return; + } + + // Act + var backup = DriverUpdaterFactory.CreateBackup(); + + // Assert + Assert.NotNull(backup); + Assert.IsAssignableFrom(backup); + } + + /// + /// Tests that CreateValidator with custom logger works correctly. + /// + [Fact] + public void CreateValidator_WithCustomLogger_ReturnsInstance() + { + // Skip on MacOS and Unknown platforms + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + if (platform == "MacOS" || platform == "Unknown") + { + return; + } + + // Arrange + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + + // Act + var validator = DriverUpdaterFactory.CreateValidator(logger); + + // Assert + Assert.NotNull(validator); + } + + /// + /// Tests that CreateBackup with custom logger works correctly. + /// + [Fact] + public void CreateBackup_WithCustomLogger_ReturnsInstance() + { + // Skip on MacOS and Unknown platforms + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + if (platform == "MacOS" || platform == "Unknown") + { + return; + } + + // Arrange + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + + // Act + var backup = DriverUpdaterFactory.CreateBackup(logger); + + // Assert + Assert.NotNull(backup); + } + + /// + /// Tests that Create throws PlatformNotSupportedException on unsupported platforms. + /// This test documents expected behavior but cannot be easily tested on supported platforms. + /// + [Fact] + public void Create_OnSupportedPlatform_DoesNotThrow() + { + // Skip on MacOS as it's not yet implemented + var platform = DriverUpdaterFactory.GetCurrentPlatform(); + + if (platform == "MacOS") + { + // MacOS should throw PlatformNotSupportedException + Assert.Throws(() => DriverUpdaterFactory.Create()); + return; + } + + // Act & Assert - should not throw on Windows/Linux + var exception = Record.Exception(() => DriverUpdaterFactory.Create()); + Assert.Null(exception); + } +} diff --git a/src/c#/DrivelutionTest/DrivelutionTest.csproj b/src/c#/DrivelutionTest/DrivelutionTest.csproj new file mode 100644 index 00000000..7c3fec32 --- /dev/null +++ b/src/c#/DrivelutionTest/DrivelutionTest.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + diff --git a/src/c#/DrivelutionTest/GeneralDrivelutionTests.cs b/src/c#/DrivelutionTest/GeneralDrivelutionTests.cs new file mode 100644 index 00000000..c7460e4f --- /dev/null +++ b/src/c#/DrivelutionTest/GeneralDrivelutionTests.cs @@ -0,0 +1,330 @@ +using GeneralUpdate.Drivelution; +using GeneralUpdate.Drivelution.Abstractions.Models; +using GeneralUpdate.Drivelution.Abstractions.Configuration; +using Serilog; + +namespace DrivelutionTest; + +/// +/// Tests for GeneralDrivelution static entry point class. +/// Validates factory methods, quick update methods, and platform info retrieval. +/// +public class GeneralDrivelutionTests +{ + /// + /// Tests that Create method returns a non-null instance. + /// + [Fact] + public void Create_WithoutParameters_ReturnsNonNullInstance() + { + // Act + var updater = GeneralDrivelution.Create(); + + // Assert + Assert.NotNull(updater); + } + + /// + /// Tests that Create method with options returns a non-null instance. + /// + [Fact] + public void Create_WithOptions_ReturnsNonNullInstance() + { + // Arrange + var options = new DriverUpdateOptions + { + LogLevel = "Information", + LogFilePath = "./logs/test.log" + }; + + // Act + var updater = GeneralDrivelution.Create(options); + + // Assert + Assert.NotNull(updater); + } + + /// + /// Tests that Create method with custom logger returns a non-null instance. + /// + [Fact] + public void Create_WithCustomLogger_ReturnsNonNullInstance() + { + // Arrange + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + + // Act + var updater = GeneralDrivelution.Create(logger); + + // Assert + Assert.NotNull(updater); + } + + /// + /// Tests that Create method with custom logger and options returns instance. + /// + [Fact] + public void Create_WithCustomLoggerAndOptions_ReturnsInstance() + { + // Arrange + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + var options = new DriverUpdateOptions + { + LogLevel = "Debug" + }; + + // Act + var updater = GeneralDrivelution.Create(logger, options); + + // Assert + Assert.NotNull(updater); + } + + /// + /// Tests that GetPlatformInfo returns valid platform information. + /// + [Fact] + public void GetPlatformInfo_ReturnsValidPlatformInfo() + { + // Act + var platformInfo = GeneralDrivelution.GetPlatformInfo(); + + // Assert + Assert.NotNull(platformInfo); + Assert.NotNull(platformInfo.Platform); + Assert.NotEmpty(platformInfo.Platform); + Assert.NotNull(platformInfo.OperatingSystem); + Assert.NotEmpty(platformInfo.OperatingSystem); + Assert.NotNull(platformInfo.Architecture); + Assert.NotEmpty(platformInfo.Architecture); + Assert.NotNull(platformInfo.SystemVersion); + Assert.NotEmpty(platformInfo.SystemVersion); + } + + /// + /// Tests that PlatformInfo ToString returns a valid string. + /// + [Fact] + public void PlatformInfo_ToString_ReturnsValidString() + { + // Arrange + var platformInfo = GeneralDrivelution.GetPlatformInfo(); + + // Act + var result = platformInfo.ToString(); + + // Assert + Assert.NotNull(result); + Assert.NotEmpty(result); + Assert.Contains(platformInfo.Platform, result); + Assert.Contains(platformInfo.OperatingSystem, result); + } + + /// + /// Tests that QuickUpdateAsync with minimal parameters works (or throws expected exceptions). + /// + [Fact] + public async Task QuickUpdateAsync_WithMinimalParameters_HandlesGracefully() + { + // Arrange + var testFilePath = Path.Combine(Path.GetTempPath(), $"test_driver_{Guid.NewGuid()}.txt"); + File.WriteAllText(testFilePath, "Test driver content"); + + try + { + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + FilePath = testFilePath, + TargetOS = "", + Architecture = "" + }; + + // Act + var result = await GeneralDrivelution.QuickUpdateAsync(driverInfo); + + // Assert + Assert.NotNull(result); + // The result might fail due to validation or permissions, but should not crash + // Verify that result status is one of the valid enum values + Assert.True(Enum.IsDefined(typeof(UpdateStatus), result.Status)); + } + finally + { + if (File.Exists(testFilePath)) + { + File.Delete(testFilePath); + } + } + } + + /// + /// Tests that QuickUpdateAsync with custom strategy works. + /// + [Fact] + public async Task QuickUpdateAsync_WithCustomStrategy_HandlesGracefully() + { + // Arrange + var testFilePath = Path.Combine(Path.GetTempPath(), $"test_driver_{Guid.NewGuid()}.txt"); + File.WriteAllText(testFilePath, "Test driver content"); + + try + { + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + FilePath = testFilePath, + TargetOS = "", + Architecture = "" + }; + + var strategy = new UpdateStrategy + { + RequireBackup = false, + RetryCount = 1, + RetryIntervalSeconds = 1 + }; + + // Act + var result = await GeneralDrivelution.QuickUpdateAsync(driverInfo, strategy); + + // Assert + Assert.NotNull(result); + // Verify that result status is one of the valid enum values + Assert.True(Enum.IsDefined(typeof(UpdateStatus), result.Status)); + } + finally + { + if (File.Exists(testFilePath)) + { + File.Delete(testFilePath); + } + } + } + + /// + /// Tests that ValidateAsync with minimal driver info works. + /// + [Fact] + public async Task ValidateAsync_WithMinimalDriverInfo_ReturnsBooleanResult() + { + // Arrange + var testFilePath = Path.Combine(Path.GetTempPath(), $"test_driver_{Guid.NewGuid()}.txt"); + File.WriteAllText(testFilePath, "Test driver content"); + + try + { + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + FilePath = testFilePath, + TargetOS = "", + Architecture = "" + }; + + // Act + var result = await GeneralDrivelution.ValidateAsync(driverInfo); + + // Assert - Should return a boolean value without throwing + // No assertion needed - the test passes if no exception is thrown + } + finally + { + if (File.Exists(testFilePath)) + { + File.Delete(testFilePath); + } + } + } + + /// + /// Tests that ValidateAsync returns false for non-existent file. + /// + [Fact] + public async Task ValidateAsync_WithNonExistentFile_ReturnsFalse() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + FilePath = "/nonexistent/path/driver.sys", + TargetOS = "", + Architecture = "" + }; + + // Act + var result = await GeneralDrivelution.ValidateAsync(driverInfo); + + // Assert + Assert.False(result); + } + + /// + /// Tests that QuickUpdateAsync with cancellation token works. + /// + [Fact] + public async Task QuickUpdateAsync_WithCancellationToken_CanBeCancelled() + { + // Arrange + var testFilePath = Path.Combine(Path.GetTempPath(), $"test_driver_{Guid.NewGuid()}.txt"); + File.WriteAllText(testFilePath, "Test driver content"); + + try + { + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + FilePath = testFilePath + }; + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + // Act & Assert + // Should either complete quickly or throw cancellation exception + var exception = await Record.ExceptionAsync( + () => GeneralDrivelution.QuickUpdateAsync(driverInfo, cts.Token)); + + // Either succeeded, failed gracefully, or was cancelled + Assert.True(exception == null || exception is OperationCanceledException); + } + finally + { + if (File.Exists(testFilePath)) + { + File.Delete(testFilePath); + } + } + } + + /// + /// Tests that GetPlatformInfo reports correct support status. + /// + [Fact] + public void GetPlatformInfo_ReportsCorrectSupportStatus() + { + // Act + var platformInfo = GeneralDrivelution.GetPlatformInfo(); + + // Assert + if (platformInfo.Platform == "Windows" || platformInfo.Platform == "Linux") + { + Assert.True(platformInfo.IsSupported); + } + else if (platformInfo.Platform == "MacOS") + { + Assert.False(platformInfo.IsSupported); // MacOS not yet implemented + } + } +} diff --git a/src/c#/DrivelutionTest/Models/ModelTests.cs b/src/c#/DrivelutionTest/Models/ModelTests.cs new file mode 100644 index 00000000..9e788f9e --- /dev/null +++ b/src/c#/DrivelutionTest/Models/ModelTests.cs @@ -0,0 +1,383 @@ +using GeneralUpdate.Drivelution.Abstractions.Models; + +namespace DrivelutionTest.Models; + +/// +/// Tests for DriverInfo model class. +/// Validates driver information structure and properties. +/// +public class DriverInfoTests +{ + /// + /// Tests that DriverInfo can be instantiated with default values. + /// + [Fact] + public void Constructor_CreatesInstanceWithDefaultValues() + { + // Act + var driverInfo = new DriverInfo(); + + // Assert + Assert.NotNull(driverInfo); + Assert.Equal(string.Empty, driverInfo.Name); + Assert.Equal(string.Empty, driverInfo.Version); + Assert.Equal(string.Empty, driverInfo.FilePath); + Assert.Equal("SHA256", driverInfo.HashAlgorithm); + Assert.NotNull(driverInfo.TrustedPublishers); + Assert.Empty(driverInfo.TrustedPublishers); + Assert.NotNull(driverInfo.Metadata); + Assert.Empty(driverInfo.Metadata); + } + + /// + /// Tests that DriverInfo properties can be set and retrieved. + /// + [Fact] + public void Properties_CanBeSetAndRetrieved() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.2.3", + FilePath = "/path/to/driver.sys", + TargetOS = "Windows", + Architecture = "X64", + HardwareId = "PCI\\VEN_1234&DEV_5678", + Hash = "abc123", + HashAlgorithm = "SHA256", + Description = "Test driver description", + ReleaseDate = new DateTime(2024, 1, 1) + }; + + // Act & Assert + Assert.Equal("Test Driver", driverInfo.Name); + Assert.Equal("1.2.3", driverInfo.Version); + Assert.Equal("/path/to/driver.sys", driverInfo.FilePath); + Assert.Equal("Windows", driverInfo.TargetOS); + Assert.Equal("X64", driverInfo.Architecture); + Assert.Equal("PCI\\VEN_1234&DEV_5678", driverInfo.HardwareId); + Assert.Equal("abc123", driverInfo.Hash); + Assert.Equal("SHA256", driverInfo.HashAlgorithm); + Assert.Equal("Test driver description", driverInfo.Description); + Assert.Equal(new DateTime(2024, 1, 1), driverInfo.ReleaseDate); + } + + /// + /// Tests that TrustedPublishers list can be modified. + /// + [Fact] + public void TrustedPublishers_CanBeModified() + { + // Arrange + var driverInfo = new DriverInfo(); + + // Act + driverInfo.TrustedPublishers.Add("Microsoft Corporation"); + driverInfo.TrustedPublishers.Add("NVIDIA Corporation"); + + // Assert + Assert.Equal(2, driverInfo.TrustedPublishers.Count); + Assert.Contains("Microsoft Corporation", driverInfo.TrustedPublishers); + Assert.Contains("NVIDIA Corporation", driverInfo.TrustedPublishers); + } + + /// + /// Tests that Metadata dictionary can be modified. + /// + [Fact] + public void Metadata_CanBeModified() + { + // Arrange + var driverInfo = new DriverInfo(); + + // Act + driverInfo.Metadata["Author"] = "Test Author"; + driverInfo.Metadata["License"] = "MIT"; + driverInfo.Metadata["Website"] = "https://example.com"; + + // Assert + Assert.Equal(3, driverInfo.Metadata.Count); + Assert.Equal("Test Author", driverInfo.Metadata["Author"]); + Assert.Equal("MIT", driverInfo.Metadata["License"]); + Assert.Equal("https://example.com", driverInfo.Metadata["Website"]); + } + + /// + /// Tests that HashAlgorithm defaults to SHA256. + /// + [Fact] + public void HashAlgorithm_DefaultsToSHA256() + { + // Arrange & Act + var driverInfo = new DriverInfo(); + + // Assert + Assert.Equal("SHA256", driverInfo.HashAlgorithm); + } +} + +/// +/// Tests for UpdateStrategy model class. +/// Validates update strategy configuration and properties. +/// +public class UpdateStrategyTests +{ + /// + /// Tests that UpdateStrategy can be instantiated with default values. + /// + [Fact] + public void Constructor_CreatesInstanceWithDefaultValues() + { + // Act + var strategy = new UpdateStrategy(); + + // Assert + Assert.NotNull(strategy); + Assert.Equal(UpdateMode.Full, strategy.Mode); + Assert.False(strategy.ForceUpdate); + Assert.True(strategy.RequireBackup); + Assert.Equal(3, strategy.RetryCount); + Assert.Equal(5, strategy.RetryIntervalSeconds); + Assert.Equal(0, strategy.Priority); + Assert.Equal(RestartMode.Prompt, strategy.RestartMode); + Assert.False(strategy.SkipSignatureValidation); + Assert.False(strategy.SkipHashValidation); + Assert.Equal(300, strategy.TimeoutSeconds); + } + + /// + /// Tests that UpdateStrategy properties can be set and retrieved. + /// + [Fact] + public void Properties_CanBeSetAndRetrieved() + { + // Arrange + var strategy = new UpdateStrategy + { + Mode = UpdateMode.Incremental, + ForceUpdate = true, + RequireBackup = false, + BackupPath = "/backup", + RetryCount = 5, + RetryIntervalSeconds = 10, + Priority = 1, + RestartMode = RestartMode.Immediate, + SkipSignatureValidation = true, + SkipHashValidation = true, + TimeoutSeconds = 600 + }; + + // Act & Assert + Assert.Equal(UpdateMode.Incremental, strategy.Mode); + Assert.True(strategy.ForceUpdate); + Assert.False(strategy.RequireBackup); + Assert.Equal("/backup", strategy.BackupPath); + Assert.Equal(5, strategy.RetryCount); + Assert.Equal(10, strategy.RetryIntervalSeconds); + Assert.Equal(1, strategy.Priority); + Assert.Equal(RestartMode.Immediate, strategy.RestartMode); + Assert.True(strategy.SkipSignatureValidation); + Assert.True(strategy.SkipHashValidation); + Assert.Equal(600, strategy.TimeoutSeconds); + } + + /// + /// Tests UpdateMode enum values. + /// + [Fact] + public void UpdateMode_HasExpectedValues() + { + // Assert + Assert.Equal(0, (int)UpdateMode.Full); + Assert.Equal(1, (int)UpdateMode.Incremental); + } + + /// + /// Tests RestartMode enum values. + /// + [Fact] + public void RestartMode_HasExpectedValues() + { + // Assert + Assert.Equal(0, (int)RestartMode.None); + Assert.Equal(1, (int)RestartMode.Prompt); + Assert.Equal(2, (int)RestartMode.Delayed); + Assert.Equal(3, (int)RestartMode.Immediate); + } +} + +/// +/// Tests for UpdateResult model class. +/// Validates update result tracking and properties. +/// +public class UpdateResultTests +{ + /// + /// Tests that UpdateResult can be instantiated. + /// + [Fact] + public void Constructor_CreatesInstance() + { + // Act + var result = new UpdateResult(); + + // Assert + Assert.NotNull(result); + Assert.False(result.Success); + Assert.Equal(UpdateStatus.NotStarted, result.Status); + Assert.NotNull(result.StepLogs); + Assert.Empty(result.StepLogs); + Assert.Equal(string.Empty, result.Message); + } + + /// + /// Tests that UpdateResult properties can be set and retrieved. + /// + [Fact] + public void Properties_CanBeSetAndRetrieved() + { + // Arrange + var startTime = DateTime.UtcNow; + var endTime = startTime.AddMinutes(5); + var error = new ErrorInfo + { + Code = "TEST_ERROR", + Message = "Test error message" + }; + + var result = new UpdateResult + { + Success = true, + Status = UpdateStatus.Succeeded, + Error = error, + StartTime = startTime, + EndTime = endTime, + BackupPath = "/backup/path", + RolledBack = false, + Message = "Update completed" + }; + + // Act & Assert + Assert.True(result.Success); + Assert.Equal(UpdateStatus.Succeeded, result.Status); + Assert.Equal(error, result.Error); + Assert.Equal(startTime, result.StartTime); + Assert.Equal(endTime, result.EndTime); + Assert.Equal("/backup/path", result.BackupPath); + Assert.False(result.RolledBack); + Assert.Equal("Update completed", result.Message); + } + + /// + /// Tests that DurationMs is calculated correctly. + /// + [Fact] + public void DurationMs_CalculatesCorrectly() + { + // Arrange + var startTime = DateTime.UtcNow; + var endTime = startTime.AddSeconds(30); + + var result = new UpdateResult + { + StartTime = startTime, + EndTime = endTime + }; + + // Act + var duration = result.DurationMs; + + // Assert + Assert.InRange(duration, 29900, 30100); // Allow 100ms tolerance + } + + /// + /// Tests that StepLogs can be modified. + /// + [Fact] + public void StepLogs_CanBeModified() + { + // Arrange + var result = new UpdateResult(); + + // Act + result.StepLogs.Add("Step 1: Started"); + result.StepLogs.Add("Step 2: Validation"); + result.StepLogs.Add("Step 3: Completed"); + + // Assert + Assert.Equal(3, result.StepLogs.Count); + Assert.Contains("Step 1: Started", result.StepLogs); + Assert.Contains("Step 2: Validation", result.StepLogs); + Assert.Contains("Step 3: Completed", result.StepLogs); + } + + /// + /// Tests UpdateStatus enum values. + /// + [Fact] + public void UpdateStatus_HasExpectedValues() + { + // Assert + Assert.Equal(0, (int)UpdateStatus.NotStarted); + Assert.Equal(1, (int)UpdateStatus.Validating); + Assert.Equal(2, (int)UpdateStatus.BackingUp); + Assert.Equal(3, (int)UpdateStatus.Updating); + Assert.Equal(4, (int)UpdateStatus.Verifying); + Assert.Equal(5, (int)UpdateStatus.Succeeded); + Assert.Equal(6, (int)UpdateStatus.Failed); + Assert.Equal(7, (int)UpdateStatus.RolledBack); + } +} + +/// +/// Tests for ErrorInfo model class. +/// Validates error information structure. +/// +public class ErrorInfoTests +{ + /// + /// Tests that ErrorInfo can be instantiated. + /// + [Fact] + public void Constructor_CreatesInstance() + { + // Act + var errorInfo = new ErrorInfo(); + + // Assert + Assert.NotNull(errorInfo); + } + + /// + /// Tests that ErrorInfo properties can be set and retrieved. + /// + [Fact] + public void Properties_CanBeSetAndRetrieved() + { + // Arrange + var innerException = new InvalidOperationException("Inner exception"); + var errorInfo = new ErrorInfo + { + Code = "ERR001", + Type = ErrorType.InstallationFailed, + Message = "Installation failed", + Details = "Detailed error information", + StackTrace = "Stack trace here", + InnerException = innerException, + CanRetry = true, + SuggestedResolution = "Try again" + }; + + // Act & Assert + Assert.Equal("ERR001", errorInfo.Code); + Assert.Equal(ErrorType.InstallationFailed, errorInfo.Type); + Assert.Equal("Installation failed", errorInfo.Message); + Assert.Equal("Detailed error information", errorInfo.Details); + Assert.Equal("Stack trace here", errorInfo.StackTrace); + Assert.Equal(innerException, errorInfo.InnerException); + Assert.True(errorInfo.CanRetry); + Assert.Equal("Try again", errorInfo.SuggestedResolution); + } +} diff --git a/src/c#/DrivelutionTest/Utilities/CompatibilityCheckerTests.cs b/src/c#/DrivelutionTest/Utilities/CompatibilityCheckerTests.cs new file mode 100644 index 00000000..bfc10ab0 --- /dev/null +++ b/src/c#/DrivelutionTest/Utilities/CompatibilityCheckerTests.cs @@ -0,0 +1,335 @@ +using GeneralUpdate.Drivelution.Core.Utilities; +using GeneralUpdate.Drivelution.Abstractions.Models; + +namespace DrivelutionTest.Utilities; + +/// +/// Tests for CompatibilityChecker utility class. +/// Validates platform compatibility checking functionality. +/// +public class CompatibilityCheckerTests +{ + /// + /// Tests that GetCurrentOS returns a valid operating system name. + /// + [Fact] + public void GetCurrentOS_ReturnsValidOSName() + { + // Act + var os = CompatibilityChecker.GetCurrentOS(); + + // Assert + Assert.NotNull(os); + Assert.Contains(os, new[] { "Windows", "Linux", "MacOS", "Unknown" }); + } + + /// + /// Tests that GetCurrentArchitecture returns a valid architecture. + /// + [Fact] + public void GetCurrentArchitecture_ReturnsValidArchitecture() + { + // Act + var arch = CompatibilityChecker.GetCurrentArchitecture(); + + // Assert + Assert.NotNull(arch); + Assert.NotEmpty(arch); + // Common architectures: X64, X86, Arm, Arm64 + Assert.True(arch.Length > 0); + } + + /// + /// Tests that GetSystemVersion returns a valid version string. + /// + [Fact] + public void GetSystemVersion_ReturnsValidVersionString() + { + // Act + var version = CompatibilityChecker.GetSystemVersion(); + + // Assert + Assert.NotNull(version); + Assert.NotEmpty(version); + } + + /// + /// Tests that CheckCompatibility returns true for compatible driver. + /// + [Fact] + public void CheckCompatibility_WithCompatibleDriver_ReturnsTrue() + { + // Arrange + var currentOS = CompatibilityChecker.GetCurrentOS(); + var currentArch = CompatibilityChecker.GetCurrentArchitecture(); + + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = currentOS, + Architecture = currentArch, + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = CompatibilityChecker.CheckCompatibility(driverInfo); + + // Assert + Assert.True(isCompatible); + } + + /// + /// Tests that CheckCompatibility returns false for incompatible OS. + /// + [Fact] + public void CheckCompatibility_WithIncompatibleOS_ReturnsFalse() + { + // Arrange + var currentOS = CompatibilityChecker.GetCurrentOS(); + var incompatibleOS = currentOS == "Windows" ? "Linux" : "Windows"; + + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = incompatibleOS, + Architecture = CompatibilityChecker.GetCurrentArchitecture(), + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = CompatibilityChecker.CheckCompatibility(driverInfo); + + // Assert + Assert.False(isCompatible); + } + + /// + /// Tests that CheckCompatibility returns false for incompatible architecture. + /// + [Fact] + public void CheckCompatibility_WithIncompatibleArchitecture_ReturnsFalse() + { + // Arrange + var currentArch = CompatibilityChecker.GetCurrentArchitecture(); + var incompatibleArch = currentArch.Contains("64") ? "X86" : "X64"; + + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = CompatibilityChecker.GetCurrentOS(), + Architecture = incompatibleArch, + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = CompatibilityChecker.CheckCompatibility(driverInfo); + + // Assert + Assert.False(isCompatible); + } + + /// + /// Tests that CheckCompatibility returns true when TargetOS is empty. + /// + [Fact] + public void CheckCompatibility_WithEmptyTargetOS_ReturnsTrue() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = "", + Architecture = CompatibilityChecker.GetCurrentArchitecture(), + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = CompatibilityChecker.CheckCompatibility(driverInfo); + + // Assert + Assert.True(isCompatible); + } + + /// + /// Tests that CheckCompatibility returns true when Architecture is empty. + /// + [Fact] + public void CheckCompatibility_WithEmptyArchitecture_ReturnsTrue() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = CompatibilityChecker.GetCurrentOS(), + Architecture = "", + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = CompatibilityChecker.CheckCompatibility(driverInfo); + + // Assert + Assert.True(isCompatible); + } + + /// + /// Tests that CheckCompatibility throws ArgumentNullException for null driver info. + /// + [Fact] + public void CheckCompatibility_WithNullDriverInfo_ThrowsArgumentNullException() + { + // Act & Assert + Assert.Throws(() => CompatibilityChecker.CheckCompatibility(null!)); + } + + /// + /// Tests that CheckCompatibilityAsync works correctly. + /// + [Fact] + public async Task CheckCompatibilityAsync_WithCompatibleDriver_ReturnsTrue() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = CompatibilityChecker.GetCurrentOS(), + Architecture = CompatibilityChecker.GetCurrentArchitecture(), + FilePath = "/test/driver.sys" + }; + + // Act + var isCompatible = await CompatibilityChecker.CheckCompatibilityAsync(driverInfo); + + // Assert + Assert.True(isCompatible); + } + + /// + /// Tests that CheckCompatibilityAsync can be cancelled. + /// + [Fact] + public async Task CheckCompatibilityAsync_WithCancellation_ThrowsOperationCanceledException() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = CompatibilityChecker.GetCurrentOS(), + Architecture = CompatibilityChecker.GetCurrentArchitecture(), + FilePath = "/test/driver.sys" + }; + var cts = new CancellationTokenSource(); + cts.Cancel(); + + // Act & Assert + await Assert.ThrowsAnyAsync( + () => CompatibilityChecker.CheckCompatibilityAsync(driverInfo, cts.Token)); + } + + /// + /// Tests that GetCompatibilityReport returns a complete report. + /// + [Fact] + public void GetCompatibilityReport_ReturnsCompleteReport() + { + // Arrange + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = CompatibilityChecker.GetCurrentOS(), + Architecture = CompatibilityChecker.GetCurrentArchitecture(), + FilePath = "/test/driver.sys" + }; + + // Act + var report = CompatibilityChecker.GetCompatibilityReport(driverInfo); + + // Assert + Assert.NotNull(report); + Assert.NotNull(report.CurrentOS); + Assert.NotNull(report.CurrentArchitecture); + Assert.NotNull(report.SystemVersion); + Assert.True(report.OSCompatible); + Assert.True(report.ArchitectureCompatible); + Assert.True(report.OverallCompatible); + } + + /// + /// Tests that GetCompatibilityReport shows incompatibility correctly. + /// + [Fact] + public void GetCompatibilityReport_WithIncompatibleDriver_ShowsIncompatibility() + { + // Arrange + var currentOS = CompatibilityChecker.GetCurrentOS(); + var incompatibleOS = currentOS == "Windows" ? "Linux" : "Windows"; + + var driverInfo = new DriverInfo + { + Name = "Test Driver", + Version = "1.0.0", + TargetOS = incompatibleOS, + Architecture = "InvalidArchitecture", + FilePath = "/test/driver.sys" + }; + + // Act + var report = CompatibilityChecker.GetCompatibilityReport(driverInfo); + + // Assert + Assert.NotNull(report); + Assert.False(report.OSCompatible); + Assert.False(report.ArchitectureCompatible); + Assert.False(report.OverallCompatible); + } + + /// + /// Tests that architecture normalization works correctly. + /// + [Fact] + public void CheckCompatibility_WithArchitectureAliases_RecognizesAliases() + { + // Arrange - Test common architecture aliases + var testCases = new[] + { + ("X64", "AMD64"), + ("X64", "x86_64"), + ("X86", "i386"), + ("ARM64", "AARCH64") + }; + + foreach (var (arch1, arch2) in testCases) + { + var driverInfo1 = new DriverInfo + { + Name = "Test Driver", + TargetOS = "", + Architecture = arch1, + FilePath = "/test/driver.sys" + }; + + var driverInfo2 = new DriverInfo + { + Name = "Test Driver", + TargetOS = "", + Architecture = arch2, + FilePath = "/test/driver.sys" + }; + + // Act - Both should be treated the same way + var result1 = CompatibilityChecker.CheckCompatibility(driverInfo1); + var result2 = CompatibilityChecker.CheckCompatibility(driverInfo2); + + // Assert - Results should be consistent (both true or both false) + Assert.Equal(result1, result2); + } + } +} diff --git a/src/c#/DrivelutionTest/Utilities/HashValidatorTests.cs b/src/c#/DrivelutionTest/Utilities/HashValidatorTests.cs new file mode 100644 index 00000000..ce7c6d40 --- /dev/null +++ b/src/c#/DrivelutionTest/Utilities/HashValidatorTests.cs @@ -0,0 +1,237 @@ +using GeneralUpdate.Drivelution.Core.Utilities; + +namespace DrivelutionTest.Utilities; + +/// +/// Tests for HashValidator utility class. +/// Validates file hash computation and validation functionality. +/// +public class HashValidatorTests : IDisposable +{ + private readonly string _testFilePath; + private readonly string _testContent = "Hello, World! This is test content for hash validation."; + + public HashValidatorTests() + { + // Create a temporary test file + _testFilePath = Path.Combine(Path.GetTempPath(), $"test_{Guid.NewGuid()}.txt"); + File.WriteAllText(_testFilePath, _testContent); + } + + /// + /// Cleanup temporary test files. + /// + public void Dispose() + { + if (File.Exists(_testFilePath)) + { + try + { + File.Delete(_testFilePath); + } + catch + { + // Ignore cleanup errors + } + } + } + + /// + /// Tests that ComputeHashAsync returns a valid SHA256 hash. + /// + [Fact] + public async Task ComputeHashAsync_WithSHA256_ReturnsValidHash() + { + // Act + var hash = await HashValidator.ComputeHashAsync(_testFilePath, "SHA256"); + + // Assert + Assert.NotNull(hash); + Assert.NotEmpty(hash); + Assert.Equal(64, hash.Length); // SHA256 hash is 64 hex characters + Assert.Matches("^[a-f0-9]+$", hash); // Only lowercase hex characters + } + + /// + /// Tests that ComputeHashAsync returns a valid MD5 hash. + /// + [Fact] + public async Task ComputeHashAsync_WithMD5_ReturnsValidHash() + { + // Act + var hash = await HashValidator.ComputeHashAsync(_testFilePath, "MD5"); + + // Assert + Assert.NotNull(hash); + Assert.NotEmpty(hash); + Assert.Equal(32, hash.Length); // MD5 hash is 32 hex characters + Assert.Matches("^[a-f0-9]+$", hash); + } + + /// + /// Tests that ComputeHashAsync throws FileNotFoundException for non-existent file. + /// + [Fact] + public async Task ComputeHashAsync_WithNonExistentFile_ThrowsFileNotFoundException() + { + // Arrange + var nonExistentFile = Path.Combine(Path.GetTempPath(), $"nonexistent_{Guid.NewGuid()}.txt"); + + // Act & Assert + await Assert.ThrowsAsync( + () => HashValidator.ComputeHashAsync(nonExistentFile)); + } + + /// + /// Tests that ComputeHashAsync throws ArgumentException for unsupported algorithm. + /// + [Fact] + public async Task ComputeHashAsync_WithUnsupportedAlgorithm_ThrowsArgumentException() + { + // Act & Assert + await Assert.ThrowsAsync( + () => HashValidator.ComputeHashAsync(_testFilePath, "UNSUPPORTED")); + } + + /// + /// Tests that ValidateHashAsync returns true for matching hash. + /// + [Fact] + public async Task ValidateHashAsync_WithMatchingHash_ReturnsTrue() + { + // Arrange + var expectedHash = await HashValidator.ComputeHashAsync(_testFilePath, "SHA256"); + + // Act + var isValid = await HashValidator.ValidateHashAsync(_testFilePath, expectedHash, "SHA256"); + + // Assert + Assert.True(isValid); + } + + /// + /// Tests that ValidateHashAsync returns false for non-matching hash. + /// + [Fact] + public async Task ValidateHashAsync_WithNonMatchingHash_ReturnsFalse() + { + // Arrange + var wrongHash = "0000000000000000000000000000000000000000000000000000000000000000"; + + // Act + var isValid = await HashValidator.ValidateHashAsync(_testFilePath, wrongHash, "SHA256"); + + // Assert + Assert.False(isValid); + } + + /// + /// Tests that ValidateHashAsync is case-insensitive. + /// + [Fact] + public async Task ValidateHashAsync_IsCaseInsensitive() + { + // Arrange + var expectedHash = await HashValidator.ComputeHashAsync(_testFilePath, "SHA256"); + var upperCaseHash = expectedHash.ToUpperInvariant(); + + // Act + var isValid = await HashValidator.ValidateHashAsync(_testFilePath, upperCaseHash, "SHA256"); + + // Assert + Assert.True(isValid); + } + + /// + /// Tests that ValidateHashAsync throws ArgumentException for null or empty hash. + /// + [Fact] + public async Task ValidateHashAsync_WithNullOrEmptyHash_ThrowsArgumentException() + { + // Act & Assert + await Assert.ThrowsAsync( + () => HashValidator.ValidateHashAsync(_testFilePath, "", "SHA256")); + + await Assert.ThrowsAsync( + () => HashValidator.ValidateHashAsync(_testFilePath, null!, "SHA256")); + } + + /// + /// Tests that ComputeStringHash returns a valid hash for string input. + /// + [Fact] + public void ComputeStringHash_WithValidString_ReturnsValidHash() + { + // Arrange + var input = "Test String"; + + // Act + var hash = HashValidator.ComputeStringHash(input, "SHA256"); + + // Assert + Assert.NotNull(hash); + Assert.NotEmpty(hash); + Assert.Equal(64, hash.Length); + } + + /// + /// Tests that ComputeStringHash returns consistent results. + /// + [Fact] + public void ComputeStringHash_WithSameInput_ReturnsConsistentHash() + { + // Arrange + var input = "Consistent Test"; + + // Act + var hash1 = HashValidator.ComputeStringHash(input, "SHA256"); + var hash2 = HashValidator.ComputeStringHash(input, "SHA256"); + + // Assert + Assert.Equal(hash1, hash2); + } + + /// + /// Tests that ComputeStringHash returns different hashes for different inputs. + /// + [Fact] + public void ComputeStringHash_WithDifferentInputs_ReturnsDifferentHashes() + { + // Arrange + var input1 = "Test 1"; + var input2 = "Test 2"; + + // Act + var hash1 = HashValidator.ComputeStringHash(input1, "SHA256"); + var hash2 = HashValidator.ComputeStringHash(input2, "SHA256"); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + /// + /// Tests that ComputeStringHash throws ArgumentException for null or empty input. + /// + [Fact] + public void ComputeStringHash_WithNullOrEmptyInput_ThrowsArgumentException() + { + // Act & Assert + Assert.Throws(() => HashValidator.ComputeStringHash("")); + Assert.Throws(() => HashValidator.ComputeStringHash(null!)); + } + + /// + /// Tests that hash computation can be cancelled. + /// + [Fact] + public async Task ComputeHashAsync_WithCancellation_ThrowsOperationCanceledException() + { + // Arrange + var cts = new CancellationTokenSource(); + cts.Cancel(); + + // Act & Assert + await Assert.ThrowsAnyAsync( + () => HashValidator.ComputeHashAsync(_testFilePath, "SHA256", cts.Token)); + } +} diff --git a/src/c#/DrivelutionTest/Utilities/VersionComparerAndRestartHelperTests.cs b/src/c#/DrivelutionTest/Utilities/VersionComparerAndRestartHelperTests.cs new file mode 100644 index 00000000..04234777 --- /dev/null +++ b/src/c#/DrivelutionTest/Utilities/VersionComparerAndRestartHelperTests.cs @@ -0,0 +1,384 @@ +using GeneralUpdate.Drivelution.Core.Utilities; +using GeneralUpdate.Drivelution.Abstractions.Models; + +namespace DrivelutionTest.Utilities; + +/// +/// Tests for VersionComparer utility class. +/// Validates semantic versioning comparison functionality. +/// +public class VersionComparerTests +{ + /// + /// Tests that Compare returns 0 for equal versions. + /// + [Fact] + public void Compare_WithEqualVersions_ReturnsZero() + { + // Act + var result = VersionComparer.Compare("1.0.0", "1.0.0"); + + // Assert + Assert.Equal(0, result); + } + + /// + /// Tests that Compare returns 1 when first version is greater. + /// + [Fact] + public void Compare_WithGreaterFirstVersion_ReturnsOne() + { + // Act + var result = VersionComparer.Compare("2.0.0", "1.0.0"); + + // Assert + Assert.Equal(1, result); + } + + /// + /// Tests that Compare returns -1 when first version is less. + /// + [Fact] + public void Compare_WithLesserFirstVersion_ReturnsNegativeOne() + { + // Act + var result = VersionComparer.Compare("1.0.0", "2.0.0"); + + // Assert + Assert.Equal(-1, result); + } + + /// + /// Tests version comparison with minor version differences. + /// + [Fact] + public void Compare_WithMinorVersionDifference_ReturnsCorrectResult() + { + // Assert + Assert.True(VersionComparer.Compare("1.1.0", "1.0.0") > 0); + Assert.True(VersionComparer.Compare("1.0.0", "1.1.0") < 0); + } + + /// + /// Tests version comparison with patch version differences. + /// + [Fact] + public void Compare_WithPatchVersionDifference_ReturnsCorrectResult() + { + // Assert + Assert.True(VersionComparer.Compare("1.0.1", "1.0.0") > 0); + Assert.True(VersionComparer.Compare("1.0.0", "1.0.1") < 0); + } + + /// + /// Tests that version without prerelease is greater than version with prerelease. + /// + [Fact] + public void Compare_ReleaseVersionIsGreaterThanPrerelease() + { + // Act + var result = VersionComparer.Compare("1.0.0", "1.0.0-alpha"); + + // Assert + Assert.True(result > 0); + } + + /// + /// Tests prerelease version comparison. + /// + [Fact] + public void Compare_WithPrereleaseVersions_ComparesCorrectly() + { + // Assert + Assert.True(VersionComparer.Compare("1.0.0-beta", "1.0.0-alpha") > 0); + Assert.True(VersionComparer.Compare("1.0.0-alpha.2", "1.0.0-alpha.1") > 0); + } + + /// + /// Tests IsGreaterThan method. + /// + [Fact] + public void IsGreaterThan_WithGreaterVersion_ReturnsTrue() + { + // Act + var result = VersionComparer.IsGreaterThan("2.0.0", "1.0.0"); + + // Assert + Assert.True(result); + } + + /// + /// Tests IsGreaterThan method with lesser version. + /// + [Fact] + public void IsGreaterThan_WithLesserVersion_ReturnsFalse() + { + // Act + var result = VersionComparer.IsGreaterThan("1.0.0", "2.0.0"); + + // Assert + Assert.False(result); + } + + /// + /// Tests IsLessThan method. + /// + [Fact] + public void IsLessThan_WithLesserVersion_ReturnsTrue() + { + // Act + var result = VersionComparer.IsLessThan("1.0.0", "2.0.0"); + + // Assert + Assert.True(result); + } + + /// + /// Tests IsLessThan method with greater version. + /// + [Fact] + public void IsLessThan_WithGreaterVersion_ReturnsFalse() + { + // Act + var result = VersionComparer.IsLessThan("2.0.0", "1.0.0"); + + // Assert + Assert.False(result); + } + + /// + /// Tests IsEqual method. + /// + [Fact] + public void IsEqual_WithEqualVersions_ReturnsTrue() + { + // Act + var result = VersionComparer.IsEqual("1.0.0", "1.0.0"); + + // Assert + Assert.True(result); + } + + /// + /// Tests IsEqual method with different versions. + /// + [Fact] + public void IsEqual_WithDifferentVersions_ReturnsFalse() + { + // Act + var result = VersionComparer.IsEqual("1.0.0", "1.0.1"); + + // Assert + Assert.False(result); + } + + /// + /// Tests IsValidSemVer with valid versions. + /// + [Theory] + [InlineData("1.0.0")] + [InlineData("1.0.0-alpha")] + [InlineData("1.0.0-alpha.1")] + [InlineData("1.0.0+20130313144700")] + [InlineData("1.0.0-beta+exp.sha.5114f85")] + [InlineData("10.20.30")] + public void IsValidSemVer_WithValidVersions_ReturnsTrue(string version) + { + // Act + var result = VersionComparer.IsValidSemVer(version); + + // Assert + Assert.True(result); + } + + /// + /// Tests IsValidSemVer with invalid versions. + /// + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("1")] + [InlineData("1.0")] + [InlineData("1.0.0.0")] + [InlineData("v1.0.0")] + [InlineData("01.0.0")] + public void IsValidSemVer_WithInvalidVersions_ReturnsFalse(string version) + { + // Act + var result = VersionComparer.IsValidSemVer(version); + + // Assert + Assert.False(result); + } + + /// + /// Tests Compare throws ArgumentException for null or empty versions. + /// + [Fact] + public void Compare_WithNullOrEmptyVersion_ThrowsArgumentException() + { + // Act & Assert + Assert.Throws(() => VersionComparer.Compare("", "1.0.0")); + Assert.Throws(() => VersionComparer.Compare("1.0.0", "")); + Assert.Throws(() => VersionComparer.Compare(null!, "1.0.0")); + } + + /// + /// Tests Compare throws FormatException for invalid version format. + /// + [Fact] + public void Compare_WithInvalidFormat_ThrowsFormatException() + { + // Act & Assert + Assert.Throws(() => VersionComparer.Compare("invalid", "1.0.0")); + Assert.Throws(() => VersionComparer.Compare("1.0.0", "v1.0.0")); + } + + /// + /// Tests complex prerelease version comparison scenarios. + /// + [Fact] + public void Compare_WithComplexPrereleaseVersions_ComparesCorrectly() + { + // Arrange & Act & Assert + Assert.True(VersionComparer.Compare("1.0.0-rc.1", "1.0.0-beta.11") > 0); + Assert.True(VersionComparer.Compare("1.0.0-rc.1", "1.0.0-rc.0") > 0); + Assert.True(VersionComparer.Compare("1.0.0-alpha.beta", "1.0.0-alpha.1") > 0); // alphanumeric > numeric + Assert.True(VersionComparer.Compare("1.0.0-alpha", "1.0.0-alpha.1") < 0); // shorter < longer + } + + /// + /// Tests that build metadata is ignored in comparison. + /// + [Fact] + public void Compare_IgnoresBuildMetadata() + { + // Act + var result = VersionComparer.Compare("1.0.0+build1", "1.0.0+build2"); + + // Assert + Assert.Equal(0, result); + } + + /// + /// Tests version comparison with various real-world scenarios. + /// + [Theory] + [InlineData("1.0.0", "1.0.0", 0)] + [InlineData("2.0.0", "1.9.9", 1)] + [InlineData("1.0.0", "1.0.1", -1)] + [InlineData("1.1.0", "1.0.9", 1)] + [InlineData("1.0.0", "1.0.0-rc.1", 1)] + [InlineData("1.0.0-rc.2", "1.0.0-rc.1", 1)] + public void Compare_RealWorldScenarios_ReturnsExpectedResult(string version1, string version2, int expected) + { + // Act + var result = VersionComparer.Compare(version1, version2); + + // Assert + Assert.Equal(Math.Sign(expected), Math.Sign(result)); + } +} + +/// +/// Tests for RestartHelper utility class. +/// Validates restart handling functionality. +/// +public class RestartHelperTests +{ + /// + /// Tests that IsRestartRequired returns false for None mode. + /// + [Fact] + public void IsRestartRequired_WithNoneMode_ReturnsFalse() + { + // Act + var result = RestartHelper.IsRestartRequired(RestartMode.None); + + // Assert + Assert.False(result); + } + + /// + /// Tests that IsRestartRequired returns true for non-None modes. + /// + [Theory] + [InlineData(RestartMode.Prompt)] + [InlineData(RestartMode.Delayed)] + [InlineData(RestartMode.Immediate)] + public void IsRestartRequired_WithRestartModes_ReturnsTrue(RestartMode mode) + { + // Act + var result = RestartHelper.IsRestartRequired(mode); + + // Assert + Assert.True(result); + } + + /// + /// Tests that HandleRestartAsync with None mode returns true. + /// + [Fact] + public async Task HandleRestartAsync_WithNoneMode_ReturnsTrue() + { + // Act + var result = await RestartHelper.HandleRestartAsync(RestartMode.None); + + // Assert + Assert.True(result); + } + + /// + /// Tests that HandleRestartAsync with Prompt mode returns false (no user interaction in tests). + /// + [Fact] + public async Task HandleRestartAsync_WithPromptMode_ReturnsFalse() + { + // Act + var result = await RestartHelper.HandleRestartAsync(RestartMode.Prompt); + + // Assert + Assert.False(result); // Prompt returns false as there's no user interaction + } + + /// + /// Tests that PromptUserForRestart displays message and returns false. + /// + [Fact] + public void PromptUserForRestart_DisplaysMessage_ReturnsFalse() + { + // Act + var result = RestartHelper.PromptUserForRestart("Test message"); + + // Assert + Assert.False(result); + } + + /// + /// Tests that PromptUserForRestart with empty message uses default message. + /// + [Fact] + public void PromptUserForRestart_WithEmptyMessage_UsesDefaultMessage() + { + // Act + var result = RestartHelper.PromptUserForRestart(""); + + // Assert + Assert.False(result); + } + + /// + /// Tests that RestartHelper methods are callable without throwing exceptions + /// in non-privileged test environment (actual restart will fail due to permissions). + /// Note: We cannot actually restart the system during tests. + /// + [Fact] + public void RestartHelper_PublicMethods_AreCallable() + { + // This test documents that the RestartHelper class and its public methods exist + // and are callable. Actual restart functionality cannot be safely tested in unit tests. + // Act & Assert + Assert.True(RestartHelper.IsRestartRequired(RestartMode.None) == false); + Assert.True(RestartHelper.IsRestartRequired(RestartMode.Prompt) == true); + } +}