From d2a20c7e12a8d223f8121a9946c57c2b3fceabd0 Mon Sep 17 00:00:00 2001 From: pallyoung Date: Tue, 25 Aug 2026 12:56:45 +0800 Subject: [PATCH] Handle terminal runtime acceptance recovery --- .../verify-desktop-installed-update.test.ts | 21 ++++++++++ scripts/verify-desktop-installed-update.ts | 38 +++++++++++-------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/scripts/verify-desktop-installed-update.test.ts b/scripts/verify-desktop-installed-update.test.ts index 0163c82f..dd0ad3d0 100644 --- a/scripts/verify-desktop-installed-update.test.ts +++ b/scripts/verify-desktop-installed-update.test.ts @@ -183,6 +183,9 @@ describe("verify-desktop-installed-update", () => { expect(driver).toContain("session = await connectBrowser(activeCdpUrl);"); expect(driver).toContain("allowFailedFrozenState?: boolean;"); expect(driver).toContain('values.get("allow-failed-frozen-state") === "true"'); + expect(driver).toContain( + 'options.scenario.name === "runtime-health-rollback" ? actualRuntimeVersion : null' + ); }); it("normalizes sidecar websocket urls for browser-issued commands", () => { @@ -548,6 +551,24 @@ describe("verify-desktop-installed-update", () => { expect(report.rollbackRuntimeVersion).toBe("0.5.0"); }); + it("accepts a journal plan that reconciles completely during crash recovery", async () => { + const deps = createDeps(); + const scenario: InstalledDesktopScenario = { + ...combinedScenario, + name: "restart-journal-recovery", + }; + + const report = await verifyInstalledDesktopScenario(scenario, deps); + + expect(report.journalRecovered).toBe(true); + expect(report.restartCount).toBe(1); + expect(deps.interruptAtPhase).toHaveBeenCalledWith("restart-journal"); + expect(deps.reconnectAfterRestart).toHaveBeenCalledOnce(); + expect(deps.armRestartAfterInstall).not.toHaveBeenCalled(); + expect(deps.waitForRestartAfterInstall).not.toHaveBeenCalled(); + expect(deps.invoke).not.toHaveBeenCalledWith("restartAndInstallUpdate"); + }); + it("updates shared Web before following it with the WSL Runtime", async () => { const deps = createDeps({ invoke: vi.fn(async (method) => { diff --git a/scripts/verify-desktop-installed-update.ts b/scripts/verify-desktop-installed-update.ts index a22a14c6..4ce14fe0 100644 --- a/scripts/verify-desktop-installed-update.ts +++ b/scripts/verify-desktop-installed-update.ts @@ -389,6 +389,7 @@ export async function verifyInstalledDesktopScenario( await deps.prepareActivity(); const confirmationCount = 1; await deps.invoke("prepareUpdateRestart"); + let finalState: ProductUpdateState | null = null; if (scenario.name === "restart-journal-recovery") { await deps.interruptAtPhase("restart-journal"); await deps.reconnectAfterRestart(); @@ -396,17 +397,23 @@ export async function verifyInstalledDesktopScenario( await deps.invoke("getUpdateState"), "getUpdateState after journal interruption" ); - if (recovered.status !== "ready") { - throw new Error(`Desktop did not recover a ready journal plan: ${recovered.status}`); + assertPlanComponents(recovered, scenario.expectedComponentIds); + if (recovered.status !== "ready" && recovered.status !== "succeeded") { + throw new Error( + `Desktop did not recover a ready or completed journal plan: ${recovered.status}` + ); } + if (recovered.status === "succeeded") finalState = recovered; + } + if (!finalState) { + await deps.armRestartAfterInstall(); + const restartResult = await deps.invoke("restartAndInstallUpdate"); + if (restartResult !== true) throw new Error("Desktop rejected restart-and-install handoff"); + await deps.waitForRestartAfterInstall(); + await deps.reconnectAfterRestart(); + finalState = asState(await deps.invoke("getUpdateState"), "getUpdateState"); } - await deps.armRestartAfterInstall(); - const restartResult = await deps.invoke("restartAndInstallUpdate"); - if (restartResult !== true) throw new Error("Desktop rejected restart-and-install handoff"); const restartCount = 1; - await deps.waitForRestartAfterInstall(); - await deps.reconnectAfterRestart(); - const finalState = asState(await deps.invoke("getUpdateState"), "getUpdateState"); const finalStateAccepted = scenario.name === "runtime-health-rollback" ? finalState.status === "failed" @@ -788,6 +795,11 @@ async function createDefaultDeps(options: InstalledDriverOptions): Promise<{ const observedWslRuntimeVersion = await readWslActiveRuntimeVersion(options.wslDistro); const external = await readEvidenceFile(options.evidencePath); const observedWslMarker = await wslMarkerExists(options.wslDistro, options.wslMarkerPath); + const actualRuntimeVersion = + observedRuntimeVersion ?? + external.actualRuntimeVersion ?? + observedFactoryRuntimeVersion ?? + state.productVersion; if ( isWslScenario(options.scenario.name) && typeof external.wslNpmMarkerExists !== "boolean" && @@ -808,19 +820,13 @@ async function createDefaultDeps(options: InstalledDriverOptions): Promise<{ external.actualShellVersion ?? state.diagnostics.shellVersion ?? "", - actualRuntimeVersion: - observedRuntimeVersion ?? - external.actualRuntimeVersion ?? - observedFactoryRuntimeVersion ?? - state.productVersion, + actualRuntimeVersion, wslRuntimeVersion: observedWslRuntimeVersion ?? external.wslRuntimeVersion ?? null, wslNpmMarkerExists: external.wslNpmMarkerExists ?? observedWslMarker ?? false, journalRecovered: external.journalRecovered ?? journalRecovered, rollbackRuntimeVersion: external.rollbackRuntimeVersion ?? - (options.scenario.name === "runtime-health-rollback" - ? await readActiveRuntimeVersion(options.userDataDir) - : null), + (options.scenario.name === "runtime-health-rollback" ? actualRuntimeVersion : null), externalSidecarReadOnly: external.externalSidecarReadOnly ?? false, logPaths: external.logPaths ?? state.diagnostics.logLocations, };