112 lines
3.5 KiB
TypeScript
112 lines
3.5 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { createUpdaterController, type UpdaterBackend, type UpdaterReadyRecord } from "./updater-controller"
|
|
|
|
function setup(input?: { currentVersion?: string; ready?: UpdaterReadyRecord }) {
|
|
const calls: string[] = []
|
|
const backend: UpdaterBackend = {
|
|
async checkForUpdates() {
|
|
calls.push("check")
|
|
return { isUpdateAvailable: true, updateInfo: { version: "2.0.0" } }
|
|
},
|
|
async downloadUpdate() {
|
|
calls.push("download")
|
|
},
|
|
quitAndInstall() {
|
|
calls.push("install")
|
|
},
|
|
}
|
|
let ready = input?.ready
|
|
const controller = createUpdaterController({
|
|
enabled: true,
|
|
currentVersion: input?.currentVersion ?? "1.0.0",
|
|
backend,
|
|
persistence: {
|
|
get: () => ready,
|
|
set: (value) => {
|
|
ready = value
|
|
},
|
|
clear: () => {
|
|
ready = undefined
|
|
},
|
|
},
|
|
stop: async () => {
|
|
calls.push("stop")
|
|
},
|
|
})
|
|
return { controller, calls, getReady: () => ready }
|
|
}
|
|
|
|
describe("updater controller", () => {
|
|
test("checks, downloads, persists, and publishes one authoritative ready state", async () => {
|
|
const app = setup()
|
|
const states: ReturnType<typeof app.controller.getState>[] = []
|
|
app.controller.subscribe((state) => states.push(state))
|
|
|
|
await app.controller.start()
|
|
|
|
expect(app.calls).toEqual(["check", "download"])
|
|
expect(app.getReady()).toEqual({ version: "2.0.0" })
|
|
expect(states.map((state) => state.status)).toEqual(["idle", "checking", "downloading", "ready"])
|
|
expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" })
|
|
})
|
|
|
|
test("revalidates a persisted target through the updater cache on launch", async () => {
|
|
const app = setup({ ready: { version: "2.0.0" } })
|
|
|
|
await app.controller.start()
|
|
|
|
expect(app.calls).toEqual(["check", "download"])
|
|
expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" })
|
|
})
|
|
|
|
test("clears a target already installed before checking", async () => {
|
|
const app = setup({ currentVersion: "2.0.0", ready: { version: "2.0.0" } })
|
|
|
|
await app.controller.start()
|
|
|
|
expect(app.getReady()).toBeUndefined()
|
|
expect(app.calls).toEqual(["check"])
|
|
})
|
|
|
|
test("coalesces concurrent checks", async () => {
|
|
const app = setup()
|
|
|
|
await Promise.all([app.controller.check(), app.controller.check(), app.controller.check()])
|
|
|
|
expect(app.calls).toEqual(["check", "download"])
|
|
})
|
|
|
|
test("returns to ready when quitAndInstall returns without exiting", async () => {
|
|
const app = setup()
|
|
await app.controller.start()
|
|
|
|
await app.controller.install()
|
|
|
|
expect(app.calls).toEqual(["check", "download", "stop", "install"])
|
|
expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" })
|
|
})
|
|
|
|
test("returns to ready when installation cannot start", async () => {
|
|
const app = setup()
|
|
await app.controller.start()
|
|
|
|
const failed = createUpdaterController({
|
|
enabled: true,
|
|
currentVersion: "1.0.0",
|
|
backend: {
|
|
checkForUpdates: async () => ({ isUpdateAvailable: true, updateInfo: { version: "2.0.0" } }),
|
|
downloadUpdate: async () => {},
|
|
quitAndInstall() {},
|
|
},
|
|
persistence: { get: () => undefined, set() {}, clear() {} },
|
|
stop: async () => {
|
|
throw new Error("stop failed")
|
|
},
|
|
})
|
|
await failed.start()
|
|
|
|
await expect(failed.install()).rejects.toThrow("stop failed")
|
|
expect(failed.getState()).toEqual({ status: "ready", version: "2.0.0" })
|
|
})
|
|
})
|