using AcDream.Launcher.Core.Updates; using AcDream.Platform; namespace AcDream.Launcher.Core.Tests.Updates; public sealed class LauncherUpdaterIntegrationTests : IDisposable { private readonly string _root = Path.Combine( Path.GetTempPath(), "acdream-updater-integration-tests", Guid.NewGuid().ToString("N")); private readonly ApplicationPathSet _paths; private readonly string _rid = LauncherRuntimeIdentity.DetectRid(); public LauncherUpdaterIntegrationTests() => _paths = UpdateTestData.Paths(_root); public void Dispose() { if (Directory.Exists(_root)) { Directory.Delete(_root, recursive: true); } } [Fact] public async Task LocalHttpManifestDownloadExtractPromotionAndNextCheckAreCoherent() { using var server = new LocalHttpFixture(); byte[] client = UpdateTestData.ClientZip(_rid, "release-2"); byte[] launcher = UpdateTestData.LauncherZip(_rid, "release-2"); ConfigureRelease(server, "2.0.0", "1.0.0", _rid, client, launcher); using var http = new HttpClient(); using var source = ReleaseManifestClient.CreateLoopbackFixture( server.UriFor("manifest.json")); var versions = new ClientVersionStore(_paths); var updater = new LauncherUpdater( source, http, versions, new LauncherSelfUpdateManager(_paths, http), LauncherVersion.Parse("1.0.0"), _rid, Path.Combine(_root, "launcher")); _ = await updater.InitializeAsync(); var progress = new List(); LauncherUpdateCheckResult check = await updater.CheckAsync(); ClientVersionResolution installed = await updater.InstallClientAsync( check, new ImmediateProgress(progress.Add)); LauncherUpdateCheckResult after = await updater.CheckAsync(); Assert.True(check.IsClientUpdateAvailable); Assert.True(check.IsLauncherUpdateAvailable); Assert.True(check.IsLauncherMinimumSatisfied); Assert.True(installed.IsVerified); Assert.Equal("2.0.0", installed.Version!.Value); Assert.False(after.IsClientUpdateAvailable); Assert.True(after.IsLauncherUpdateAvailable); Assert.Contains(progress, item => item.Phase == LauncherUpdatePhase.DownloadingClient); Assert.Contains(progress, item => item.Phase == LauncherUpdatePhase.ExtractingClient); Assert.Contains(progress, item => item.Phase == LauncherUpdatePhase.ActivatingClient); Assert.Equal(LauncherUpdatePhase.Completed, progress[^1].Phase); Assert.Empty(Directory.EnumerateFiles( versions.AppDirectory, ".client-download-*", SearchOption.TopDirectoryOnly)); } [Fact] public async Task MinimumLauncherGateAndMissingRidFailBeforePublication() { using var server = new LocalHttpFixture(); byte[] client = UpdateTestData.ClientZip(_rid); byte[] launcher = UpdateTestData.LauncherZip(_rid); ConfigureRelease(server, "3.0.0", "2.0.0", _rid, client, launcher); using var http = new HttpClient(); using var source = ReleaseManifestClient.CreateLoopbackFixture( server.UriFor("manifest.json")); var versions = new ClientVersionStore(_paths); var updater = new LauncherUpdater( source, http, versions, new LauncherSelfUpdateManager(_paths, http), LauncherVersion.Parse("1.0.0"), _rid, Path.Combine(_root, "launcher")); _ = await updater.InitializeAsync(); LauncherUpdateCheckResult check = await updater.CheckAsync(); Assert.False(check.IsLauncherMinimumSatisfied); await Assert.ThrowsAsync(() => updater.InstallClientAsync(check)); Assert.False(File.Exists(versions.CurrentPointerPath)); string otherRid = _rid == "win-x64" ? "linux-x64" : "win-x64"; ConfigureRelease(server, "3.0.0", "1.0.0", otherRid, UpdateTestData.ClientZip(otherRid), UpdateTestData.LauncherZip(otherRid)); LauncherUpdateException missingRid = await Assert.ThrowsAsync( () => updater.CheckAsync()); Assert.Contains(_rid, missingRid.Message, StringComparison.Ordinal); } [Fact] public async Task RunningPredicateAndCrossProcessBarrierRefuseUpdateWithoutNetworkMutation() { using var server = new LocalHttpFixture(); byte[] client = UpdateTestData.ClientZip(_rid); byte[] launcher = UpdateTestData.LauncherZip(_rid); ConfigureRelease(server, "2.0.0", "1.0.0", _rid, client, launcher); using var http = new HttpClient(); using var source = ReleaseManifestClient.CreateLoopbackFixture( server.UriFor("manifest.json")); var versions = new ClientVersionStore(_paths); bool running = true; var updater = new LauncherUpdater( source, http, versions, new LauncherSelfUpdateManager(_paths, http), LauncherVersion.Parse("1.0.0"), _rid, Path.Combine(_root, "launcher"), () => running); _ = await updater.InitializeAsync(); LauncherUpdateCheckResult check = await updater.CheckAsync(); LauncherUpdateException local = await Assert.ThrowsAsync(() => updater.InstallClientAsync(check)); Assert.Contains("Stop every", local.Message, StringComparison.Ordinal); Assert.False(File.Exists(versions.CurrentPointerPath)); running = false; using UpdateSessionBarrier.SessionLease session = versions.Barrier.AcquireSession(); LauncherUpdateException shared = await Assert.ThrowsAsync(() => updater.InstallClientAsync(check)); Assert.Contains("session", shared.Message, StringComparison.OrdinalIgnoreCase); Assert.False(File.Exists(versions.CurrentPointerPath)); } [Fact] public async Task CancelledSlowClientDownloadLeavesNoPointerOrStaging() { using var server = new LocalHttpFixture(); byte[] client = UpdateTestData.ClientZip(_rid, new string('x', 1_000_000)); byte[] launcher = UpdateTestData.LauncherZip(_rid); server.Add("client.zip", client, chunkSize: 1024, chunkDelay: TimeSpan.FromMilliseconds(2)); server.Add("launcher.zip", launcher); server.Add( "manifest.json", UpdateTestData.Manifest( "2.0.0", "1.0.0", _rid, server.UriFor("client.zip"), client, server.UriFor("launcher.zip"), launcher), contentType: "application/json"); using var http = new HttpClient(); using var source = ReleaseManifestClient.CreateLoopbackFixture( server.UriFor("manifest.json")); var versions = new ClientVersionStore(_paths); var updater = new LauncherUpdater( source, http, versions, new LauncherSelfUpdateManager(_paths, http), LauncherVersion.Parse("1.0.0"), _rid, Path.Combine(_root, "launcher")); _ = await updater.InitializeAsync(); LauncherUpdateCheckResult check = await updater.CheckAsync(); using var cancellation = new CancellationTokenSource(TimeSpan.FromMilliseconds(30)); await Assert.ThrowsAnyAsync(() => updater.InstallClientAsync(check, cancellationToken: cancellation.Token)); Assert.False(File.Exists(versions.CurrentPointerPath)); Assert.Empty(Directory.EnumerateFileSystemEntries( versions.AppDirectory, ".client-*", SearchOption.TopDirectoryOnly)); } private static void ConfigureRelease( LocalHttpFixture server, string version, string minimum, string rid, byte[] client, byte[] launcher) { server.Add("client.zip", client); server.Add("launcher.zip", launcher); server.Add( "manifest.json", UpdateTestData.Manifest( version, minimum, rid, server.UriFor("client.zip"), client, server.UriFor("launcher.zip"), launcher), contentType: "application/json"); } private sealed class ImmediateProgress(Action callback) : IProgress { public void Report(LauncherUpdateProgress value) => callback(value); } }