diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs index 4a171417..b6c7871a 100644 --- a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs @@ -133,13 +133,13 @@ private async Task LaunchWithStrategy(IStrategy roleStra // Inject binary differ into OS-level strategy for differential patching // Must be called after Create() since _osStrategy is initialized there. - var differ = ResolveExtension(); - if (differ != null) + var dirtyStrategy = ResolveExtension(); + if (dirtyStrategy != null) { if (roleStrategy is ClientUpdateStrategy cs2) - cs2.SetDiffer(differ); + cs2.SetDirtyStrategy(dirtyStrategy); else if (roleStrategy is UpgradeUpdateStrategy us2) - us2.SetDiffer(differ); + us2.SetDirtyStrategy(dirtyStrategy); } // Check custom skip condition before executing update diff --git a/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs index d8c8b7f8..36c396c6 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs @@ -55,8 +55,6 @@ protected T GetOption(UpdateOption? option) public TBootstrap SslPolicy() where T : Security.ISslValidationPolicy, new() { _extensions[typeof(Security.ISslValidationPolicy)] = typeof(T); return (TBootstrap)this; } - public TBootstrap BinaryDiffer() where T : Differential.IBinaryDiffer, new() - { _extensions[typeof(Differential.IBinaryDiffer)] = typeof(T); return (TBootstrap)this; } public TBootstrap PipelineFactory() where T : Pipeline.IUpdatePipelineFactory, new() { _extensions[typeof(Pipeline.IUpdatePipelineFactory)] = typeof(T); return (TBootstrap)this; } @@ -88,6 +86,9 @@ protected T GetOption(UpdateOption? option) public TBootstrap DirtyStrategy() where T : Differential.IDirtyStrategy, new() { _extensions[typeof(Differential.IDirtyStrategy)] = typeof(T); return (TBootstrap)this; } + public TBootstrap BinaryDiffer() where T : Differential.IBinaryDiffer, new() + { _extensions[typeof(Differential.IBinaryDiffer)] = typeof(T); return (TBootstrap)this; } + public TBootstrap ConfigureBlackList(BlackListConfig config) { _instances[typeof(BlackListConfig)] = config ?? BlackListConfig.Empty; diff --git a/src/c#/GeneralUpdate.Core/Differential/IBinaryDiffer.cs b/src/c#/GeneralUpdate.Core/Differential/IBinaryDiffer.cs index c0f4aae6..1cb56e23 100644 --- a/src/c#/GeneralUpdate.Core/Differential/IBinaryDiffer.cs +++ b/src/c#/GeneralUpdate.Core/Differential/IBinaryDiffer.cs @@ -1,40 +1,17 @@ -using System.IO; using System.Threading; using System.Threading.Tasks; -namespace GeneralUpdate.Core.Differential -{ - /// - /// Defines a pluggable binary differential algorithm (diff generation and patch application). - /// Implementations may use different strategies: BSDIFF, HDiffPatch-style, VCDIFF, etc. - /// - /// - /// This interface lives in Core so that Pipeline middleware can depend on it - /// without creating a circular dependency on the GeneralUpdate.Differential assembly. - /// - /// Concrete implementations (StreamingHdiffDiffer, BSDIFF, etc.) live in - /// GeneralUpdate.Differential and are injected via Bootstrap.BinaryDiffer<T>(). - /// - public interface IBinaryDiffer - { - /// - /// Generates a binary patch from to , - /// writing the result to . - /// - Task CleanAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default); +namespace GeneralUpdate.Core.Differential; - /// - /// Applies a binary patch to , producing - /// using the patch at . - /// - Task DirtyAsync( - string oldFilePath, - string newFilePath, - string patchFilePath, - CancellationToken cancellationToken = default); - } +/// +/// Pluggable file-level binary patch-application algorithm. +/// Implement this to customize how individual files are patched (BSDIFF, HDiffPatch, etc.). +/// +/// For full directory-level control, inject instead. +/// +public interface IBinaryDiffer +{ + /// Applies a binary patch: oldFile + patchFile → newFile. + Task DirtyAsync(string oldFilePath, string newFilePath, string patchFilePath, + CancellationToken cancellationToken = default); } diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index bc45cf38..873d742c 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -10,9 +10,9 @@ namespace GeneralUpdate.Core.Pipeline; /// Differential patch middleware. Applies binary patches (BSDIFF, HDiffPatch, etc.) /// to bring files from an old version to a new version. /// -/// The implementation is resolved from -/// (key "BinaryDiffer"), set by -/// when the differ is injected via +/// The implementation is resolved from +/// (key "DirtyStrategy"), set by +/// when the differ is injected via /// Bootstrap.BinaryDiffer<T>(). Without injection, patches are skipped. /// public class PatchMiddleware : IMiddleware @@ -23,19 +23,19 @@ public async Task InvokeAsync(PipelineContext context) var targetPath = context.Get("PatchPath"); // Resolve differ from pipeline context (injected via AbstractStrategy) - var differ = context.Get("BinaryDiffer"); + var dirtyStrategy = context.Get("DirtyStrategy"); - if (differ == null) + if (dirtyStrategy == null) { - GeneralTracer.Info("PatchMiddleware.InvokeAsync: no IBinaryDiffer injected — patch skipped. " + - "Use Bootstrap.BinaryDiffer() to enable differential patching."); + GeneralTracer.Info("PatchMiddleware.InvokeAsync: no IDirtyStrategy injected — patch skipped. " + + "Use Bootstrap.DirtyStrategy() to enable differential patching."); return; } GeneralTracer.Info($"PatchMiddleware.InvokeAsync: applying differential patch. SourcePath={sourcePath}, PatchPath={targetPath}"); try { - await differ.DirtyAsync(sourcePath, targetPath, targetPath); + await dirtyStrategy.ExecuteAsync(sourcePath, targetPath); GeneralTracer.Info("PatchMiddleware.InvokeAsync: differential patch applied successfully."); } catch (Exception ex) diff --git a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs index be7e18c9..08a01cf1 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs @@ -25,7 +25,10 @@ public abstract class AbstractStrategy : IStrategy protected IUpdateReporter? Reporter { get; set; } /// Optional binary differ for differential patch updates. - public IBinaryDiffer? Differ { get; set; } + public IDirtyStrategy? DirtyStrategy { get; set; } + + /// Optional file-level binary differ for patch application. + public IBinaryDiffer? BinaryDiffer { get; set; } public virtual void Execute() => throw new NotImplementedException(); @@ -95,7 +98,8 @@ protected virtual PipelineContext CreatePipelineContext(VersionInfo version, str context.Add("PatchPath", patchPath); context.Add("PatchEnabled", _configinfo.PatchEnabled); // Binary differ for differential patching - context.Add("BinaryDiffer", Differ); + context.Add("DirtyStrategy", DirtyStrategy); + context.Add("BinaryDiffer", BinaryDiffer); return context; } diff --git a/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs index cac7cd2e..465f720e 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs @@ -1,3 +1,4 @@ +using GeneralUpdate.Core.Differential; using System; using System.Collections.Generic; using System.Diagnostics; @@ -46,8 +47,8 @@ public void Create(GlobalConfigInfo parameter) { _configInfo = parameter ?? throw new ArgumentNullException(nameof(parameter)); _osStrategy = ResolveOsStrategy(); - if (_pendingDiffer != null && _osStrategy is AbstractStrategy abs) - abs.Differ = _pendingDiffer; + if (_pendingDirtyStrategy != null && _osStrategy is AbstractStrategy abs) + abs.DirtyStrategy = _pendingDirtyStrategy; } public async Task ExecuteAsync() @@ -75,16 +76,23 @@ public void Execute() ExecuteAsync().GetAwaiter().GetResult(); } - private Differential.IBinaryDiffer? _pendingDiffer; + private IDirtyStrategy? _pendingDirtyStrategy; - /// Sets the binary differ on the underlying OS-level strategy for differential patch updates. - /// Safe to call before or after Create(). If called before, the differ is cached and applied when Create() resolves _osStrategy. - public void SetDiffer(Differential.IBinaryDiffer? differ) + /// Sets the directory-level dirty strategy on the underlying OS-level strategy for differential patch updates. + /// Safe to call before or after Create(). If called before, the strategy is cached and applied when Create() resolves _osStrategy. + public void SetDirtyStrategy(IDirtyStrategy? dirtyStrategy) { if (_osStrategy is AbstractStrategy abs) - abs.Differ = differ; + abs.DirtyStrategy = dirtyStrategy; else - _pendingDiffer = differ; + _pendingDirtyStrategy = dirtyStrategy; + } + + /// Sets the file-level binary differ on the underlying OS-level strategy. + public void SetBinaryDiffer(IBinaryDiffer? binaryDiffer) + { + if (_osStrategy is AbstractStrategy abs) + abs.BinaryDiffer = binaryDiffer; } public void StartApp() @@ -174,31 +182,6 @@ private async Task ExecuteStandardWorkflowAsync() return; } - // Build process info for the upgrade process - // Convert DownloadAsset list to VersionInfo for ProcessInfo compatibility - var downloadVersions = downloadPlan.Assets.Select(a => new VersionInfo - { - Name = a.Name, - Hash = a.SHA256, - Url = a.Url, - Version = a.Version, - Format = _configInfo.Format ?? "ZIP" - }).ToList(); - - var processInfo = ConfigurationMapper.MapToProcessInfo( - _configInfo, downloadVersions, - _configInfo.BlackFormats ?? BlackListDefaults.DefaultBlackFormats, - _configInfo.BlackFiles ?? BlackListDefaults.DefaultBlackFiles, - _configInfo.SkipDirectorys ?? BlackListDefaults.DefaultSkipDirectories); - - // Keep JSON string for backward compatibility (GlobalConfigInfo.ProcessInfo) - _configInfo.ProcessInfo = JsonSerializer.Serialize(processInfo, - ProcessInfoJsonContext.Default.ProcessInfo); - - // Wire ProcessInfo via AES-encrypted file IPC. - new EncryptedFileProcessInfoProvider().Send(processInfo); - GeneralTracer.Info("ClientUpdateStrategy: ProcessInfo sent via encrypted file IPC."); - // Backup — conditionally skipped when BackupEnabled is false if (_configInfo.BackupEnabled != false) { @@ -232,12 +215,70 @@ private async Task ExecuteStandardWorkflowAsync() await SafeReportDownloadCompletedAsync(hooksCtx).ConfigureAwait(false); await SafeOnDownloadCompletedAsync(hooksCtx).ConfigureAwait(false); - // Apply updates and start app - await _osStrategy.ExecuteAsync(); + // Phase: apply Upgrade packages — update Upgrade.exe itself before launching it. + // Safe because MainApp and Upgrade.exe are different files (no lock conflict). + var allVersions = downloadPlan.Assets.Select(a => new VersionInfo + { + Name = a.Name, + Hash = a.SHA256, + Url = a.Url, + Version = a.Version, + Format = _configInfo.Format ?? "ZIP", + AppType = a.IsForcibly ? null : null // preserve original AppType + }).ToList(); + + // Rebuild the full VersionInfo list with AppType preserved from download source + var downloadVersions = downloadPlan.Assets.Select(a => new VersionInfo + { + Name = a.Name, + Hash = a.SHA256, + Url = a.Url, + Version = a.Version, + Format = _configInfo.Format ?? "ZIP", + AppType = _configInfo.IsUpgradeUpdate == true && a.Version != _configInfo.ClientVersion + ? (int)AppType.Upgrade : (int)AppType.Client + }).ToList(); + + // Split: Upgrade versions vs MainApp versions + var upgradeVersions = downloadVersions.Where(v => v.AppType == (int)AppType.Upgrade).ToList(); + var clientVersions = downloadVersions.Where(v => v.AppType != (int)AppType.Upgrade).ToList(); + + GeneralTracer.Info($"ClientUpdateStrategy: Upgrade packages={upgradeVersions.Count}, MainApp packages={clientVersions.Count}"); + + // Apply Upgrade packages now (update Upgrade.exe before launching it) + if (upgradeVersions.Count > 0) + { + GeneralTracer.Info("ClientUpdateStrategy: applying Upgrade packages."); + _configInfo.UpdateVersions = upgradeVersions; + _osStrategy!.Create(_configInfo); + await _osStrategy.ExecuteAsync(); + } + + // Send IPC with remaining MainApp versions for the upgrade process + var processInfo = ConfigurationMapper.MapToProcessInfo( + _configInfo, clientVersions, + _configInfo.BlackFormats ?? BlackListDefaults.DefaultBlackFormats, + _configInfo.BlackFiles ?? BlackListDefaults.DefaultBlackFiles, + _configInfo.SkipDirectorys ?? BlackListDefaults.DefaultSkipDirectories); + + _configInfo.ProcessInfo = JsonSerializer.Serialize(processInfo, + ProcessInfoJsonContext.Default.ProcessInfo); + new EncryptedFileProcessInfoProvider().Send(processInfo); + GeneralTracer.Info("ClientUpdateStrategy: ProcessInfo sent with MainApp versions only."); + await SafeOnAfterUpdateAsync(hooksCtx).ConfigureAwait(false); await SafeReportUpdateAppliedAsync(hooksCtx).ConfigureAwait(false); await SafeOnBeforeStartAppAsync(hooksCtx).ConfigureAwait(false); - _osStrategy.StartApp(); + + // Launch the upgrade process to apply MainApp updates + var updaterPath = Path.Combine(_configInfo.InstallPath, _configInfo.AppName); + if (!File.Exists(updaterPath)) + throw new FileNotFoundException($"Upgrade application not found: {updaterPath}"); + + GeneralTracer.Info($"ClientUpdateStrategy: launching upgrade process {updaterPath}"); + Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = updaterPath }); + GeneralTracer.Info("ClientUpdateStrategy: upgrade process launched, exiting."); + await GracefulExit.CurrentProcessAsync().ConfigureAwait(false); } #endregion diff --git a/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs index 09b5a64d..7f52fc05 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs @@ -1,3 +1,4 @@ +using GeneralUpdate.Core.Differential; using System; using System.Runtime.InteropServices; using System.Text; @@ -34,8 +35,8 @@ public void Create(GlobalConfigInfo parameter) { _configInfo = parameter ?? throw new ArgumentNullException(nameof(parameter)); _osStrategy = ResolveOsStrategy(); - if (_pendingDiffer != null && _osStrategy is AbstractStrategy abs) - abs.Differ = _pendingDiffer; + if (_pendingDirtyStrategy != null && _osStrategy is AbstractStrategy abs) + abs.DirtyStrategy = _pendingDirtyStrategy; } public async Task ExecuteAsync() @@ -56,10 +57,10 @@ public async Task ExecuteAsync() _osStrategy!.Create(_configInfo); - // Apply updates via OS-specific pipeline (Hash -> Compress -> Patch) + // Apply MainApp updates — Client already applied Upgrade packages, IPC only has MainApp versions if (_configInfo.UpdateVersions?.Count > 0) { - GeneralTracer.Info($"UpgradeUpdateStrategy: applying {_configInfo.UpdateVersions.Count} update(s)."); + GeneralTracer.Info("UpgradeUpdateStrategy: applying " + _configInfo.UpdateVersions.Count + " MainApp update(s)."); await _osStrategy.ExecuteAsync(); } else @@ -92,16 +93,23 @@ public void Execute() ExecuteAsync().GetAwaiter().GetResult(); } - private Differential.IBinaryDiffer? _pendingDiffer; + private IDirtyStrategy? _pendingDirtyStrategy; - /// Sets the binary differ on the underlying OS-level strategy for differential patch updates. - /// Safe to call before or after Create(). If called before, the differ is cached and applied when Create() resolves _osStrategy. - public void SetDiffer(Differential.IBinaryDiffer? differ) + /// Sets the directory-level dirty strategy on the underlying OS-level strategy for differential patch updates. + /// Safe to call before or after Create(). If called before, the strategy is cached and applied when Create() resolves _osStrategy. + public void SetDirtyStrategy(IDirtyStrategy? dirtyStrategy) { if (_osStrategy is AbstractStrategy abs) - abs.Differ = differ; + abs.DirtyStrategy = dirtyStrategy; else - _pendingDiffer = differ; + _pendingDirtyStrategy = dirtyStrategy; + } + + /// Sets the file-level binary differ on the underlying OS-level strategy. + public void SetBinaryDiffer(IBinaryDiffer? binaryDiffer) + { + if (_osStrategy is AbstractStrategy abs) + abs.BinaryDiffer = binaryDiffer; } public void StartApp() diff --git a/src/c#/GeneralUpdate.Differential/Abstractions/IBinaryDiffer.cs b/src/c#/GeneralUpdate.Differential/Abstractions/IBinaryDiffer.cs index 3d885f3a..637053ca 100644 --- a/src/c#/GeneralUpdate.Differential/Abstractions/IBinaryDiffer.cs +++ b/src/c#/GeneralUpdate.Differential/Abstractions/IBinaryDiffer.cs @@ -1,20 +1,16 @@ -// IBinaryDiffer has been moved to GeneralUpdate.Core.Differential. -// This file provides a backward-compatible type alias. -// New code should reference GeneralUpdate.Core.Differential.IBinaryDiffer directly. - +using System.Threading; +using System.Threading.Tasks; using CoreBinaryDiffer = GeneralUpdate.Core.Differential.IBinaryDiffer; -namespace GeneralUpdate.Differential.Abstractions +namespace GeneralUpdate.Differential.Abstractions; + +/// +/// Binary differential algorithm with both patch generation and application. +/// Extends (DirtyAsync) with CleanAsync. +/// +public interface IBinaryDiffer : CoreBinaryDiffer { - /// - /// Binary differential algorithm abstraction. - /// - /// - /// Migration note: This interface is an alias for - /// . Use - /// using GeneralUpdate.Core.Differential; directly in new code. - /// - public interface IBinaryDiffer : CoreBinaryDiffer - { - } + /// Generates a patch: oldFile vs newFile → patchFile. + Task CleanAsync(string oldFilePath, string newFilePath, string patchFilePath, + CancellationToken cancellationToken = default); } diff --git a/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs b/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs index 0f6dd7b5..6cff8e62 100644 --- a/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs +++ b/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs @@ -109,13 +109,6 @@ public bool ValidateCertificate(System.Security.Cryptography.X509Certificates.X5 System.Net.Security.SslPolicyErrors sslPolicyErrors) => true; } - private sealed class StubBinaryDiffer : GeneralUpdate.Core.Differential.IBinaryDiffer - { - public Task CleanAsync(string oldFilePath, string newFilePath, string patchFilePath, - CancellationToken cancellationToken = default) => Task.CompletedTask; - public Task DirtyAsync(string oldFilePath, string newFilePath, string patchFilePath, - CancellationToken cancellationToken = default) => Task.CompletedTask; - } private sealed class StubPipelineFactory : GeneralUpdate.Core.Pipeline.IUpdatePipelineFactory { @@ -176,17 +169,15 @@ private sealed class StubDirtyStrategy : GeneralUpdate.Core.Differential.IDirtyS [Fact] public void Inject_Hooks() => Assert.NotNull(B().Hooks()); [Fact] public void Inject_Strategy() => Assert.NotNull(B().Strategy()); [Fact] public void Inject_SslPolicy() => Assert.NotNull(B().SslPolicy()); - [Fact] public void Inject_BinaryDiffer() => Assert.NotNull(B().BinaryDiffer()); + [Fact] public void Inject_DirtyStrategy() => Assert.NotNull(B().DirtyStrategy()); [Fact] public void Inject_PipelineFactory() => Assert.NotNull(B().PipelineFactory()); [Fact] public void Inject_DownloadPolicy() => Assert.NotNull(B().DownloadPolicy()); [Fact] public void Inject_DownloadExecutor() => Assert.NotNull(B().DownloadExecutor()); [Fact] public void Inject_DownloadSource() => Assert.NotNull(B().DownloadSource()); [Fact] public void Inject_DownloadPipeline() => Assert.NotNull(B().DownloadPipeline()); [Fact] public void Inject_UpdateReporter() => Assert.NotNull(B().UpdateReporter()); - [Fact] public void Inject_UpdateAuth() => Assert.NotNull(B().UpdateAuth()); [Fact] public void Inject_DownloadOrchestrator() => Assert.NotNull(B().DownloadOrchestrator()); [Fact] public void Inject_CleanStrategy() => Assert.NotNull(B().CleanStrategy()); - [Fact] public void Inject_DirtyStrategy() => Assert.NotNull(B().DirtyStrategy()); [Fact] public void Chain_AllExtensionsInjected() @@ -199,7 +190,7 @@ public void Chain_AllExtensionsInjected() .DownloadSource() .DownloadPipeline() .DownloadOrchestrator() - .BinaryDiffer() + .DirtyStrategy() .CleanStrategy() .DirtyStrategy() .SslPolicy() diff --git a/tests/CoreTest/Pipeline/PatchMiddlewareTests.cs b/tests/CoreTest/Pipeline/PatchMiddlewareTests.cs index 5a2afe94..a4661d4b 100644 --- a/tests/CoreTest/Pipeline/PatchMiddlewareTests.cs +++ b/tests/CoreTest/Pipeline/PatchMiddlewareTests.cs @@ -5,40 +5,28 @@ namespace CoreTest.Pipeline; /// /// AAAT unit tests for . -/// Covers: null differ (skip), non-null differ (invoke), success path, exception propagation. +/// Covers: null strategy (skip), non-null strategy (invoke), success path, exception propagation. /// public class PatchMiddlewareTests { - private sealed class StubDiffer : IBinaryDiffer + private sealed class StubDirtyStrategy : IDirtyStrategy { public bool Invoked { get; private set; } public bool ShouldThrow { get; set; } - public Task CleanAsync( - string oldFilePath, string newFilePath, string patchFilePath, - CancellationToken cancellationToken = default) + public Task ExecuteAsync(string appPath, string patchPath) { Invoked = true; if (ShouldThrow) - throw new InvalidOperationException("test differ failure"); - return Task.CompletedTask; - } - - public Task DirtyAsync( - string oldFilePath, string newFilePath, string patchFilePath, - CancellationToken cancellationToken = default) - { - Invoked = true; - if (ShouldThrow) - throw new InvalidOperationException("test differ failure"); + throw new InvalidOperationException("test dirty strategy failure"); return Task.CompletedTask; } } - #region No differ in context — skip + #region No strategy in context — skip [Fact] - public async Task InvokeAsync_NoDifferInContext_SkipsWithoutThrow() + public async Task InvokeAsync_NoDirtyStrategyInContext_SkipsWithoutThrow() { var middleware = new PatchMiddleware(); var context = new PipelineContext(); @@ -63,32 +51,32 @@ public async Task InvokeAsync_NullContextProperties_SkipsWithoutThrow() #endregion - #region Differ in context — invokes + #region Strategy in context — invokes [Fact] - public async Task InvokeAsync_DifferInContext_InvokesDirtyAsync() + public async Task InvokeAsync_DirtyStrategyInContext_InvokesExecuteAsync() { - var differ = new StubDiffer(); + var strategy = new StubDirtyStrategy(); var middleware = new PatchMiddleware(); var context = new PipelineContext(); context.Add("SourcePath", "/src/a.txt"); context.Add("PatchPath", "/patch/a.txt"); - context.Add("BinaryDiffer", differ); + context.Add("DirtyStrategy", strategy); await middleware.InvokeAsync(context); - Assert.True(differ.Invoked); + Assert.True(strategy.Invoked); } [Fact] - public async Task InvokeAsync_DifferInContext_ThrowsExceptionPropagates() + public async Task InvokeAsync_DirtyStrategyThrows_ExceptionPropagates() { - var differ = new StubDiffer { ShouldThrow = true }; + var strategy = new StubDirtyStrategy { ShouldThrow = true }; var middleware = new PatchMiddleware(); var context = new PipelineContext(); context.Add("SourcePath", "/src"); context.Add("PatchPath", "/patch"); - context.Add("BinaryDiffer", differ); + context.Add("DirtyStrategy", strategy); await Assert.ThrowsAsync(() => middleware.InvokeAsync(context)); } @@ -98,31 +86,31 @@ public async Task InvokeAsync_DifferInContext_ThrowsExceptionPropagates() #region PipelineContext values edge cases [Fact] - public async Task InvokeAsync_DifferInContext_NullPaths_InvokesStill() + public async Task InvokeAsync_DirtyStrategyInContext_NullPaths_InvokesStill() { - var differ = new StubDiffer(); + var strategy = new StubDirtyStrategy(); var middleware = new PatchMiddleware(); var context = new PipelineContext(); - context.Add("BinaryDiffer", differ); + context.Add("DirtyStrategy", strategy); await middleware.InvokeAsync(context); - Assert.True(differ.Invoked); + Assert.True(strategy.Invoked); } [Fact] - public async Task InvokeAsync_DifferInContext_EmptyStringPaths_InvokesStill() + public async Task InvokeAsync_DirtyStrategyInContext_EmptyStringPaths_InvokesStill() { - var differ = new StubDiffer(); + var strategy = new StubDirtyStrategy(); var middleware = new PatchMiddleware(); var context = new PipelineContext(); context.Add("SourcePath", string.Empty); context.Add("PatchPath", string.Empty); - context.Add("BinaryDiffer", differ); + context.Add("DirtyStrategy", strategy); await middleware.InvokeAsync(context); - Assert.True(differ.Invoked); + Assert.True(strategy.Invoked); } #endregion