feat.PLONDS客户端补全

This commit is contained in:
lincube
2026-06-02 13:16:13 +08:00
parent 54d97e312d
commit 0ea98c08bf
10 changed files with 521 additions and 30 deletions

View File

@@ -91,6 +91,25 @@ public sealed class PlondsClientServiceTests : IDisposable
Assert.Contains("full package fallback also failed", result.ErrorMessage);
}
[Fact]
public async Task DownloadPlanner_WhenManifestRequiresCleanInstall_DoesNotPreparePlondsPackage()
{
var downloader = new FakeDownloader(deltaFails: false, fullFails: false);
var planner = new PlondsDownloadPlanner(downloader);
var result = await planner.PrepareAsync(
new PlondsManifestCandidate(
new("s3", "s3", "https://s3.test/PLONDS.json"),
CreateManifest("1.2.3", requiresCleanInstall: true)),
CancellationToken.None);
Assert.False(result.Success);
Assert.True(result.RequiresUiHandling);
Assert.Contains("clean install", result.ErrorMessage);
Assert.Equal(0, downloader.DeltaCalls);
Assert.Equal(0, downloader.FullCalls);
}
[Fact]
public async Task PlondsService_ReadsBuiltInSources_RegistersManifestSources_AndPreparesHighestVersion()
{
@@ -411,18 +430,62 @@ public sealed class PlondsClientServiceTests : IDisposable
Assert.True(File.Exists(Path.Combine(currentDeployment, ".destroy")));
}
[Fact]
public async Task PreparedPackageInstaller_InstallsFullPackageFromCompleteRootLayout()
{
var launcherRoot = Path.Combine(_tempRoot, "launcher-full");
var currentDeployment = Path.Combine(launcherRoot, "app-1.0.0-0");
Directory.CreateDirectory(currentDeployment);
File.WriteAllText(Path.Combine(currentDeployment, ".current"), string.Empty);
File.WriteAllText(Path.Combine(currentDeployment, "LanMountainDesktop.exe"), "old-exe");
File.WriteAllText(Path.Combine(currentDeployment, "app.dll"), "old");
var filesRoot = Path.Combine(_tempRoot, "Files-root");
var fullAppDirectory = Path.Combine(filesRoot, "app-1.2.0");
Directory.CreateDirectory(fullAppDirectory);
File.WriteAllText(Path.Combine(filesRoot, "LanMountainDesktop.Launcher.exe"), "launcher");
File.WriteAllText(Path.Combine(filesRoot, "LanMountainDesktop.AirAppRuntime.exe"), "runtime");
File.WriteAllText(Path.Combine(fullAppDirectory, ".current"), string.Empty);
File.WriteAllText(Path.Combine(fullAppDirectory, "LanMountainDesktop.exe"), "new-exe");
File.WriteAllText(Path.Combine(fullAppDirectory, "app.dll"), "new");
var package = new PlondsPreparedPackage(
new Version(1, 2, 0),
PlondsPackageMode.Full,
Path.Combine(_tempRoot, "PLONDS.json"),
null,
null,
Path.Combine(_tempRoot, "Files.zip"),
filesRoot);
var result = await new PlondsPreparedPackageInstaller().InstallAsync(
package,
launcherRoot,
progress: null,
CancellationToken.None);
Assert.True(result.Success);
var target = Assert.Single(Directory.GetDirectories(launcherRoot, "app-1.2.0-*"));
Assert.Equal("new-exe", File.ReadAllText(Path.Combine(target, "LanMountainDesktop.exe")));
Assert.Equal("new", File.ReadAllText(Path.Combine(target, "app.dll")));
Assert.False(File.Exists(Path.Combine(target, "LanMountainDesktop.Launcher.exe")));
Assert.True(File.Exists(Path.Combine(target, ".current")));
Assert.True(File.Exists(Path.Combine(currentDeployment, ".destroy")));
}
private static PlondsClientManifest CreateManifest(
string version,
IReadOnlyList<PlondsSourceDescriptor>? sources = null,
PlondsClientDownloads? downloads = null,
IReadOnlyDictionary<string, string>? checksums = null)
IReadOnlyDictionary<string, string>? checksums = null,
bool requiresCleanInstall = false)
{
return new PlondsClientManifest(
FormatVersion: "2.0",
CurrentVersion: version,
PreviousVersion: "1.0.0",
IsFullUpdate: false,
RequiresCleanInstall: false,
RequiresCleanInstall: requiresCleanInstall,
Channel: "stable",
Platform: "windows-x64",
UpdatedAt: DateTimeOffset.Parse("2026-06-01T00:00:00Z"),

View File

@@ -140,6 +140,43 @@ public sealed class UpdateSettingsInterfaceTests
Assert.False(orchestratorCreated);
}
[Fact]
public async Task UpdateSettingsService_WhenPlondsManifestRequiresCleanInstall_ReportsFullInstaller()
{
var settings = new FakeSettingsService
{
Snapshot =
{
UpdateDownloadSource = UpdateSettingsValues.DownloadSourcePlonds
}
};
var plonds = new FakePlondsService
{
LatestResult = PlondsLatestResult.Available(
new Version(1, 0, 0),
new Version(9, 9, 9),
[new PlondsManifestCandidate(
new PlondsSourceDescriptor("s3", "s3", "https://s3.test/PLONDS.json", 100),
CreatePlondsManifest("9.9.9", requiresCleanInstall: true))])
};
var orchestratorCreated = false;
var service = new UpdateSettingsService(
settings,
orchestratorFactory: () =>
{
orchestratorCreated = true;
throw new InvalidOperationException("UpdateOrchestrator should not be created for PLONDS check.");
},
plondsService: plonds);
var report = await service.CheckAsync(CancellationToken.None);
Assert.True(report.IsUpdateAvailable);
Assert.Equal(UpdatePayloadKind.FullInstaller, report.PayloadKind);
Assert.Equal("9.9.9", report.LatestVersion);
Assert.False(orchestratorCreated);
}
[Fact]
public async Task UpdateSettingsService_WhenGitHubSelected_UsesOrchestrator()
{
@@ -226,14 +263,14 @@ public sealed class UpdateSettingsInterfaceTests
new UpdateStateStore(new FakeSettingsFacade(new FakeUpdateSettingsService { State = state })));
}
private static PlondsClientManifest CreatePlondsManifest(string version)
private static PlondsClientManifest CreatePlondsManifest(string version, bool requiresCleanInstall = false)
{
return new PlondsClientManifest(
FormatVersion: "2.0",
CurrentVersion: version,
PreviousVersion: "1.0.0",
IsFullUpdate: false,
RequiresCleanInstall: false,
RequiresCleanInstall: requiresCleanInstall,
Channel: "stable",
Platform: "windows-x64",
UpdatedAt: DateTimeOffset.Parse("2026-06-01T00:00:00Z"),