diff --git a/src/daemon/schtasks.test.ts b/src/daemon/schtasks.test.ts index a0d24d89da..125b1f3a0a 100644 --- a/src/daemon/schtasks.test.ts +++ b/src/daemon/schtasks.test.ts @@ -65,147 +65,150 @@ describe("resolveTaskScriptPath", () => { }); describe("readScheduledTaskCommand", () => { - it("parses script with quoted arguments containing spaces", async () => { + async function withScheduledTaskScript( + options: { + scriptLines?: string[]; + env?: + | Record + | ((tmpDir: string) => Record); + }, + run: (env: Record) => Promise, + ) { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); try { - const scriptPath = path.join(tmpDir, ".openclaw", "gateway.cmd"); - await fs.mkdir(path.dirname(scriptPath), { recursive: true }); - // Use forward slashes which work in Windows cmd and avoid escape parsing issues - await fs.writeFile( - scriptPath, - ["@echo off", '"C:/Program Files/Node/node.exe" gateway.js'].join("\r\n"), - "utf8", - ); - - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; - const result = await readScheduledTaskCommand(env); - expect(result).toEqual({ - programArguments: ["C:/Program Files/Node/node.exe", "gateway.js"], - }); + const extraEnv = typeof options.env === "function" ? options.env(tmpDir) : options.env; + const env = { + USERPROFILE: tmpDir, + OPENCLAW_PROFILE: "default", + ...extraEnv, + }; + if (options.scriptLines) { + const scriptPath = resolveTaskScriptPath(env); + await fs.mkdir(path.dirname(scriptPath), { recursive: true }); + await fs.writeFile(scriptPath, options.scriptLines.join("\r\n"), "utf8"); + } + await run(env); } finally { await fs.rm(tmpDir, { recursive: true, force: true }); } + } + + it("parses script with quoted arguments containing spaces", async () => { + await withScheduledTaskScript( + { + // Use forward slashes which work in Windows cmd and avoid escape parsing issues. + scriptLines: ["@echo off", '"C:/Program Files/Node/node.exe" gateway.js'], + }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toEqual({ + programArguments: ["C:/Program Files/Node/node.exe", "gateway.js"], + }); + }, + ); }); it("returns null when script does not exist", async () => { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); - try { - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; + await withScheduledTaskScript({}, async (env) => { const result = await readScheduledTaskCommand(env); expect(result).toBeNull(); - } finally { - await fs.rm(tmpDir, { recursive: true, force: true }); - } + }); }); it("returns null when script has no command", async () => { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); - try { - const scriptPath = path.join(tmpDir, ".openclaw", "gateway.cmd"); - await fs.mkdir(path.dirname(scriptPath), { recursive: true }); - await fs.writeFile( - scriptPath, - ["@echo off", "rem This is just a comment"].join("\r\n"), - "utf8", - ); - - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; - const result = await readScheduledTaskCommand(env); - expect(result).toBeNull(); - } finally { - await fs.rm(tmpDir, { recursive: true, force: true }); - } + await withScheduledTaskScript( + { scriptLines: ["@echo off", "rem This is just a comment"] }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toBeNull(); + }, + ); }); it("parses full script with all components", async () => { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); - try { - const scriptPath = path.join(tmpDir, ".openclaw", "gateway.cmd"); - await fs.mkdir(path.dirname(scriptPath), { recursive: true }); - await fs.writeFile( - scriptPath, - [ + await withScheduledTaskScript( + { + scriptLines: [ "@echo off", "rem OpenClaw Gateway", "cd /d C:\\Projects\\openclaw", "set NODE_ENV=production", "set OPENCLAW_PORT=18789", "node gateway.js --verbose", - ].join("\r\n"), - "utf8", - ); - - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; - const result = await readScheduledTaskCommand(env); - expect(result).toEqual({ - programArguments: ["node", "gateway.js", "--verbose"], - workingDirectory: "C:\\Projects\\openclaw", - environment: { - NODE_ENV: "production", - OPENCLAW_PORT: "18789", - }, - }); - } finally { - await fs.rm(tmpDir, { recursive: true, force: true }); - } + ], + }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toEqual({ + programArguments: ["node", "gateway.js", "--verbose"], + workingDirectory: "C:\\Projects\\openclaw", + environment: { + NODE_ENV: "production", + OPENCLAW_PORT: "18789", + }, + }); + }, + ); }); + it("parses command with Windows backslash paths", async () => { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); - try { - const scriptPath = path.join(tmpDir, ".openclaw", "gateway.cmd"); - await fs.mkdir(path.dirname(scriptPath), { recursive: true }); - await fs.writeFile( - scriptPath, - [ + await withScheduledTaskScript( + { + scriptLines: [ "@echo off", '"C:\\Program Files\\nodejs\\node.exe" C:\\Users\\test\\AppData\\Roaming\\npm\\node_modules\\openclaw\\dist\\index.js gateway --port 18789', - ].join("\r\n"), - "utf8", - ); - - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; - const result = await readScheduledTaskCommand(env); - expect(result).toEqual({ - programArguments: [ - "C:\\Program Files\\nodejs\\node.exe", - "C:\\Users\\test\\AppData\\Roaming\\npm\\node_modules\\openclaw\\dist\\index.js", - "gateway", - "--port", - "18789", ], - }); - } finally { - await fs.rm(tmpDir, { recursive: true, force: true }); - } + }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toEqual({ + programArguments: [ + "C:\\Program Files\\nodejs\\node.exe", + "C:\\Users\\test\\AppData\\Roaming\\npm\\node_modules\\openclaw\\dist\\index.js", + "gateway", + "--port", + "18789", + ], + }); + }, + ); }); it("preserves UNC paths in command arguments", async () => { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-schtasks-test-")); - try { - const scriptPath = path.join(tmpDir, ".openclaw", "gateway.cmd"); - await fs.mkdir(path.dirname(scriptPath), { recursive: true }); - await fs.writeFile( - scriptPath, - [ + await withScheduledTaskScript( + { + scriptLines: [ "@echo off", '"\\\\fileserver\\OpenClaw Share\\node.exe" "\\\\fileserver\\OpenClaw Share\\dist\\index.js" gateway --port 18789', - ].join("\r\n"), - "utf8", - ); - - const env = { USERPROFILE: tmpDir, OPENCLAW_PROFILE: "default" }; - const result = await readScheduledTaskCommand(env); - expect(result).toEqual({ - programArguments: [ - "\\\\fileserver\\OpenClaw Share\\node.exe", - "\\\\fileserver\\OpenClaw Share\\dist\\index.js", - "gateway", - "--port", - "18789", ], - }); - } finally { - await fs.rm(tmpDir, { recursive: true, force: true }); - } + }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toEqual({ + programArguments: [ + "\\\\fileserver\\OpenClaw Share\\node.exe", + "\\\\fileserver\\OpenClaw Share\\dist\\index.js", + "gateway", + "--port", + "18789", + ], + }); + }, + ); + }); + + it("reads script from OPENCLAW_STATE_DIR override", async () => { + await withScheduledTaskScript( + { + env: (tmpDir) => ({ OPENCLAW_STATE_DIR: path.join(tmpDir, "custom-state") }), + scriptLines: ["@echo off", "node gateway.js --from-state-dir"], + }, + async (env) => { + const result = await readScheduledTaskCommand(env); + expect(result).toEqual({ + programArguments: ["node", "gateway.js", "--from-state-dir"], + }); + }, + ); }); });