|
1 | 1 | // Daemon program argument tests cover CLI argument construction for services. |
2 | 2 | import path from "node:path"; |
3 | 3 | import { afterEach, describe, expect, it, vi } from "vitest"; |
| 4 | +import { resetWindowsInstallRootsForTests } from "../infra/windows-install-roots.js"; |
| 5 | +import { withMockedWindowsPlatform } from "../test-utils/vitest-spies.js"; |
4 | 6 | |
5 | 7 | const childProcessMocks = vi.hoisted(() => ({ |
6 | 8 | execFileSync: vi.fn(), |
@@ -43,6 +45,8 @@ const originalArgv = [...process.argv];
|
43 | 45 | afterEach(() => { |
44 | 46 | process.argv = [...originalArgv]; |
45 | 47 | vi.resetAllMocks(); |
| 48 | +vi.unstubAllEnvs(); |
| 49 | +resetWindowsInstallRootsForTests(); |
46 | 50 | }); |
47 | 51 | |
48 | 52 | describe("resolveGatewayProgramArguments", () => { |
@@ -179,6 +183,39 @@ describe("resolveGatewayProgramArguments", () => {
|
179 | 183 | expect(result.workingDirectory).toBe(path.resolve("/repo")); |
180 | 184 | }); |
181 | 185 | |
| 186 | +it("uses trusted Windows where.exe when resolving dev runtime binaries", async () => { |
| 187 | +const repoIndexPath = path.resolve("/repo/src/index.ts"); |
| 188 | +const repoEntryPath = path.resolve("/repo/src/entry.ts"); |
| 189 | +process.argv = [String.raw`D:\nodejs\node.exe`, repoIndexPath]; |
| 190 | +vi.stubEnv("SystemRoot", String.raw`D:\Windows`); |
| 191 | +resetWindowsInstallRootsForTests({ queryRegistryValue: () => null }); |
| 192 | +fsMocks.realpath.mockResolvedValue(repoIndexPath); |
| 193 | +fsMocks.access.mockResolvedValue(undefined); |
| 194 | +childProcessMocks.execFileSync.mockReturnValue(String.raw`D:\Tools\bun.exe` + "\r\n"); |
| 195 | + |
| 196 | +let result: Awaited<ReturnType<typeof resolveGatewayProgramArguments>> | undefined; |
| 197 | +await withMockedWindowsPlatform(async () => { |
| 198 | +result = await resolveGatewayProgramArguments({ |
| 199 | +dev: true, |
| 200 | +port: 18789, |
| 201 | +runtime: "bun", |
| 202 | +}); |
| 203 | +}); |
| 204 | + |
| 205 | +expect(childProcessMocks.execFileSync).toHaveBeenCalledWith( |
| 206 | +path.win32.join(String.raw`D:\Windows`, "System32", "where.exe"), |
| 207 | +["bun"], |
| 208 | +{ encoding: "utf8" }, |
| 209 | +); |
| 210 | +expect(result?.programArguments).toEqual([ |
| 211 | +String.raw`D:\Tools\bun.exe`, |
| 212 | +repoEntryPath, |
| 213 | +"gateway", |
| 214 | +"--port", |
| 215 | +"18789", |
| 216 | +]); |
| 217 | +}); |
| 218 | + |
182 | 219 | it("uses an executable wrapper when provided", async () => { |
183 | 220 | const wrapperPath = path.resolve("/usr/local/bin/openclaw-doppler"); |
184 | 221 | fsMocks.stat.mockResolvedValue({ isFile: () => true } as never); |
|