/*
* 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.Threading.Tasks;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using ProtonVPN.Tests.Common;
using ProtonVPN.Update.Files.Validatable;
namespace ProtonVPN.Update.Tests.Files.Validatable
{
[TestClass]
public class CachedFileValidatorTest
{
private IFileValidator _origin;
private IFileValidator _cachedFileValidator;
[TestInitialize]
public void TestInitialize()
{
_origin = Substitute.For();
_cachedFileValidator = new CachedFileValidator(_origin);
}
[TestMethod]
public async Task Valid_ShouldCall_Origin_Valid_WithArguments()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
const string checkSum = "The expected check sum";
await _cachedFileValidator.Valid(filename, checkSum);
await _origin.Received(1).Valid(filename, checkSum);
}
[TestMethod]
[DataRow(false)]
[DataRow(true)]
public async Task Valid_ShouldBe_Origin_Valid(bool value)
{
const string filename = "TestData\\ProtonVPN_win_v1.5.2.exe";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(value));
bool result = await _cachedFileValidator.Valid(filename, "checkSum");
result.Should().Be(value);
}
[TestMethod]
public async Task Valid_ShouldBeTrue_SecondTime_WenFileHasNotChanged()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
const string checkSum = "b1dc9dbd738a5f98b7f5e920ffcc5ba9db42517e";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
bool result = await _cachedFileValidator.Valid(filename, checkSum);
result.Should().BeTrue();
}
[TestMethod]
public async Task Valid_ShouldNotCall_Origin_SecondTime_WenFileHasNotChanged()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
const string checkSum = "b1dc9dbd738a5f98b7f5e920ffcc5ba9db42517e";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
await _cachedFileValidator.Valid(filename, checkSum);
await _origin.DidNotReceiveWithAnyArgs().Valid("", "");
}
[TestMethod]
public async Task Valid_ShouldCall_Origin_WenFilename_HasChanged()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
const string checkSum = "b1dc9dbd738a5f98b7f5e920ffcc5ba9db42517e";
const string changedFilename = "TestData\\ProtonVPN_win_v1.5.0.exe";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
await _cachedFileValidator.Valid(changedFilename, checkSum);
await _origin.Received().Valid(changedFilename, checkSum);
}
[TestMethod]
public async Task Valid_ShouldCall_Origin_WenCheckSum_HasChanged()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
const string checkSum = "b1dc9dbd738a5f98b7f5e920ffcc5ba9db42517e";
const string changedCheckSum = "Changed Check Sum";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
await _cachedFileValidator.Valid(filename, changedCheckSum);
await _origin.Received().Valid(filename, changedCheckSum);
}
[TestMethod]
public async Task Valid_ShouldCall_Origin_WenFileLastWriteTime_HasChanged()
{
string updatesPath = TestConfig.GetFolderPath();
CopyFile("ProtonVPN_win_v1.5.1.exe", updatesPath);
string filename = Path.Combine(updatesPath, "ProtonVPN_win_v1.5.1.exe");
const string checkSum = "ba6b5ca2db65ff7817e3336a386e7525c01dc639";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
File.SetLastWriteTimeUtc(filename, new DateTime(2000, 04, 28, 12, 15, 33));
await _cachedFileValidator.Valid(filename, checkSum);
await _origin.Received().Valid(filename, checkSum);
}
[TestMethod]
public async Task Valid_ShouldCall_Origin_WenFileLength_HasChanged()
{
string updatesPath = TestConfig.GetFolderPath();
CopyFile("ProtonVPN_win_v1.5.1.exe", updatesPath);
string filename = Path.Combine(updatesPath, "ProtonVPN_win_v1.5.1.exe");
const string checkSum = "ba6b5ca2db65ff7817e3336a386e7525c01dc639";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
DateTime lastWriteTime = File.GetLastWriteTimeUtc(filename);
CopyFile("windows-releases.json", updatesPath, "ProtonVPN_win_v1.5.1.exe");
File.SetLastWriteTimeUtc(filename, lastWriteTime);
await _cachedFileValidator.Valid(filename, checkSum);
await _origin.Received().Valid(filename, checkSum);
}
[TestMethod]
public async Task Valid_ShouldBeFalse_WenFileDoesNotExist()
{
string updatesPath = TestConfig.GetFolderPath();
CopyFile("ProtonVPN_win_v1.5.1.exe", updatesPath);
string filename = Path.Combine(updatesPath, "ProtonVPN_win_v1.5.1.exe");
const string checkSum = "ba6b5ca2db65ff7817e3336a386e7525c01dc639";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(true));
(await _cachedFileValidator.Valid(filename, checkSum)).Should().BeTrue();
_origin.ClearReceivedCalls();
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromResult(false));
File.Delete(filename);
bool result = await _cachedFileValidator.Valid(filename, checkSum);
result.Should().BeFalse();
}
[TestMethod]
public void Valid_ShouldPassException_WhenOriginThrows()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
_origin.Valid("", "").ThrowsForAnyArgs();
Func action = () => _cachedFileValidator.Valid(filename, "");
action.Should().ThrowAsync();
}
[TestMethod]
public void Valid_ShouldPassException_WhenOriginThrowsAsync()
{
const string filename = "TestData\\ProtonVPN_win_v1.0.0.exe";
_origin.Valid("", "").ReturnsForAnyArgs(Task.FromException(new SomeException()));
Func action = () => _cachedFileValidator.Valid(filename, "");
action.Should().ThrowAsync();
}
#region Helpers
private class SomeException : Exception { }
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, true);
}
#endregion
}
}