/* * Copyright (c) 2023 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using System; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; using ProtonVPN.Common.Legacy.OS.DeviceIds; using ProtonVPN.Common.Legacy.OS.Net.Http; using ProtonVPN.Configurations.Contracts; using ProtonVPN.Logging.Contracts; using ProtonVPN.Tests.Common; using ProtonVPN.Update.Config; using ProtonVPN.Update.Contracts.Config; using ProtonVPN.Update.Files.Launchable; using ProtonVPN.Update.Updates; // ReSharper disable ObjectCreationAsStatement namespace ProtonVPN.Update.Tests.Updates { [TestClass] public class AppUpdatesTest { private IConfiguration _configuration; private IDeviceIdCache _deviceIdCache; private ILogger _logger; private ILaunchableFile _launchableFile; private IHttpClient _httpClient; private IFeedUrlProvider _feedUrlProvider; private DefaultAppUpdateConfig _config; private Uri _feedUrl = new Uri("http://127.0.0.1/windows-releases.json"); #region Initialization [TestInitialize] public void TestInitialize() { _configuration = Substitute.For(); _deviceIdCache = Substitute.For(); _logger = Substitute.For(); _launchableFile = Substitute.For(); _httpClient = Substitute.For(); _feedUrlProvider = Substitute.For(); _feedUrlProvider.GetFeedUrl().Returns(_feedUrl); _config = new DefaultAppUpdateConfig { FeedHttpClient = _httpClient, FileHttpClient = _httpClient, FeedUriProvider = _feedUrlProvider, UpdatesPath = "Updates", CurrentVersion = new Version(), EarlyAccessCategoryName = "EarlyAccess" }; } private IAppUpdates AppUpdates(Version version, [CallerMemberName] string updatesPath = null) { _config.CurrentVersion = version; _config.UpdatesPath = updatesPath; return AppUpdates(); } private IAppUpdates AppUpdates() { return new AppUpdates(_config, _launchableFile, _logger, _deviceIdCache, _configuration); } #endregion #region Test: AppUpdates [TestMethod] public void AppUpdates_ShouldNotTrow() { Action f = () => AppUpdates(); f.Should().NotThrow(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenConfig_IsNull() { Action f = () => new AppUpdates(null, _launchableFile, _logger, _deviceIdCache, _configuration); f.Should().Throw(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenHttpClient_IsNull() { _config.FeedHttpClient = null; _config.FileHttpClient = null; Action f = () => AppUpdates(); f.Should().Throw(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenCurrentVersion_IsNull() { _config.CurrentVersion = null; Action f = () => AppUpdates(); f.Should().Throw(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenFeedUri_IsNull() { _config.FeedUriProvider = null; Action f = () => AppUpdates(); f.Should().Throw(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenUpdatesPath_IsNull() { _config.UpdatesPath = null; Action f = () => AppUpdates(); f.Should().Throw(); } [TestMethod] public void AppUpdates_ShouldTrow_WhenUpdatesPath_IsEmpty() { _config.UpdatesPath = ""; Action f = () => AppUpdates(); f.Should().Throw(); } #endregion #region Test: Cleanup [TestMethod] public void Cleanup_ShouldDelete_Subdirectories_FromDownloadsDirectory() { string updatesPath = TestConfig.GetFolderPath(); Directory.CreateDirectory(Path.Combine(updatesPath, "2.2.2")); Directory.CreateDirectory(Path.Combine(updatesPath, "Some", "Another")); CopyFile("Empty file.txt", updatesPath); CopyFile("Empty file.txt", Path.Combine(updatesPath, "Some", "2nd")); IAppUpdates updater = AppUpdates(new Version(1, 2, 0), updatesPath); updater.Cleanup(); string[] directories = Directory.GetDirectories(updatesPath); Directory.Delete(updatesPath, true); directories.Should().BeEmpty(); } [TestMethod] public void Cleanup_ShouldDelete_AllNotExeFiles_FromDownloadsDirectory() { string updatesPath = TestConfig.GetFolderPath(); CopyFile("Empty file.txt", updatesPath); CopyFile("Empty file.txt", updatesPath, "Without extension"); CopyFile("ProtonVPN_win_v1.0.0.exe", updatesPath, "Some.1"); CopyFile("ProtonVPN_win_v2.0.0.exe", updatesPath, "Later version not exe.dll"); IAppUpdates updater = AppUpdates(new Version(1, 2, 0), updatesPath); updater.Cleanup(); string[] files = Directory.GetFiles(updatesPath, "*", SearchOption.AllDirectories); Directory.Delete(updatesPath, true); files.Should().BeEmpty(); } [TestMethod] public void Cleanup_ShouldDelete_OutdatedExeFiles_FromDownloadsDirectory() { string updatesPath = TestConfig.GetFolderPath(); CopyFile("Empty file.txt", updatesPath, "Unknown.exe"); CopyFile("ProtonVPN_win_v1.0.0.exe", updatesPath); CopyFile("ProtonVPN_win_v1.5.0.exe", updatesPath); CopyFile("ProtonVPN_win_v1.5.1.exe", updatesPath); IAppUpdates updater = AppUpdates(new Version(1, 5, 1), updatesPath); updater.Cleanup(); string[] files = Directory.GetFiles(updatesPath, "*", SearchOption.AllDirectories); Directory.Delete(updatesPath, true); files.Should() .HaveCount(1) .And.Match(f => Path.GetFileName(f.First()) == "ProtonVPN_win_v1.5.1.exe"); } #endregion #region Helpers private static void CopyFile(string sourcePath, string destPath, string newFilename = null) { if (!string.IsNullOrEmpty(destPath)) { Directory.CreateDirectory(destPath); } string filename = !string.IsNullOrEmpty(newFilename) ? newFilename : Path.GetFileName(sourcePath); string destFullPath = Path.Combine(destPath ?? "", filename ?? ""); File.Copy(TestConfig.GetFolderPath(sourcePath), destFullPath); } #endregion } }