diff --git a/src/Networking/NexusMods.Networking.Downloaders/DownloadService.cs b/src/Networking/NexusMods.Networking.Downloaders/DownloadService.cs index 7d8f75c9cf..52d413dbb8 100644 --- a/src/Networking/NexusMods.Networking.Downloaders/DownloadService.cs +++ b/src/Networking/NexusMods.Networking.Downloaders/DownloadService.cs @@ -1,8 +1,8 @@ using System.Collections.ObjectModel; +using System.ComponentModel; using DynamicData; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using NexusMods.Abstractions.MnemonicDB.Attributes.Extensions; using NexusMods.Abstractions.NexusWebApi.Types; using NexusMods.MnemonicDB.Abstractions; using NexusMods.Networking.Downloaders.Interfaces; @@ -14,8 +14,7 @@ using Microsoft.Extensions.Hosting; using NexusMods.Abstractions.Activities; using NexusMods.Abstractions.IO; -using NexusMods.MnemonicDB.Abstractions.DatomIterators; -using NexusMods.MnemonicDB.Abstractions.Query; +using NexusMods.Abstractions.Settings; using NexusMods.Paths; namespace NexusMods.Networking.Downloaders; @@ -25,6 +24,9 @@ public class DownloadService : IDownloadService, IDisposable, IHostedService { /// public ReadOnlyObservableCollection Downloads => _downloadsCollection; + + /// + public AbsolutePath OngoingDownloadsDirectory { get; private set; } private ReadOnlyObservableCollection _downloadsCollection = ReadOnlyObservableCollection.Empty; private readonly SourceCache _downloads = new(t => t.PersistentState.Id); @@ -35,13 +37,24 @@ public class DownloadService : IDownloadService, IDisposable, IHostedService private readonly CompositeDisposable _disposables; private readonly IFileStore _fileStore; - public DownloadService(ILogger logger, IServiceProvider provider, IFileStore fileStore, IConnection conn) + public DownloadService( + ILogger logger, + IServiceProvider provider, + IFileStore fileStore, + IConnection conn, + IFileSystem fs, + ISettingsManager settingsManager) { _logger = logger; _provider = provider; _conn = conn; _disposables = new CompositeDisposable(); _fileStore = fileStore; + OngoingDownloadsDirectory = settingsManager.Get().OngoingDownloadLocation.ToPath(fs); + if (!OngoingDownloadsDirectory.DirectoryExists()) + { + OngoingDownloadsDirectory.CreateDirectory(); + } } internal IEnumerable GetItemsToResume() @@ -206,11 +219,9 @@ public async Task StopAsync(CancellationToken cancellationToken) { var suspendingTasks = _downloads.Items .Where(dl => dl.PersistentState.Status == DownloadTaskStatus.Downloading) - // TODO(Al12rs): should Suspend() instead, but only after moving ongoing dl files outside Temp folder, - // that is otherwise cleaned up on application close, causing exceptions due to file in use, - // on top of discarding all the progress. - .Select(dl => dl.Cancel()); + .Select(dl => dl.Suspend()); await Task.WhenAll(suspendingTasks); } + } diff --git a/src/Networking/NexusMods.Networking.Downloaders/DownloadSettings.cs b/src/Networking/NexusMods.Networking.Downloaders/DownloadSettings.cs new file mode 100644 index 0000000000..360dd80416 --- /dev/null +++ b/src/Networking/NexusMods.Networking.Downloaders/DownloadSettings.cs @@ -0,0 +1,67 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using NexusMods.Abstractions.Settings; +using NexusMods.Paths; + +namespace NexusMods.Networking.Downloaders; + +/// +/// Settings for downloads. +/// +[PublicAPI] +public record DownloadSettings : ISettings +{ + + /// + /// Base directory where files generated during ongoing download operations are located. + /// + /// + /// Should not be placed in Temp or similar directories, + /// as download files may need to persist across application and system restarts. + /// + public ConfigurablePath OngoingDownloadLocation { get; set; } + + + /// + public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) + { + return settingsBuilder + .ConfigureDefault(CreateDefault) + .ConfigureStorageBackend(builder => builder.UseJson()); + } + + /// + /// Create default value. + /// + public static DownloadSettings CreateDefault(IServiceProvider serviceProvider) + { + var os = serviceProvider.GetRequiredService().OS; + + return new DownloadSettings + { + OngoingDownloadLocation = GetStandardDownloadPath(os), + }; + } + + private static ConfigurablePath GetStandardDownloadPath(IOSInformation os) + { + var basePath = os.MatchPlatform( + onWindows: () => KnownPath.LocalApplicationDataDirectory, + onLinux: () => KnownPath.XDG_DATA_HOME, + onOSX: () => KnownPath.LocalApplicationDataDirectory + ); + // NOTE: OSX ".App" is apparently special, using _ instead of . to prevent weirdness + var baseDirectoryName = os.IsOSX ? "NexusMods_App/" : "NexusMods.App/"; + var downloadsSubPath = baseDirectoryName + "Downloads/Ongoing"; + + return new ConfigurablePath(basePath, downloadsSubPath); + } + + /// + /// Absolute path to the standard downloads' folder. + /// + public static AbsolutePath GetStandardDownloadsFolder(IFileSystem fs) + { + return GetStandardDownloadPath(fs.OS).ToPath(fs); + } +} diff --git a/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadService.cs b/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadService.cs index 7349b21573..be6a6ae835 100644 --- a/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadService.cs +++ b/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadService.cs @@ -20,6 +20,11 @@ public interface IDownloadService /// ReadOnlyObservableCollection Downloads { get; } + /// + /// The base directory for ongoing downloads + /// + AbsolutePath OngoingDownloadsDirectory { get; } + /// /// Adds a task that will download from a NXM link. /// diff --git a/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadTask.cs b/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadTask.cs index 3a48c19854..1e0eb352cc 100644 --- a/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadTask.cs +++ b/src/Networking/NexusMods.Networking.Downloaders/Interfaces/IDownloadTask.cs @@ -16,9 +16,9 @@ public interface IDownloadTask DownloaderState.ReadOnly PersistentState { get; } /// - /// The download location of the task. + /// Path of the ongoing download file /// - public AbsolutePath DownloadLocation { get; } + public AbsolutePath DownloadPath { get; } /// /// Calculates the download speed of the current job. @@ -93,12 +93,7 @@ public enum DownloadTaskStatus : byte Downloading, /// - /// The mod is being archived (and possibly installed) to a loadout. - /// - Installing, - - /// - /// The task has ran to completion. + /// The task has run to completion. /// Completed, diff --git a/src/Networking/NexusMods.Networking.Downloaders/Services.cs b/src/Networking/NexusMods.Networking.Downloaders/Services.cs index ba79dc2c5e..bad08dcc64 100644 --- a/src/Networking/NexusMods.Networking.Downloaders/Services.cs +++ b/src/Networking/NexusMods.Networking.Downloaders/Services.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using NexusMods.Abstractions.Settings; using NexusMods.MnemonicDB.Abstractions; using NexusMods.Networking.Downloaders.Interfaces; using NexusMods.Networking.Downloaders.Tasks; @@ -18,6 +19,7 @@ public static class Services public static IServiceCollection AddDownloaders(this IServiceCollection services) { return services.AddSingleton() + .AddSettings() .AddHostedService(sp=> sp.GetRequiredService()) .AddSingleton(sp=> sp.GetRequiredService()) .AddTransient() diff --git a/src/Networking/NexusMods.Networking.Downloaders/Tasks/ADownloadTask.cs b/src/Networking/NexusMods.Networking.Downloaders/Tasks/ADownloadTask.cs index e6dd8e0fdf..ba47ff2e1b 100644 --- a/src/Networking/NexusMods.Networking.Downloaders/Tasks/ADownloadTask.cs +++ b/src/Networking/NexusMods.Networking.Downloaders/Tasks/ADownloadTask.cs @@ -1,18 +1,13 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; -using DynamicData.Kernel; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NexusMods.Abstractions.Activities; using NexusMods.Abstractions.FileStore; -using NexusMods.Abstractions.FileStore.Downloads; using NexusMods.Abstractions.HttpDownloader; -using NexusMods.Abstractions.IO; -using NexusMods.DataModel; using NexusMods.MnemonicDB.Abstractions; using NexusMods.Networking.Downloaders.Interfaces; using NexusMods.Networking.Downloaders.Tasks.State; using NexusMods.Paths; +using NexusMods.Paths.Utilities; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -30,33 +25,33 @@ public abstract class ADownloadTask : ReactiveObject, IDownloadTask /// protected HttpDownloaderState? TransientState = null!; protected ILogger Logger; - protected TemporaryFileManager TemporaryFileManager; protected HttpClient HttpClient; protected IHttpDownloader HttpDownloader; protected CancellationTokenSource CancellationTokenSource; - protected TemporaryPath _downloadLocation = default!; + protected AbsolutePath _downloadPath = default!; protected IFileSystem FileSystem; protected IFileOriginRegistry FileOriginRegistry; private DownloaderState.ReadOnly _persistentState; - + private IDownloadService _downloadService; + protected ADownloadTask(IServiceProvider provider) { Connection = provider.GetRequiredService(); Logger = provider.GetRequiredService>(); - TemporaryFileManager = provider.GetRequiredService(); HttpClient = provider.GetRequiredService(); HttpDownloader = provider.GetRequiredService(); CancellationTokenSource = new CancellationTokenSource(); ActivityFactory = provider.GetRequiredService(); FileSystem = provider.GetRequiredService(); FileOriginRegistry = provider.GetRequiredService(); + _downloadService = provider.GetRequiredService(); } public void Init(DownloaderState.ReadOnly state) { PersistentState = state; Downloaded = state.Downloaded; - _downloadLocation = new TemporaryPath(FileSystem, FileSystem.FromUnsanitizedFullPath(state.DownloadPath), false); + _downloadPath = FileSystem.FromUnsanitizedFullPath(state.DownloadPath); } /// @@ -73,13 +68,19 @@ public void RefreshState() /// protected EntityId Create(ITransaction tx) { - _downloadLocation = TemporaryFileManager.CreateFile(); + // Add a subfolder for the download task + var guid = Guid.NewGuid().ToString(); + var downloadSubfolder = _downloadService.OngoingDownloadsDirectory.Combine(guid); + downloadSubfolder.CreateDirectory(); + + _downloadPath = downloadSubfolder.Combine(guid).AppendExtension(KnownExtensions.Tmp); + var state = new DownloaderState.New(tx) { FriendlyName = "", Status = DownloadTaskStatus.Idle, Downloaded = Size.Zero, - DownloadPath = DownloadLocation.ToString(), + DownloadPath = DownloadPath.ToString(), }; return state.Id; } @@ -151,7 +152,7 @@ protected async Task MarkComplete() [Reactive] public DownloaderState.ReadOnly PersistentState { get; protected set; } - public AbsolutePath DownloadLocation => _downloadLocation; + public AbsolutePath DownloadPath => _downloadPath; [Reactive] public Bandwidth Bandwidth { get; set; } = Bandwidth.From(0); @@ -173,6 +174,10 @@ public async Task Cancel() try { await CancellationTokenSource.CancelAsync(); } catch (Exception) { /* ignored */ } await SetStatus(DownloadTaskStatus.Cancelled); + + // Cleanup the download directory (this could actually still be in use by the downloader, + // since CancelAsync is not guaranteed to wait for exception handlers to finish handling the cancellation) + CleanupDownloadFiles(); } /// @@ -193,24 +198,39 @@ public async Task Resume() await SetStatus(DownloadTaskStatus.Downloading); TransientState = new HttpDownloaderState { - Activity = ActivityFactory.Create(IHttpDownloader.Group, "Downloading {FileName}", DownloadLocation), + Activity = ActivityFactory.Create(IHttpDownloader.Group, "Downloading {FileName}", DownloadPath), }; _ = StartActivityUpdater(); Logger.LogDebug("Dispatching download task for {Name}", PersistentState.FriendlyName); - await Download(DownloadLocation, CancellationTokenSource.Token); + try + { + await Download(DownloadPath, CancellationTokenSource.Token); + } + catch (OperationCanceledException e) + { + return; + } + UpdateActivity(); await SetStatus(DownloadTaskStatus.Analyzing); Logger.LogInformation("Finished download of {Name} starting analysis", PersistentState.FriendlyName); await AnalyzeFile(); await MarkComplete(); + CleanupDownloadFiles(); + } + + // Delete the download task subfolder and all files within it. + private void CleanupDownloadFiles() + { + DownloadPath.Parent.DeleteDirectory(recursive: true); } private async Task AnalyzeFile() { try { - await FileOriginRegistry.RegisterDownload(DownloadLocation, PersistentState.Id, PersistentState.FriendlyName); + await FileOriginRegistry.RegisterDownload(DownloadPath, PersistentState.Id, PersistentState.FriendlyName); } catch (Exception ex) { diff --git a/src/Networking/NexusMods.Networking.HttpDownloader/AdvancedHttpDownloader.cs b/src/Networking/NexusMods.Networking.HttpDownloader/AdvancedHttpDownloader.cs index bd7c6a3f1a..2e2ccc6dc9 100644 --- a/src/Networking/NexusMods.Networking.HttpDownloader/AdvancedHttpDownloader.cs +++ b/src/Networking/NexusMods.Networking.HttpDownloader/AdvancedHttpDownloader.cs @@ -163,12 +163,12 @@ private async Task DownloadWithoutResume(IReadOnlyList private static async Task FinalizeDownload(DownloadState state, CancellationToken cancel) { - var tempPath = state.TempFilePath; + var progressPath = state.ProgressFilePath; if (state.HasIncompleteChunk) return Hash.Zero; state.StateFilePath.Delete(); - File.Move(tempPath.ToString(), state.Destination.ToString(), true); + File.Move(progressPath.ToString(), state.Destination.ToString(), true); return await state.Destination.XxHash64Async(token: cancel); } @@ -177,8 +177,8 @@ private static async Task FinalizeDownload(DownloadState state, Cancellati private async Task FileWriterTask(DownloadState state, ChannelReader writes, IActivitySource job, CancellationToken cancel) { - var tempPath = state.TempFilePath; - await using var file = tempPath.Open(FileMode.OpenOrCreate, FileAccess.Write); + var progressPath = state.ProgressFilePath; + await using var file = progressPath.Open(FileMode.OpenOrCreate, FileAccess.Write); file.SetLength((long)(ulong)state.TotalSize); while (true) @@ -471,7 +471,7 @@ private async Task InitiateState(AbsolutePath destination, Size s { DownloadState? state = null; var stateFilePath = DownloadState.GetStateFilePath(destination); - if (stateFilePath.FileExists && DownloadState.GetTempFilePath(destination).FileExists) + if (stateFilePath.FileExists && DownloadState.GetProgressFilePath(destination).FileExists) { _logger.LogInformation("Resuming prior download {FilePath}", destination); state = await DeserializeDownloadState(stateFilePath, cancel); diff --git a/src/Networking/NexusMods.Networking.HttpDownloader/DTOs/DownloadState.cs b/src/Networking/NexusMods.Networking.HttpDownloader/DTOs/DownloadState.cs index b07bacb61c..ea00781f80 100644 --- a/src/Networking/NexusMods.Networking.HttpDownloader/DTOs/DownloadState.cs +++ b/src/Networking/NexusMods.Networking.HttpDownloader/DTOs/DownloadState.cs @@ -69,7 +69,7 @@ public Size CompletedSize /// The file where the download is stored while it is in progress /// [JsonIgnore] - public AbsolutePath TempFilePath => GetTempFilePath(Destination); + public AbsolutePath ProgressFilePath => GetProgressFilePath(Destination); /// /// Based on the destination, get the path to the state file @@ -83,7 +83,7 @@ public Size CompletedSize /// /// /// - public static AbsolutePath GetTempFilePath(AbsolutePath destination) => destination.ReplaceExtension(new Extension(".downloading")); + public static AbsolutePath GetProgressFilePath(AbsolutePath destination) => destination.ReplaceExtension(new Extension(".downloading")); /// diff --git a/src/NexusMods.App.UI/Controls/DownloadGrid/Columns/DownloadStatus/DownloadStatusDesignViewModel.cs b/src/NexusMods.App.UI/Controls/DownloadGrid/Columns/DownloadStatus/DownloadStatusDesignViewModel.cs index 6d73af8c31..15c86378b8 100644 --- a/src/NexusMods.App.UI/Controls/DownloadGrid/Columns/DownloadStatus/DownloadStatusDesignViewModel.cs +++ b/src/NexusMods.App.UI/Controls/DownloadGrid/Columns/DownloadStatus/DownloadStatusDesignViewModel.cs @@ -71,7 +71,6 @@ public static string FormatStatus(DownloadTaskStatus state, long totalBytes, lon DownloadTaskStatus.Idle => Language.DownloadStatusDesignViewModel_FormatStatus_Queued, DownloadTaskStatus.Paused => Language.DownloadStatusDesignViewModel_FormatStatus_Paused, DownloadTaskStatus.Downloading => Language.DownloadStatusDesignViewModel_FormatStatus_Downloading, - DownloadTaskStatus.Installing => Language.DownloadStatusDesignViewModel_FormatStatus_Installing, DownloadTaskStatus.Completed => Language.DownloadStatusDesignViewModel_FormatStatus_Complete, DownloadTaskStatus.Analyzing => Language.DownloadStatusDesignViewModel_FormatStatus_Analyzing, DownloadTaskStatus.Cancelled => Language.DownloadStatusDesignViewModel_FormatStatus_Cancelled, diff --git a/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceDataStoreTests.cs b/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceDataStoreTests.cs index 18dec52295..dfda51ff2b 100644 --- a/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceDataStoreTests.cs +++ b/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceDataStoreTests.cs @@ -1,20 +1,22 @@ using FluentAssertions; -using NexusMods.Abstractions.HttpDownloader; using NexusMods.Games.RedEngine; using NexusMods.Games.TestFramework; using NexusMods.Networking.Downloaders.Interfaces; -using NexusMods.Paths; namespace NexusMods.Networking.Downloaders.Tests; -public class DownloadServiceDataStoreTests( - DownloadService downloadService, - IServiceProvider serviceProvider, - LocalHttpServer server, - TemporaryFileManager temporaryFileManager, - IHttpDownloader httpDownloader) - : AGameTest(serviceProvider) +public class DownloadServiceDataStoreTests : AGameTest { + private readonly DownloadService _downloadService; + private readonly LocalHttpServer _server; + + public DownloadServiceDataStoreTests(DownloadService downloadService, + IServiceProvider serviceProvider, + LocalHttpServer server) : base(serviceProvider) + { + _downloadService = downloadService; + _server = server; + } // Create a new instance of the DownloadService @@ -22,7 +24,7 @@ public class DownloadServiceDataStoreTests( public async Task WhenComplete_StaysPersistedInDataStore() { var currentCount = GetTaskCountIncludingCompleted(); - await downloadService.AddTask(new Uri($"{server.Prefix}Resources/RootedAtGameFolder/-Skyrim 202X 9.0 - Architecture-2347-9-0-1664994366.zip")); + await _downloadService.AddTask(new Uri($"{_server.Prefix}Resources/RootedAtGameFolder/-Skyrim 202X 9.0 - Architecture-2347-9-0-1664994366.zip")); var newCount = GetTaskCountIncludingCompleted(); newCount.Should().Be(currentCount + 1); } @@ -34,8 +36,8 @@ public async Task WhenStarted_IsPersistedInDataStore() // Should be persisted into datastore on start, because var currentCount = GetTasks().Count(); - var url = new Uri($"{server.Prefix}Resources/RootedAtGameFolder/-Skyrim 202X 9.0 - Architecture-2347-9-0-1664994366.zip"); - await downloadService.AddTask(url); + var url = new Uri($"{_server.Prefix}Resources/RootedAtGameFolder/-Skyrim 202X 9.0 - Architecture-2347-9-0-1664994366.zip"); + await _downloadService.AddTask(url); var newCount = GetTasks().Count(); newCount.Should().Be(currentCount + 1); @@ -43,13 +45,13 @@ public async Task WhenStarted_IsPersistedInDataStore() private IEnumerable GetTasks() { - return downloadService.Downloads + return _downloadService.Downloads .ToList(); } private int GetTaskCountIncludingCompleted() { - return downloadService.Downloads.Count; + return _downloadService.Downloads.Count; } } diff --git a/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceTests.cs b/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceTests.cs index 1c23ae71f7..999cd2ab65 100644 --- a/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceTests.cs +++ b/tests/Networking/NexusMods.Networking.Downloaders.Tests/DownloadServiceTests.cs @@ -10,7 +10,6 @@ public class DownloadServiceTests // For the uninitiated with xUnit: This is initialized before every test. private readonly DownloadService _downloadService; private readonly LocalHttpServer _httpServer; - private readonly TemporaryFileManager _temporaryFileManager; private IReadOnlyCollection _downloadTasks; public DownloadServiceTests(DownloadService downloadService, @@ -18,7 +17,6 @@ public DownloadServiceTests(DownloadService downloadService, { _httpServer = httpServer; _downloadService = downloadService; - _temporaryFileManager = temporaryFileManager; } [Fact] @@ -51,8 +49,8 @@ public async Task AddTask_Uri_ShouldDownload() DownloadTaskStatus.Downloading, DownloadTaskStatus.Completed); - task.DownloadLocation.FileExists.Should().BeTrue(); - (await task.DownloadLocation.ReadAllTextAsync()).Should().Be("Hello, World!"); + // File is deleted after Analyzing and repacking + task.DownloadPath.FileExists.Should().BeFalse(); task.Downloaded.Value.Should().BeGreaterThan(0); } @@ -106,8 +104,8 @@ public async Task CanSuspendDownloads() DownloadTaskStatus.Downloading, DownloadTaskStatus.Completed); - task.DownloadLocation.FileExists.Should().BeTrue(); - (await task.DownloadLocation.ReadAllTextAsync()).Should().Be("Suspended Test"); + // File is deleted after Analyzing and repacking + task.DownloadPath.FileExists.Should().BeFalse(); task.Downloaded.Value.Should().BeGreaterThan(0); } diff --git a/tests/Networking/NexusMods.Networking.Downloaders.Tests/Startup.cs b/tests/Networking/NexusMods.Networking.Downloaders.Tests/Startup.cs index a5d414c227..a4c97f8c8a 100644 --- a/tests/Networking/NexusMods.Networking.Downloaders.Tests/Startup.cs +++ b/tests/Networking/NexusMods.Networking.Downloaders.Tests/Startup.cs @@ -1,11 +1,13 @@ using Microsoft.Extensions.DependencyInjection; using NexusMods.Abstractions.GuidedInstallers; using NexusMods.Abstractions.Serialization; +using NexusMods.Abstractions.Settings; using NexusMods.App.BuildInfo; using NexusMods.Games.FOMOD; using NexusMods.Games.Generic; using NexusMods.Games.RedEngine; using NexusMods.Games.TestFramework; +using NexusMods.Paths; using NexusMods.StandardGameLocators.TestHelpers; namespace NexusMods.Networking.Downloaders.Tests; @@ -14,6 +16,9 @@ public class Startup { public void ConfigureServices(IServiceCollection services) { + const KnownPath baseKnownPath = KnownPath.EntryDirectory; + var baseDirectory = $"NexusMods.Downloaders.Tests-{Guid.NewGuid()}"; + services .AddSingleton() .AddDefaultServicesForTesting() @@ -24,6 +29,10 @@ public void ConfigureServices(IServiceCollection services) .AddSerializationAbstractions() .AddFomod() .AddDownloaders() + .OverrideSettingsForTests(settings => settings with + { + OngoingDownloadLocation = new ConfigurablePath(baseKnownPath, $"{baseDirectory}/Downloads/Ongoing"), + }) .AddSingleton() .Validate(); } diff --git a/tests/Networking/NexusMods.Networking.HttpDownloader.Tests/AdvancedHttpDownloaderTests.cs b/tests/Networking/NexusMods.Networking.HttpDownloader.Tests/AdvancedHttpDownloaderTests.cs index 987dc13a23..68ea59f931 100644 --- a/tests/Networking/NexusMods.Networking.HttpDownloader.Tests/AdvancedHttpDownloaderTests.cs +++ b/tests/Networking/NexusMods.Networking.HttpDownloader.Tests/AdvancedHttpDownloaderTests.cs @@ -70,7 +70,7 @@ public async Task CanResumeDownload() var downloadTask = _httpDownloader.DownloadAsync(sources,path, token: tokenSource.Token); var progressFile = DownloadState.GetStateFilePath(path.Path); - var downloadingFile = DownloadState.GetTempFilePath(path.Path); + var downloadingFile = DownloadState.GetProgressFilePath(path.Path); while (!progressFile.FileExists) await Task.Delay(10);