diff --git a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs index a84c1f55..6c224e5c 100644 --- a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs @@ -9,7 +9,7 @@ namespace GeneralUpdate.Core.Download.Abstractions; public interface IDownloadExecutor { Task ExecuteAsync( - string url, string destPath, + DownloadAsset asset, string destPath, IProgress? progress = null, CancellationToken token = default); } diff --git a/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs index a1c92cda..7293061c 100644 --- a/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs @@ -26,7 +26,7 @@ public HttpDownloadExecutor(HttpClient client, TimeSpan? timeout = null, bool en } public async Task ExecuteAsync( - string url, string destPath, + DownloadAsset asset, string destPath, IProgress? progress = null, CancellationToken token = default) { @@ -42,7 +42,7 @@ public async Task ExecuteAsync( try { - using var request = new HttpRequestMessage(HttpMethod.Get, url); + using var request = new HttpRequestMessage(HttpMethod.Get, asset.Url); if (_enableResume && existingBytes > 0) request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(existingBytes, null); @@ -74,12 +74,12 @@ public async Task ExecuteAsync( totalBytes > 0 ? totalBytes + existingBytes : null, 100, DownloadStatus.Completed)); - return new DownloadResult(url, destPath, downloaded, elapsed, retries, true, null); + return new DownloadResult(asset, destPath, downloaded, elapsed, retries, true, null); } catch (Exception ex) when (ex is not OperationCanceledException) { sw.Stop(); - return new DownloadResult(url, null, existingBytes, sw.Elapsed, retries, false, ex.Message); + return new DownloadResult(asset, null, existingBytes, sw.Elapsed, retries, false, ex.Message); } } diff --git a/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs index 5a4b565a..d145275e 100644 --- a/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs @@ -17,14 +17,14 @@ public OssDownloadExecutor(HttpClient client) => _client = client ?? throw new ArgumentNullException(nameof(client)); public async Task ExecuteAsync( - string url, string destPath, + DownloadAsset asset, string destPath, IProgress? progress = null, CancellationToken token = default) { var sw = System.Diagnostics.Stopwatch.StartNew(); try { - using var response = await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token) + using var response = await _client.GetAsync(asset.Url, HttpCompletionOption.ResponseHeadersRead, token) .ConfigureAwait(false); response.EnsureSuccessStatusCode(); var total = response.Content.Headers.ContentLength ?? -1; @@ -37,12 +37,12 @@ public async Task ExecuteAsync( progress?.Report(new DownloadProgress( Path.GetFileName(destPath), downloaded, total > 0 ? total : null, 100, DownloadStatus.Completed)); - return new DownloadResult(url, destPath, downloaded, elapsed, 0, true, null); + return new DownloadResult(asset, destPath, downloaded, elapsed, 0, true, null); } catch (Exception ex) when (ex is not OperationCanceledException) { sw.Stop(); - return new DownloadResult(url, null, 0, sw.Elapsed, 0, false, ex.Message); + return new DownloadResult(asset, null, 0, sw.Elapsed, 0, false, ex.Message); } } } diff --git a/src/c#/GeneralUpdate.Core/Download/Models/DownloadProgress.cs b/src/c#/GeneralUpdate.Core/Download/Models/DownloadProgress.cs index 4c778607..7e838819 100644 --- a/src/c#/GeneralUpdate.Core/Download/Models/DownloadProgress.cs +++ b/src/c#/GeneralUpdate.Core/Download/Models/DownloadProgress.cs @@ -15,7 +15,7 @@ DownloadStatus Status ); public record DownloadResult( - string? Url, + DownloadAsset Asset, string? LocalPath, long DownloadedBytes, TimeSpan Duration, diff --git a/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs b/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs index 48834363..35423f59 100644 --- a/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs +++ b/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs @@ -80,7 +80,7 @@ public async Task ExecuteAsync( { // Download var downloadResult = await executor.ExecuteAsync( - asset.Url, destPath, + asset, destPath, progress != null ? new AssetProgressReporter(progress, asset.Name) : null, ct).ConfigureAwait(false); @@ -100,7 +100,7 @@ public async Task ExecuteAsync( } catch (Exception ex) { - return new DownloadResult(asset.Url, destPath, + return new DownloadResult(asset, destPath, downloadResult.DownloadedBytes, downloadResult.Duration, downloadResult.RetryCount, false, $"SHA256 verification failed: {ex.Message}"); } @@ -122,7 +122,7 @@ public async Task ExecuteAsync( // Dispatch all-completed event ONCE after all assets finish (only failed results) var failedDetails = results.Where(r => !r.Success) - .Select(r => ((object)r.Url, r.ErrorMessage ?? "failed")).ToList(); + .Select(r => ((object)r.Asset.Url, r.ErrorMessage ?? "failed")).ToList(); DownloadProgressReporter.DispatchAllCompleted( this, results.All(r => r.Success), diff --git a/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs b/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs index fdc7c245..0f6dd7b5 100644 --- a/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs +++ b/tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs @@ -129,9 +129,9 @@ private sealed class StubDownloadPolicy : GeneralUpdate.Core.Download.Abstractio private sealed class StubDownloadExecutor : GeneralUpdate.Core.Download.Abstractions.IDownloadExecutor { - public Task ExecuteAsync(string url, string destPath, + public Task ExecuteAsync(GeneralUpdate.Core.Download.Models.DownloadAsset asset, string destPath, IProgress? progress = null, CancellationToken token = default) - => Task.FromResult(new GeneralUpdate.Core.Download.Models.DownloadResult(url, destPath, 0, TimeSpan.Zero, 0, true, null)); + => Task.FromResult(new GeneralUpdate.Core.Download.Models.DownloadResult(asset, destPath, 0, TimeSpan.Zero, 0, true, null)); } private sealed class StubDownloadSource : GeneralUpdate.Core.Download.Abstractions.IDownloadSource diff --git a/tests/CoreTest/Configuration/ConfigurationModelsTests.cs b/tests/CoreTest/Configuration/ConfigurationModelsTests.cs index 97bb3356..7940fd4a 100644 --- a/tests/CoreTest/Configuration/ConfigurationModelsTests.cs +++ b/tests/CoreTest/Configuration/ConfigurationModelsTests.cs @@ -219,7 +219,7 @@ public void DownloadProgress_Failed() public void DownloadResult_Success() { var result = new DownloadResult( - "https://cdn.example.com/update.zip", + null!, "/tmp/update.zip", 50L * 1024 * 1024, TimeSpan.FromSeconds(30), @@ -236,7 +236,7 @@ public void DownloadResult_Success() public void DownloadResult_FailureWithRetries() { var result = new DownloadResult( - "https://cdn.example.com/update.zip", + null!, null, 0, TimeSpan.FromSeconds(15), diff --git a/tests/CoreTest/Download/OrchestratorOptionsBehaviourTests.cs b/tests/CoreTest/Download/OrchestratorOptionsBehaviourTests.cs index dd6add3e..2c069be3 100644 --- a/tests/CoreTest/Download/OrchestratorOptionsBehaviourTests.cs +++ b/tests/CoreTest/Download/OrchestratorOptionsBehaviourTests.cs @@ -263,8 +263,9 @@ public async Task HttpDownloadExecutor_EnableResumeFalse_DeletesExistingFile() try { + var asset = new DownloadAsset("file.bin", "http://example.com/file.bin", 0, null, "1.0.0"); var result = await executor.ExecuteAsync( - "http://example.com/file.bin", destPath, token: CancellationToken.None); + asset, destPath, token: CancellationToken.None); Assert.True(result.Success, $"Download should succeed, error: {result.ErrorMessage}"); Assert.True(File.Exists(destPath)); } @@ -308,8 +309,9 @@ public async Task HttpDownloadExecutor_WithRangeResponse_AppendsCorrectly() try { + var asset = new DownloadAsset("file.bin", "http://example.com/file.bin", 0, null, "1.0.0"); var result = await executor.ExecuteAsync( - "http://example.com/file.bin", destPath, token: CancellationToken.None); + asset, destPath, token: CancellationToken.None); Assert.True(result.Success); // Partial content (30 bytes) appended to existing (20 bytes) = 50 total var fileInfo = new FileInfo(destPath);