157 lines
5.9 KiB
C#
157 lines
5.9 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
using CursorLang.Core.Models;
|
|
using CursorLang.Core.Services;
|
|
using CursorLang.Tests.Shared;
|
|
|
|
namespace CursorLang.Core.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// What the app does with a release once it has found one: whether it is newer
|
|
/// at all, and what ends up on disk.
|
|
/// </summary>
|
|
public sealed class UpdateServiceTests
|
|
{
|
|
[Theory]
|
|
[InlineData("1.0.0.0", "1.0.1.0", true)]
|
|
[InlineData("1.0.0.0", "2.0.0.0", true)]
|
|
[InlineData("1.0.0.0", "1.0.0.0", false)]
|
|
[InlineData("1.0.1.0", "1.0.0.0", false)]
|
|
public async Task Only_a_higher_version_counts_as_an_update(string current, string found, bool offered)
|
|
{
|
|
var feed = new FakeReleaseFeed { Release = Release(found) };
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(feed, folder, current);
|
|
|
|
ReleaseInfo? update = await service.CheckAsync(TestContext.Current.CancellationToken);
|
|
|
|
Assert.Equal(offered, update is not null);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task An_empty_repository_leaves_the_app_with_nothing()
|
|
{
|
|
var feed = new FakeReleaseFeed { Release = null };
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(feed, folder);
|
|
|
|
Assert.Null(await service.CheckAsync(TestContext.Current.CancellationToken));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task The_package_ends_up_on_disk_whole()
|
|
{
|
|
byte[] content = Encoding.UTF8.GetBytes(new string('p', 300_000));
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(new FakeReleaseFeed(), folder, client: FakeHttpHandler.Bytes(content));
|
|
|
|
string path = await service.DownloadAsync(Release("2.0.0.0"), null, TestContext.Current.CancellationToken);
|
|
|
|
Assert.Equal(content, await File.ReadAllBytesAsync(path, TestContext.Current.CancellationToken));
|
|
}
|
|
|
|
// The name comes from the version, not from the answer: the app creates a
|
|
// file with it, and the answer comes from the other side
|
|
[Fact]
|
|
public async Task The_name_of_the_file_is_built_by_the_app_itself()
|
|
{
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(new FakeReleaseFeed(), folder, client: FakeHttpHandler.Bytes([1, 2, 3]));
|
|
|
|
var release = new ReleaseInfo(
|
|
new Version(2, 0, 0, 0),
|
|
"v2.0.0",
|
|
null,
|
|
new ReleaseAsset(@"..\..\evil.msixbundle", new Uri("https://host/a"), 3));
|
|
|
|
string path = await service.DownloadAsync(release, null, TestContext.Current.CancellationToken);
|
|
|
|
Assert.Equal("CursorLang-2.0.0.0.msixbundle", Path.GetFileName(path));
|
|
Assert.Equal(folder.Path, Path.GetDirectoryName(path));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task The_download_reports_how_far_it_has_come()
|
|
{
|
|
byte[] content = new byte[500_000];
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(new FakeReleaseFeed(), folder, client: FakeHttpHandler.Bytes(content));
|
|
|
|
var reported = new CollectingProgress();
|
|
await service.DownloadAsync(Release("2.0.0.0"), reported, TestContext.Current.CancellationToken);
|
|
|
|
Assert.NotEmpty(reported.Values);
|
|
Assert.All(reported.Values, value => Assert.InRange(value, 0, 1));
|
|
Assert.Equal(reported.Values, [.. reported.Values.Order()]);
|
|
Assert.Equal(1, reported.Values[^1]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_closed_repository_gets_the_token_with_the_download_too()
|
|
{
|
|
var feed = new FakeReleaseFeed();
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(feed, folder, client: FakeHttpHandler.Bytes([1]));
|
|
|
|
await service.DownloadAsync(Release("2.0.0.0"), null, TestContext.Current.CancellationToken);
|
|
|
|
Assert.Single(feed.Authorized);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_refusal_of_the_hosting_service_leaves_no_package_behind()
|
|
{
|
|
using TempFolder folder = new();
|
|
using UpdateService service = Create(
|
|
new FakeReleaseFeed(), folder, client: FakeHttpHandler.Status(HttpStatusCode.NotFound));
|
|
|
|
await Assert.ThrowsAsync<HttpRequestException>(
|
|
() => service.DownloadAsync(Release("2.0.0.0"), null, TestContext.Current.CancellationToken));
|
|
|
|
Assert.Empty(Directory.GetFiles(folder.Path));
|
|
}
|
|
|
|
// A package left from an earlier download takes up room and is of no use
|
|
// once it has been installed
|
|
[Fact]
|
|
public async Task An_older_download_is_cleared_away()
|
|
{
|
|
using TempFolder folder = new();
|
|
string leftover = folder.File("CursorLang-1.5.0.0.msixbundle");
|
|
await File.WriteAllTextAsync(leftover, "old", TestContext.Current.CancellationToken);
|
|
|
|
using UpdateService service = Create(new FakeReleaseFeed(), folder, client: FakeHttpHandler.Bytes([1]));
|
|
string path = await service.DownloadAsync(Release("2.0.0.0"), null, TestContext.Current.CancellationToken);
|
|
|
|
Assert.False(File.Exists(leftover));
|
|
Assert.True(File.Exists(path));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The reports as the download makes them. <c>Progress<T></c> would
|
|
/// hand them over to another thread, and a test has nowhere to wait for that.
|
|
/// </summary>
|
|
private sealed class CollectingProgress : IProgress<double>
|
|
{
|
|
internal List<double> Values { get; } = [];
|
|
|
|
public void Report(double value) => Values.Add(value);
|
|
}
|
|
|
|
private static ReleaseInfo Release(string version) => new(
|
|
Version.Parse(version),
|
|
$"v{version}",
|
|
new Uri("https://host/releases/tag"),
|
|
new ReleaseAsset($"CursorLang-{version}.msixbundle", new Uri("https://host/package"), 0));
|
|
|
|
private static UpdateService Create(
|
|
IReleaseFeed feed,
|
|
TempFolder folder,
|
|
string current = "1.0.0.0",
|
|
FakeHttpHandler? client = null) =>
|
|
new(feed,
|
|
(client ?? FakeHttpHandler.Bytes([])).CreateClient(),
|
|
Version.Parse(current),
|
|
folder.Path);
|
|
}
|