| 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); |
| }); |
| }); |
|
|