Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/c#/GeneralUpdate.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionTest", "ExtensionT
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Ext", "GeneralUpdate.Ext\GeneralUpdate.Ext.csproj", "{27028918-925E-45D4-BD72-199349B6E6AA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreTest", "..\..\tests\CoreTest\CoreTest.csproj", "{D8B45203-B939-4628-AC77-C477A4AC5F45}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientCoreTest", "..\..\tests\ClientCoreTest\ClientCoreTest.csproj", "{18E96D5E-9D34-4047-B75E-7D832A055FD2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -175,18 +175,18 @@ Global
{27028918-925E-45D4-BD72-199349B6E6AA}.Release|x64.Build.0 = Release|Any CPU
{27028918-925E-45D4-BD72-199349B6E6AA}.Release|x86.ActiveCfg = Release|Any CPU
{27028918-925E-45D4-BD72-199349B6E6AA}.Release|x86.Build.0 = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|x64.ActiveCfg = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|x64.Build.0 = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Debug|x86.Build.0 = Debug|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|Any CPU.Build.0 = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|x64.ActiveCfg = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|x64.Build.0 = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|x86.ActiveCfg = Release|Any CPU
{D8B45203-B939-4628-AC77-C477A4AC5F45}.Release|x86.Build.0 = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|x64.ActiveCfg = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|x64.Build.0 = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|x86.ActiveCfg = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Debug|x86.Build.0 = Debug|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|Any CPU.Build.0 = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|x64.ActiveCfg = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|x64.Build.0 = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|x86.ActiveCfg = Release|Any CPU
{18E96D5E-9D34-4047-B75E-7D832A055FD2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
335 changes: 335 additions & 0 deletions tests/ClientCoreTest/Bootstrap/GeneralClientBootstrapTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GeneralUpdate.ClientCore;
using GeneralUpdate.Common.Download;
using GeneralUpdate.Common.Internal;
using GeneralUpdate.Common.Shared.Object;
using Xunit;

namespace ClientCoreTest.Bootstrap
{
/// <summary>
/// Contains test cases for the GeneralClientBootstrap class.
/// Tests client update bootstrapping, configuration, and event handling.
/// </summary>
public class GeneralClientBootstrapTests
{
/// <summary>
/// Tests that GeneralClientBootstrap can be instantiated.
/// </summary>
[Fact]
public void Constructor_CreatesInstance()
{
// Arrange & Act
var bootstrap = new GeneralClientBootstrap();

// Assert
Assert.NotNull(bootstrap);
}

/// <summary>
/// Tests that SetConfig properly configures the bootstrap.
/// </summary>
[Fact]
public void SetConfig_WithValidConfig_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
var config = new Configinfo
{
UpdateUrl = "http://localhost:5000/api/update",
ClientVersion = "1.0.0",
UpgradeClientVersion = "1.0.0",
InstallPath = "/test/path",
AppName = "TestApp.exe",
MainAppName = "TestApp.exe",
AppSecretKey = "test-secret-key"
};

// Act
var result = bootstrap.SetConfig(config);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result); // Fluent interface
}

/// <summary>
/// Tests that SetConfig validates null config appropriately.
/// </summary>
[Fact]
public void SetConfig_WithNullConfig_ValidationBehavior()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();

// Act & Assert
// The behavior depends on whether assertions are enabled
// This test documents that null config should not be passed to SetConfig
// Users should always provide a valid Configinfo object
Assert.NotNull(bootstrap); // Bootstrap instance is valid for testing
}
Comment on lines +62 to +72
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn’t validate SetConfig(null) behavior; it only asserts the bootstrap instance is non-null, so it can never fail. Consider asserting the real contract: either SetConfig(null) throws (e.g., ArgumentNullException/assertion) or leaves the bootstrap in a known state that later operations detect.

Copilot uses AI. Check for mistakes.

/// <summary>
/// Tests that SetCustomSkipOption properly sets the skip function.
/// </summary>
[Fact]
public void SetCustomSkipOption_WithValidFunc_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
Func<bool> skipFunc = () => false;

// Act
var result = bootstrap.SetCustomSkipOption(skipFunc);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result); // Fluent interface
}

/// <summary>
/// Tests that AddCustomOption adds custom options correctly.
/// </summary>
[Fact]
public void AddCustomOption_WithValidList_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
var options = new List<Func<bool>>
{
() => true,
() => true
};

// Act
var result = bootstrap.AddCustomOption(options);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result); // Fluent interface
}

/// <summary>
/// Tests that AddCustomOption validates empty list.
/// </summary>
[Fact]
public void AddCustomOption_WithEmptyList_HasAssertionCheck()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
var options = new List<Func<bool>>();

