mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
changed.对启动器重构的尝试
This commit is contained in:
250
LanMountainDesktop.Tests/UpdateStrategyTests.cs
Normal file
250
LanMountainDesktop.Tests/UpdateStrategyTests.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System.IO.Compression;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using LanMountainDesktop.Launcher;
|
||||
using LanMountainDesktop.Launcher.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace LanMountainDesktop.Tests;
|
||||
|
||||
public sealed class PendingUpdateDetectorTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public void ValidateIncomingState_WhenNoPayloadButDeploymentLockExists_ReturnsNoop()
|
||||
{
|
||||
_root.WriteDeploymentLock();
|
||||
var detector = new PendingUpdateDetector(
|
||||
new DeploymentLocator(_root.AppRoot),
|
||||
_root.Paths,
|
||||
new UpdateSignatureVerifier(_root.Paths));
|
||||
|
||||
var result = detector.ValidateIncomingState();
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal("noop", result.Code);
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
}
|
||||
|
||||
public sealed class UpdateSignatureVerifierTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public void Verify_WhenSignatureIsMissing_ReturnsStructuredFailure()
|
||||
{
|
||||
var payload = Path.Combine(_root.Paths.IncomingRoot, "files.json");
|
||||
Directory.CreateDirectory(_root.Paths.IncomingRoot);
|
||||
File.WriteAllText(payload, "{}");
|
||||
|
||||
var result = new UpdateSignatureVerifier(_root.Paths)
|
||||
.Verify(payload, Path.Combine(_root.Paths.IncomingRoot, "files.json.sig"), "files.json.sig");
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal("Missing files.json.sig.", result.Message);
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
}
|
||||
|
||||
public sealed class IncomingArtifactsCleanerTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public void Cleanup_RemovesLegacyPlondsAndCheckpointArtifacts()
|
||||
{
|
||||
Directory.CreateDirectory(_root.Paths.PlondsObjectsRoot);
|
||||
foreach (var path in new[]
|
||||
{
|
||||
_root.Paths.FileMapPath,
|
||||
_root.Paths.SignaturePath,
|
||||
_root.Paths.ArchivePath,
|
||||
_root.Paths.PlondsFileMapPath,
|
||||
_root.Paths.PlondsSignaturePath,
|
||||
_root.Paths.PlondsUpdateMetadataPath,
|
||||
_root.Paths.InstallCheckpointPath,
|
||||
Path.Combine(_root.Paths.PlondsObjectsRoot, "payload")
|
||||
})
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
File.WriteAllText(path, "x");
|
||||
}
|
||||
|
||||
new IncomingArtifactsCleaner(_root.Paths).Cleanup();
|
||||
|
||||
Assert.False(File.Exists(_root.Paths.FileMapPath));
|
||||
Assert.False(File.Exists(_root.Paths.InstallCheckpointPath));
|
||||
Assert.False(Directory.Exists(_root.Paths.PlondsObjectsRoot));
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
}
|
||||
|
||||
public sealed class DeploymentActivatorTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public void Activate_MovesCurrentMarkerAndMarksPreviousDestroy()
|
||||
{
|
||||
var from = Path.Combine(_root.AppRoot, "app-1");
|
||||
var to = Path.Combine(_root.AppRoot, "app-2");
|
||||
Directory.CreateDirectory(from);
|
||||
Directory.CreateDirectory(to);
|
||||
File.WriteAllText(Path.Combine(from, ".current"), string.Empty);
|
||||
File.WriteAllText(Path.Combine(to, ".partial"), string.Empty);
|
||||
|
||||
new DeploymentActivator(new DeploymentLocator(_root.AppRoot)).Activate(from, to);
|
||||
|
||||
Assert.False(File.Exists(Path.Combine(from, ".current")));
|
||||
Assert.True(File.Exists(Path.Combine(from, ".destroy")));
|
||||
Assert.True(File.Exists(Path.Combine(to, ".current")));
|
||||
Assert.False(File.Exists(Path.Combine(to, ".partial")));
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
}
|
||||
|
||||
public sealed class RollbackStrategyTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public void RollbackLatest_WhenNoSnapshotsExist_ReturnsNoSnapshot()
|
||||
{
|
||||
var snapshotStore = new UpdateSnapshotStore(_root.Paths);
|
||||
var activator = new DeploymentActivator(new DeploymentLocator(_root.AppRoot));
|
||||
|
||||
var result = new RollbackStrategy(new DeploymentLocator(_root.AppRoot), snapshotStore, activator)
|
||||
.RollbackLatest();
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal("no_snapshot", result.Code);
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
}
|
||||
|
||||
public sealed class PlondsUpdateApplierTests
|
||||
{
|
||||
[Fact]
|
||||
public void ManifestParser_ReadsObjectComponentFiles()
|
||||
{
|
||||
var map = new PlondsFileMap();
|
||||
var entries = PlondsManifestParser.CollectFileEntries(map);
|
||||
|
||||
PlondsManifestParser.PopulateFromRawJson(
|
||||
"""
|
||||
{
|
||||
"toVersion": "2.0.0",
|
||||
"components": {
|
||||
"desktop": {
|
||||
"files": {
|
||||
"LanMountainDesktop.exe": {
|
||||
"archiveSha512": "abcd",
|
||||
"archivePath": "objects/ab/cd"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
map,
|
||||
entries);
|
||||
|
||||
Assert.Equal("2.0.0", PlondsManifestParser.ResolveTargetVersion(map, null));
|
||||
var entry = Assert.Single(entries);
|
||||
Assert.Equal("LanMountainDesktop.exe", entry.Path);
|
||||
Assert.Equal("desktop", entry.Metadata["component"]);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LegacyUpdateApplierTests : IDisposable
|
||||
{
|
||||
private readonly TempLauncherRoot _root = new();
|
||||
|
||||
[Fact]
|
||||
public async Task ApplyAsync_WhenSignatureIsMissing_ReturnsSignatureFailure()
|
||||
{
|
||||
_root.WriteDeploymentLock();
|
||||
Directory.CreateDirectory(_root.Paths.IncomingRoot);
|
||||
File.WriteAllText(_root.Paths.FileMapPath, JsonSerializer.Serialize(new SignedFileMap
|
||||
{
|
||||
FromVersion = "1.0.0",
|
||||
ToVersion = "2.0.0",
|
||||
Files = [new UpdateFileEntry { Path = "state.txt" }]
|
||||
}, AppJsonContext.Default.SignedFileMap));
|
||||
using (var archive = ZipFile.Open(_root.Paths.ArchivePath, ZipArchiveMode.Create))
|
||||
{
|
||||
var entry = archive.CreateEntry("state.txt");
|
||||
await using var stream = entry.Open();
|
||||
await stream.WriteAsync(Encoding.UTF8.GetBytes("state"));
|
||||
}
|
||||
|
||||
var applier = CreateLegacyApplier();
|
||||
var result = await applier.ApplyAsync();
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal("signature_failed", result.Code);
|
||||
}
|
||||
|
||||
public void Dispose() => _root.Dispose();
|
||||
|
||||
private LegacyUpdateApplier CreateLegacyApplier()
|
||||
{
|
||||
var locator = new DeploymentLocator(_root.AppRoot);
|
||||
var snapshotStore = new UpdateSnapshotStore(_root.Paths);
|
||||
var checkpointStore = new InstallCheckpointStore(_root.Paths);
|
||||
var activator = new DeploymentActivator(locator);
|
||||
var cleaner = new IncomingArtifactsCleaner(_root.Paths);
|
||||
return new LegacyUpdateApplier(
|
||||
locator,
|
||||
_root.Paths,
|
||||
new UpdateSignatureVerifier(_root.Paths),
|
||||
new NullUpdateProgressReporter(),
|
||||
snapshotStore,
|
||||
checkpointStore,
|
||||
activator,
|
||||
cleaner);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TempLauncherRoot : IDisposable
|
||||
{
|
||||
public TempLauncherRoot()
|
||||
{
|
||||
AppRoot = Path.Combine(Path.GetTempPath(), "lmd-launcher-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(AppRoot);
|
||||
Paths = new UpdateEnginePaths(AppRoot);
|
||||
Directory.CreateDirectory(Paths.IncomingRoot);
|
||||
}
|
||||
|
||||
public string AppRoot { get; }
|
||||
|
||||
public UpdateEnginePaths Paths { get; }
|
||||
|
||||
public void WriteDeploymentLock()
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(Paths.DeploymentLockPath)!);
|
||||
File.WriteAllText(Paths.DeploymentLockPath, string.Empty);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(AppRoot))
|
||||
{
|
||||
Directory.Delete(AppRoot, true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user