diff --git a/src/cli/update-cli/restart-helper.test.ts b/src/cli/update-cli/restart-helper.test.ts index ddeb8df34e..802ced311c 100644 --- a/src/cli/update-cli/restart-helper.test.ts +++ b/src/cli/update-cli/restart-helper.test.ts @@ -11,6 +11,17 @@ describe("restart-helper", () => { const originalPlatform = process.platform; const originalGetUid = process.getuid; + async function prepareAndReadScript(env: Record) { + const scriptPath = await prepareRestartScript(env); + expect(scriptPath).toBeTruthy(); + const content = await fs.readFile(scriptPath!, "utf-8"); + return { scriptPath: scriptPath!, content }; + } + + async function cleanupScript(scriptPath: string) { + await fs.unlink(scriptPath); + } + beforeEach(() => { vi.resetAllMocks(); }); @@ -23,165 +34,108 @@ describe("restart-helper", () => { describe("prepareRestartScript", () => { it("creates a systemd restart script on Linux", async () => { Object.defineProperty(process, "platform", { value: "linux" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", }); - - expect(scriptPath).toBeTruthy(); - expect(scriptPath!.endsWith(".sh")).toBe(true); - - const content = await fs.readFile(scriptPath!, "utf-8"); + expect(scriptPath.endsWith(".sh")).toBe(true); expect(content).toContain("#!/bin/sh"); expect(content).toContain("systemctl --user restart 'openclaw-gateway.service'"); // Script should self-cleanup expect(content).toContain('rm -f "$0"'); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses OPENCLAW_SYSTEMD_UNIT override for systemd scripts", async () => { Object.defineProperty(process, "platform", { value: "linux" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", OPENCLAW_SYSTEMD_UNIT: "custom-gateway", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain("systemctl --user restart 'custom-gateway.service'"); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("creates a launchd restart script on macOS", async () => { Object.defineProperty(process, "platform", { value: "darwin" }); process.getuid = () => 501; - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", }); - - expect(scriptPath).toBeTruthy(); - expect(scriptPath!.endsWith(".sh")).toBe(true); - - const content = await fs.readFile(scriptPath!, "utf-8"); + expect(scriptPath.endsWith(".sh")).toBe(true); expect(content).toContain("#!/bin/sh"); expect(content).toContain("launchctl kickstart -k 'gui/501/ai.openclaw.gateway'"); expect(content).toContain('rm -f "$0"'); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses OPENCLAW_LAUNCHD_LABEL override on macOS", async () => { Object.defineProperty(process, "platform", { value: "darwin" }); process.getuid = () => 501; - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", OPENCLAW_LAUNCHD_LABEL: "com.custom.openclaw", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain("launchctl kickstart -k 'gui/501/com.custom.openclaw'"); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("creates a schtasks restart script on Windows", async () => { Object.defineProperty(process, "platform", { value: "win32" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", }); - - expect(scriptPath).toBeTruthy(); - expect(scriptPath!.endsWith(".bat")).toBe(true); - - const content = await fs.readFile(scriptPath!, "utf-8"); + expect(scriptPath.endsWith(".bat")).toBe(true); expect(content).toContain("@echo off"); expect(content).toContain('schtasks /End /TN "OpenClaw Gateway"'); expect(content).toContain('schtasks /Run /TN "OpenClaw Gateway"'); // Batch self-cleanup expect(content).toContain('del "%~f0"'); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses OPENCLAW_WINDOWS_TASK_NAME override on Windows", async () => { Object.defineProperty(process, "platform", { value: "win32" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "default", OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Gateway (custom)", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain('schtasks /End /TN "OpenClaw Gateway (custom)"'); expect(content).toContain('schtasks /Run /TN "OpenClaw Gateway (custom)"'); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses custom profile in service names", async () => { Object.defineProperty(process, "platform", { value: "linux" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "production", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain("openclaw-gateway-production.service"); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses custom profile in macOS launchd label", async () => { Object.defineProperty(process, "platform", { value: "darwin" }); process.getuid = () => 502; - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "staging", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain("gui/502/ai.openclaw.staging"); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("uses custom profile in Windows task name", async () => { Object.defineProperty(process, "platform", { value: "win32" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "production", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); expect(content).toContain('schtasks /End /TN "OpenClaw Gateway (production)"'); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("returns null for unsupported platforms", async () => { @@ -206,19 +160,13 @@ describe("restart-helper", () => { it("escapes single quotes in profile names for shell scripts", async () => { Object.defineProperty(process, "platform", { value: "linux" }); - const scriptPath = await prepareRestartScript({ + const { scriptPath, content } = await prepareAndReadScript({ OPENCLAW_PROFILE: "it's-a-test", }); - - expect(scriptPath).toBeTruthy(); - const content = await fs.readFile(scriptPath!, "utf-8"); // Single quotes should be escaped with '\'' pattern expect(content).not.toContain("it's"); expect(content).toContain("it'\\''s"); - - if (scriptPath) { - await fs.unlink(scriptPath); - } + await cleanupScript(scriptPath); }); it("rejects unsafe batch profile names on Windows", async () => {