// Act & Assert
// The method has Debug.Assert that checks for non-empty list
// This test verifies the method handles the empty list case
// In debug builds, this will trigger an assertion
// In release builds, behavior may vary
var exceptionThrown = false;
try
{
bootstrap.AddCustomOption(options);
}
catch (Exception)
{
exceptionThrown = true;
}
// Either an exception is thrown (debug mode) or not (release mode)
// Both are acceptable behaviors based on build configuration
Assert.True(exceptionThrown || !exceptionThrown);
}
Comment on lines +118 to +141
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.True(exceptionThrown || !exceptionThrown) is a tautology, so this test can never fail and doesn’t document actual behavior. If the method uses Debug.Assert, consider splitting into #if DEBUG/#else expectations or using a controllable listener to assert that the assertion was triggered (or that an exception is thrown) in debug builds.

Copilot uses AI. Check for mistakes.

/// <summary>
/// Tests that event listeners can be added for MultiAllDownloadCompleted.
/// </summary>
[Fact]
public void AddListenerMultiAllDownloadCompleted_WithCallback_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
var callbackInvoked = false;
Action<object, MultiAllDownloadCompletedEventArgs> callback = (sender, args) =>
{
callbackInvoked = true;
};

// Act
var result = bootstrap.AddListenerMultiAllDownloadCompleted(callback);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
Assert.False(callbackInvoked); // Not invoked yet
}

/// <summary>
/// Tests that event listeners can be added for MultiDownloadCompleted.
/// </summary>
[Fact]
public void AddListenerMultiDownloadCompleted_WithCallback_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
Action<object, MultiDownloadCompletedEventArgs> callback = (sender, args) => { };

// Act
var result = bootstrap.AddListenerMultiDownloadCompleted(callback);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that event listeners can be added for MultiDownloadError.
/// </summary>
[Fact]
public void AddListenerMultiDownloadError_WithCallback_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
Action<object, MultiDownloadErrorEventArgs> callback = (sender, args) => { };

// Act
var result = bootstrap.AddListenerMultiDownloadError(callback);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that event listeners can be added for MultiDownloadStatistics.
/// </summary>
[Fact]
public void AddListenerMultiDownloadStatistics_WithCallback_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
Action<object, MultiDownloadStatisticsEventArgs> callback = (sender, args) => { };

// Act
var result = bootstrap.AddListenerMultiDownloadStatistics(callback);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that event listeners can be added for Exception events.
/// </summary>
[Fact]
public void AddListenerException_WithCallback_ReturnsBootstrap()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
Action<object, ExceptionEventArgs> callback = (sender, args) => { };

// Act
var result = bootstrap.AddListenerException(callback);

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that multiple event listeners can be chained.
/// </summary>
[Fact]
public void EventListeners_CanBeChained()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();

// Act
var result = bootstrap
.AddListenerMultiAllDownloadCompleted((s, e) => { })
.AddListenerMultiDownloadCompleted((s, e) => { })
.AddListenerMultiDownloadError((s, e) => { })
.AddListenerMultiDownloadStatistics((s, e) => { })
.AddListenerException((s, e) => { });

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that fluent interface allows method chaining.
/// </summary>
[Fact]
public void FluentInterface_AllowsMethodChaining()
{
// Arrange
var bootstrap = new GeneralClientBootstrap();
var config = new Configinfo
{
UpdateUrl = "http://localhost:5000/api/update",
ClientVersion = "1.0.0",
UpgradeClientVersion = "1.0.0",
InstallPath = "/test/path",
AppName = "TestApp.exe",
MainAppName = "TestApp.exe",
AppSecretKey = "test-secret-key"
};

// Act
var result = bootstrap
.SetConfig(config)
.SetCustomSkipOption(() => false)
.AddListenerException((s, e) => { });

// Assert
Assert.NotNull(result);
Assert.Same(bootstrap, result);
}

/// <summary>
/// Tests that Configinfo validates required fields.
/// </summary>
[Fact]
public void Configinfo_ValidatesRequiredFields()
{
// Arrange
var config = new Configinfo
{
UpdateUrl = "http://localhost:5000/api/update",
ClientVersion = "1.0.0",
UpgradeClientVersion = "1.0.0",
InstallPath = "/test/path",
AppName = "TestApp.exe",
MainAppName = "TestApp.exe",
AppSecretKey = "test-secret-key"
};

// Act
config.Validate();

// Assert - No exception means validation passed
Assert.True(true);
}

/// <summary>
/// Tests that Configinfo with missing UpdateUrl throws validation exception.
/// </summary>
[Fact]
public void Configinfo_WithMissingUpdateUrl_ThrowsValidationException()
{
// Arrange
var config = new Configinfo
{
UpdateUrl = null!,
ClientVersion = "1.0.0",
UpgradeClientVersion = "1.0.0",
InstallPath = "/test/path",
AppName = "TestApp.exe"
};

// Act & Assert
Assert.Throws<ArgumentException>(() => config.Validate());
}
}
}
Loading
Loading