import { afterEach, describe, expect, it, vi } from "vitest"; import * as ssh from "./ssh.js"; import { adapterExecutionTargetUsesManagedHome, runAdapterExecutionTargetShellCommand, } from "./execution-target.js"; describe("runAdapterExecutionTargetShellCommand", () => { afterEach(() => { vi.restoreAllMocks(); }); it("quotes remote shell commands with the shared SSH quoting helper", async () => { const runSshCommandSpy = vi.spyOn(ssh, "runSshCommand").mockResolvedValue({ stdout: "", stderr: "", }); await runAdapterExecutionTargetShellCommand( "run-1", { kind: "remote", transport: "ssh", remoteCwd: "/srv/paperclip/workspace", spec: { host: "ssh.example.test", port: 22, username: "ssh-user", remoteCwd: "/srv/paperclip/workspace", remoteWorkspacePath: "/srv/paperclip/workspace", privateKey: null, knownHosts: null, strictHostKeyChecking: true, }, }, `printf '%s\\n' "$HOME" && echo "it's ok"`, { cwd: "/tmp/local", env: {}, }, ); expect(runSshCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ host: "ssh.example.test", username: "ssh-user", }), `sh -lc ${ssh.shellQuote(`printf '%s\\n' "$HOME" && echo "it's ok"`)}`, expect.any(Object), ); }); it("returns a timedOut result when the SSH shell command times out", async () => { vi.spyOn(ssh, "runSshCommand").mockRejectedValue(Object.assign(new Error("timed out"), { code: "ETIMEDOUT", stdout: "partial stdout", stderr: "partial stderr", signal: "SIGTERM", })); const onLog = vi.fn(async () => {}); const result = await runAdapterExecutionTargetShellCommand( "run-2", { kind: "remote", transport: "ssh", remoteCwd: "/srv/paperclip/workspace", spec: { host: "ssh.example.test", port: 22, username: "ssh-user", remoteCwd: "/srv/paperclip/workspace", remoteWorkspacePath: "/srv/paperclip/workspace", privateKey: null, knownHosts: null, strictHostKeyChecking: true, }, }, "sleep 10", { cwd: "/tmp/local", env: {}, onLog, }, ); expect(result).toMatchObject({ exitCode: null, signal: "SIGTERM", timedOut: true, stdout: "partial stdout", stderr: "partial stderr", }); expect(onLog).toHaveBeenCalledWith("stdout", "partial stdout"); expect(onLog).toHaveBeenCalledWith("stderr", "partial stderr"); }); it("returns the SSH process exit code for non-zero remote command failures", async () => { vi.spyOn(ssh, "runSshCommand").mockRejectedValue(Object.assign(new Error("non-zero exit"), { code: 17, stdout: "partial stdout", stderr: "partial stderr", signal: null, })); const onLog = vi.fn(async () => {}); const result = await runAdapterExecutionTargetShellCommand( "run-3", { kind: "remote", transport: "ssh", remoteCwd: "/srv/paperclip/workspace", spec: { host: "ssh.example.test", port: 22, username: "ssh-user", remoteCwd: "/srv/paperclip/workspace", remoteWorkspacePath: "/srv/paperclip/workspace", privateKey: null, knownHosts: null, strictHostKeyChecking: true, }, }, "false", { cwd: "/tmp/local", env: {}, onLog, }, ); expect(result).toMatchObject({ exitCode: 17, signal: null, timedOut: false, stdout: "partial stdout", stderr: "partial stderr", }); expect(onLog).toHaveBeenCalledWith("stdout", "partial stdout"); expect(onLog).toHaveBeenCalledWith("stderr", "partial stderr"); }); it("keeps managed homes disabled for both local and SSH targets", () => { expect(adapterExecutionTargetUsesManagedHome(null)).toBe(false); expect(adapterExecutionTargetUsesManagedHome({ kind: "remote", transport: "ssh", remoteCwd: "/srv/paperclip/workspace", spec: { host: "ssh.example.test", port: 22, username: "ssh-user", remoteCwd: "/srv/paperclip/workspace", remoteWorkspacePath: "/srv/paperclip/workspace", privateKey: null, knownHosts: null, strictHostKeyChecking: true, }, })).toBe(false); }); });