import { expect, test } from "bun:test"; const updaterUrl = new URL("../../lib/version-update.ts", import.meta.url).href; const entryUrl = new URL("../../index.ts", import.meta.url).href; const mainUrl = new URL("../../command/main.ts", import.meta.url).href; const errorUrl = new URL("../../lib/error.ts", import.meta.url).href; test.each([ ["success", "return true", "+ Updated to 2.7.0"], ["command error", "return true", "+ Updated to 2.7.0"], ["failure", "throw new Error('offline')", "+ New version available: 2.7.0"], [ "timeout", "return await installVersion(version, () => ({ exited: new Promise(() => {}), kill: () => {} }), 1)", "+ New version available: 2.7.0", ], ])( "CLI waits for a %s update and preserves its exit status", async (_name, outcome, message) => { const script = ` import { mock } from "bun:test"; const { createVersionObserver, installVersion } = await import(${JSON.stringify(updaterUrl)}); const observe = createVersionObserver({ currentVersion: "2.6.1", runner: async (version) => { if (${JSON.stringify(_name)} !== "timeout") await new Promise(resolve => setTimeout(resolve, 80)); ${outcome}; }}); mock.module("citty", () => ({ createMain: () => async () => { observe("2.7.0"); observe("2.8.0"); if (${JSON.stringify(_name)} === "command error") throw new Error("command failed"); process.exitCode = 7; }})); mock.module(${JSON.stringify(mainUrl)}, () => ({ main: {}, initializeCompletion: async () => {} })); mock.module(${JSON.stringify(errorUrl)}, () => ({ formatUnknownError: String, wrapCommandErrors: () => {} })); await import(${JSON.stringify(entryUrl)}); `; const started = Date.now(); const child = Bun.spawn([process.execPath, "-e", script, "login"], { stdin: "ignore", stdout: "pipe", stderr: "pipe", }); const timer = setTimeout(() => child.kill(), 5_000); try { const [code, stdout, stderr] = await Promise.all([ child.exited, new Response(child.stdout).text(), new Response(child.stderr).text(), ]); expect(code).toBe(_name === "command error" ? 1 : 7); expect(Date.now() - started).toBeGreaterThanOrEqual( _name === "timeout" ? 900 : 70, ); expect(stdout).toBe(""); expect(stderr).toBe( `${_name === "command error" ? "Error: command failed\n" : ""}\x1b[90m${message}\x1b[0m\n`, ); } finally { clearTimeout(timer); } }, );