import { describe, expect, it } from "vite-plus/test"; import { createInitialDesktopUpdateState, reduceDesktopUpdateStateOnCheckFailure, reduceDesktopUpdateStateOnCheckStart, reduceDesktopUpdateStateOnDownloadComplete, reduceDesktopUpdateStateOnDownloadFailure, reduceDesktopUpdateStateOnDownloadProgress, reduceDesktopUpdateStateOnDownloadStart, reduceDesktopUpdateStateOnInstallFailure, reduceDesktopUpdateStateOnNoUpdate, reduceDesktopUpdateStateOnUpdateAvailable,} from "./updateMachine.ts"; const runtimeInfo = { hostArch: "x64", appArch: "x64", runningUnderArm64Translation: false,} as const; describe("updateMachine", () => { it("clears transient errors when a check starts", () => { const state = reduceDesktopUpdateStateOnCheckStart( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "error", message: "network", errorContext: "check", canRetry: true, }, "2026-03-04T00:00:00.000Z", ); expect(state.status).toBe("checking"); expect(state.message).toBeNull(); expect(state.errorContext).toBeNull(); expect(state.canRetry).toBe(false); }); it("records a check failure without exposing an action", () => { const state = reduceDesktopUpdateStateOnCheckFailure( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "checking", }, "network unavailable", "2026-03-04T00:00:00.000Z", ); expect(state.status).toBe("error"); expect(state.errorContext).toBe("check"); expect(state.canRetry).toBe(true); }); it("preserves an already-downloaded update while checking the feed", () => { const downloadedState = { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "downloaded" as const, availableVersion: "1.1.0", downloadedVersion: "1.1.0", releaseNotes: [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }], omittedReleaseCount: 2, downloadPercent: 100, }; const checking = reduceDesktopUpdateStateOnCheckStart( downloadedState, "2026-03-04T00:00:00.000Z", ); const failed = reduceDesktopUpdateStateOnCheckFailure( checking, "network unavailable", "2026-03-04T00:00:01.000Z", ); expect(checking.status).toBe("checking"); expect(checking.downloadedVersion).toBe("1.1.0"); expect(checking.releaseNotes).toEqual(downloadedState.releaseNotes); expect(checking.omittedReleaseCount).toBe(2); expect(failed.status).toBe("downloaded"); expect(failed.downloadedVersion).toBe("1.1.0"); expect(failed.releaseNotes).toEqual(downloadedState.releaseNotes); expect(failed.omittedReleaseCount).toBe(2); expect(failed.message).toBeNull(); }); it("keeps the installer when the feed still offers its version", () => { const releaseNotes = [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }]; const state = reduceDesktopUpdateStateOnUpdateAvailable( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "downloaded", availableVersion: "1.1.0", downloadedVersion: "1.1.0", releaseNotes, omittedReleaseCount: 2, downloadPercent: 100, }, "1.1.0", "2026-03-04T00:00:00.000Z", ); expect(state.status).toBe("downloaded"); expect(state.downloadedVersion).toBe("1.1.0"); expect(state.releaseNotes).toEqual(releaseNotes); expect(state.omittedReleaseCount).toBe(2); expect(state.downloadPercent).toBe(100); }); it("preserves available version on download failure for retry", () => { const state = reduceDesktopUpdateStateOnDownloadFailure( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "downloading", availableVersion: "1.1.0", downloadPercent: 43, }, "checksum mismatch", ); expect(state.status).toBe("available"); expect(state.availableVersion).toBe("1.1.0"); expect(state.errorContext).toBe("download"); expect(state.canRetry).toBe(true); }); it("transitions to downloaded and then preserves install retry state", () => { const downloaded = reduceDesktopUpdateStateOnDownloadComplete( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "downloading", availableVersion: "1.1.0", }, "1.1.0", ); const failedInstall = reduceDesktopUpdateStateOnInstallFailure( downloaded, "backend shutdown timed out", ); expect(downloaded.status).toBe("downloaded"); expect(downloaded.downloadedVersion).toBe("1.1.0"); expect(failedInstall.status).toBe("downloaded"); expect(failedInstall.errorContext).toBe("install"); expect(failedInstall.canRetry).toBe(true); }); it("preserves a downloaded update when no update is available", () => { const releaseNotes = [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }]; const state = reduceDesktopUpdateStateOnNoUpdate( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "error", availableVersion: "1.1.0", downloadedVersion: "1.1.0", releaseNotes, omittedReleaseCount: 2, message: "old failure", errorContext: "download", canRetry: true, }, "2026-03-04T00:00:00.000Z", ); expect(state.status).toBe("downloaded"); expect(state.availableVersion).toBe("1.1.0"); expect(state.downloadedVersion).toBe("1.1.0"); expect(state.releaseNotes).toBe(releaseNotes); expect(state.omittedReleaseCount).toBe(2); expect(state.downloadPercent).toBe(100); expect(state.message).toBeNull(); expect(state.errorContext).toBeNull(); expect(state.canRetry).toBe(true); }); it("clears stale available state when no update is available", () => { const state = reduceDesktopUpdateStateOnNoUpdate( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "error", availableVersion: "1.1.0", releaseNotes: [{ version: "1.1.0", items: ["fix: stale update"], totalItems: 1 }], omittedReleaseCount: 2, message: "old failure", errorContext: "download", canRetry: true, }, "2026-03-04T00:00:00.000Z", ); expect(state.status).toBe("up-to-date"); expect(state.availableVersion).toBeNull(); expect(state.downloadedVersion).toBeNull(); expect(state.releaseNotes).toEqual([]); expect(state.omittedReleaseCount).toBe(0); expect(state.message).toBeNull(); expect(state.errorContext).toBeNull(); }); it("tracks available, download start, and progress cleanly", () => { const releaseNotes = [ { version: "1.1.0", items: ["feat: add update release notes"], totalItems: 1, }, ]; const available = reduceDesktopUpdateStateOnUpdateAvailable( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"), enabled: true, status: "checking", }, "1.1.0", "2026-03-04T00:00:00.000Z", releaseNotes, 2, ); const downloading = reduceDesktopUpdateStateOnDownloadStart(available); const progress = reduceDesktopUpdateStateOnDownloadProgress(downloading, 55.5); expect(available.status).toBe("available"); expect(available.channel).toBe("latest"); expect(available.releaseNotes).toBe(releaseNotes); expect(available.omittedReleaseCount).toBe(2); expect(downloading.releaseNotes).toBe(releaseNotes); expect(downloading.status).toBe("downloading"); expect(downloading.downloadPercent).toBe(0); expect(progress.downloadPercent).toBe(55.5); expect(progress.errorContext).toBeNull(); }); it("clears release notes when checking again", () => { const state = reduceDesktopUpdateStateOnCheckStart( { ...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "nightly"), enabled: true, status: "available", availableVersion: "1.1.0-nightly.1", releaseNotes: [{ version: "1.1.0-nightly.1", items: ["feat: old note"], totalItems: 1 }], omittedReleaseCount: 2, }, "2026-03-04T00:00:00.000Z", ); expect(state.releaseNotes).toEqual([]); expect(state.omittedReleaseCount).toBe(0); });});
import { describe, expect, it } from "vite-plus/test";
import {
createInitialDesktopUpdateState,
reduceDesktopUpdateStateOnCheckFailure,
reduceDesktopUpdateStateOnCheckStart,
reduceDesktopUpdateStateOnDownloadComplete,
reduceDesktopUpdateStateOnDownloadFailure,
reduceDesktopUpdateStateOnDownloadProgress,
reduceDesktopUpdateStateOnDownloadStart,
reduceDesktopUpdateStateOnInstallFailure,
reduceDesktopUpdateStateOnNoUpdate,
reduceDesktopUpdateStateOnUpdateAvailable,
} from "./updateMachine.ts";
const runtimeInfo = {
hostArch: "x64",
appArch: "x64",
runningUnderArm64Translation: false,
} as const;
describe("updateMachine", () => {
it("clears transient errors when a check starts", () => {
const state = reduceDesktopUpdateStateOnCheckStart(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "error",
message: "network",
errorContext: "check",
canRetry: true,
},
"2026-03-04T00:00:00.000Z",
);
expect(state.status).toBe("checking");
expect(state.message).toBeNull();
expect(state.errorContext).toBeNull();
expect(state.canRetry).toBe(false);
});
it("records a check failure without exposing an action", () => {
const state = reduceDesktopUpdateStateOnCheckFailure(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "checking",
},
"network unavailable",
"2026-03-04T00:00:00.000Z",
);
expect(state.status).toBe("error");
expect(state.errorContext).toBe("check");
expect(state.canRetry).toBe(true);
});
it("preserves an already-downloaded update while checking the feed", () => {
const downloadedState = {
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "downloaded" as const,
availableVersion: "1.1.0",
downloadedVersion: "1.1.0",
releaseNotes: [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }],
omittedReleaseCount: 2,
downloadPercent: 100,
};
const checking = reduceDesktopUpdateStateOnCheckStart(
downloadedState,
"2026-03-04T00:00:00.000Z",
);
const failed = reduceDesktopUpdateStateOnCheckFailure(
checking,
"network unavailable",
"2026-03-04T00:00:01.000Z",
);
expect(checking.status).toBe("checking");
expect(checking.downloadedVersion).toBe("1.1.0");
expect(checking.releaseNotes).toEqual(downloadedState.releaseNotes);
expect(checking.omittedReleaseCount).toBe(2);
expect(failed.status).toBe("downloaded");
expect(failed.downloadedVersion).toBe("1.1.0");
expect(failed.releaseNotes).toEqual(downloadedState.releaseNotes);
expect(failed.omittedReleaseCount).toBe(2);
expect(failed.message).toBeNull();
});
it("keeps the installer when the feed still offers its version", () => {
const releaseNotes = [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }];
const state = reduceDesktopUpdateStateOnUpdateAvailable(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "downloaded",
availableVersion: "1.1.0",
downloadedVersion: "1.1.0",
releaseNotes,
omittedReleaseCount: 2,
downloadPercent: 100,
},
"1.1.0",
"2026-03-04T00:00:00.000Z",
);
expect(state.status).toBe("downloaded");
expect(state.downloadedVersion).toBe("1.1.0");
expect(state.releaseNotes).toEqual(releaseNotes);
expect(state.omittedReleaseCount).toBe(2);
expect(state.downloadPercent).toBe(100);
});
it("preserves available version on download failure for retry", () => {
const state = reduceDesktopUpdateStateOnDownloadFailure(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "downloading",
availableVersion: "1.1.0",
downloadPercent: 43,
},
"checksum mismatch",
);
expect(state.status).toBe("available");
expect(state.availableVersion).toBe("1.1.0");
expect(state.errorContext).toBe("download");
expect(state.canRetry).toBe(true);
});
it("transitions to downloaded and then preserves install retry state", () => {
const downloaded = reduceDesktopUpdateStateOnDownloadComplete(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "downloading",
availableVersion: "1.1.0",
},
"1.1.0",
);
const failedInstall = reduceDesktopUpdateStateOnInstallFailure(
downloaded,
"backend shutdown timed out",
);
expect(downloaded.status).toBe("downloaded");
expect(downloaded.downloadedVersion).toBe("1.1.0");
expect(failedInstall.status).toBe("downloaded");
expect(failedInstall.errorContext).toBe("install");
expect(failedInstall.canRetry).toBe(true);
});
it("preserves a downloaded update when no update is available", () => {
const releaseNotes = [{ version: "1.1.0", items: ["fix: queued update"], totalItems: 1 }];
const state = reduceDesktopUpdateStateOnNoUpdate(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "error",
availableVersion: "1.1.0",
downloadedVersion: "1.1.0",
releaseNotes,
omittedReleaseCount: 2,
message: "old failure",
errorContext: "download",
canRetry: true,
},
"2026-03-04T00:00:00.000Z",
);
expect(state.status).toBe("downloaded");
expect(state.availableVersion).toBe("1.1.0");
expect(state.downloadedVersion).toBe("1.1.0");
expect(state.releaseNotes).toBe(releaseNotes);
expect(state.omittedReleaseCount).toBe(2);
expect(state.downloadPercent).toBe(100);
expect(state.message).toBeNull();
expect(state.errorContext).toBeNull();
expect(state.canRetry).toBe(true);
});
it("clears stale available state when no update is available", () => {
const state = reduceDesktopUpdateStateOnNoUpdate(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "error",
availableVersion: "1.1.0",
releaseNotes: [{ version: "1.1.0", items: ["fix: stale update"], totalItems: 1 }],
omittedReleaseCount: 2,
message: "old failure",
errorContext: "download",
canRetry: true,
},
"2026-03-04T00:00:00.000Z",
);
expect(state.status).toBe("up-to-date");
expect(state.availableVersion).toBeNull();
expect(state.downloadedVersion).toBeNull();
expect(state.releaseNotes).toEqual([]);
expect(state.omittedReleaseCount).toBe(0);
expect(state.message).toBeNull();
expect(state.errorContext).toBeNull();
});
it("tracks available, download start, and progress cleanly", () => {
const releaseNotes = [
{
version: "1.1.0",
items: ["feat: add update release notes"],
totalItems: 1,
},
];
const available = reduceDesktopUpdateStateOnUpdateAvailable(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "latest"),
enabled: true,
status: "checking",
},
"1.1.0",
"2026-03-04T00:00:00.000Z",
releaseNotes,
2,
);
const downloading = reduceDesktopUpdateStateOnDownloadStart(available);
const progress = reduceDesktopUpdateStateOnDownloadProgress(downloading, 55.5);
expect(available.status).toBe("available");
expect(available.channel).toBe("latest");
expect(available.releaseNotes).toBe(releaseNotes);
expect(available.omittedReleaseCount).toBe(2);
expect(downloading.releaseNotes).toBe(releaseNotes);
expect(downloading.status).toBe("downloading");
expect(downloading.downloadPercent).toBe(0);
expect(progress.downloadPercent).toBe(55.5);
expect(progress.errorContext).toBeNull();
});
it("clears release notes when checking again", () => {
const state = reduceDesktopUpdateStateOnCheckStart(
{
...createInitialDesktopUpdateState("1.0.0", runtimeInfo, "nightly"),
enabled: true,
status: "available",
availableVersion: "1.1.0-nightly.1",
releaseNotes: [{ version: "1.1.0-nightly.1", items: ["feat: old note"], totalItems: 1 }],
omittedReleaseCount: 2,
},
"2026-03-04T00:00:00.000Z",
);
expect(state.releaseNotes).toEqual([]);
expect(state.omittedReleaseCount).toBe(0);
});
});