| 1 | import { test } from "node:test"; |
| 2 | import assert from "node:assert/strict"; |
| 3 | import fs from "node:fs"; |
| 4 | import path from "node:path"; |
| 5 | import url from "node:url"; |
| 6 | import { sshArgv, sshOptions, validateSshTarget } from "../src/ssh-args.mjs"; |
| 7 | |
| 8 | const here = path.dirname(url.fileURLToPath(import.meta.url)); |
| 9 | const vectors = JSON.parse(fs.readFileSync(path.join(here, "fixtures", "ssh-destinations.json"), "utf8")); |
| 10 | |
| 11 | test("transport ssh argv pins known hosts and ends options", () => { |
| 12 | const argv = sshArgv({ host: "builder.example.test", user: "fleet", port: 2222, knownHosts: "/etc/cu_known_hosts" }); |
| 13 | for (const option of ["StrictHostKeyChecking=yes", "UpdateHostKeys=no", "UserKnownHostsFile=/etc/cu_known_hosts", "GlobalKnownHostsFile=/dev/null", "BatchMode=yes"]) { |
| 14 | const at = argv.indexOf(option); |
| 15 | assert.ok(at > 0, `missing ${option}: ${argv.join(" ")}`); |
| 16 | assert.equal(argv[at - 1], "-o"); |
| 17 | } |
| 18 | assert.ok(!argv.some((arg) => arg.includes("accept-new"))); |
| 19 | const end = argv.indexOf("--"); |
| 20 | assert.equal(argv[end + 1], "fleet@builder.example.test"); |
| 21 | assert.equal(end + 2, argv.length, "the destination is last; the remote command follows it"); |
| 22 | assert.deepEqual(sshOptions({ host: "h", port: 2200 }, { portFlag: "-P" }).slice(-2), ["-P", "2200"]); |
| 23 | }); |
| 24 | |
| 25 | test("ssh destinations follow the shared vectors", () => { |
| 26 | for (const entry of vectors.valid) assert.doesNotThrow(() => validateSshTarget(entry), JSON.stringify(entry)); |
| 27 | for (const entry of vectors.invalid) assert.throws(() => validateSshTarget(entry), JSON.stringify(entry)); |
| 28 | }); |
| 29 |