ssh-client-wrapper 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/coverage_report.yml +1 -1
- package/.github/workflows/release.yml +29 -0
- package/.github/workflows/run_test.yml +5 -7
- package/.husky/_pre-commit +1 -0
- package/.husky/pre-commit +0 -3
- package/.releaserc.json +16 -0
- package/CHANGELOG.md +13 -0
- package/README.md +99 -0
- package/eslint.config.mjs +181 -0
- package/lib/fork.js +31 -23
- package/lib/index.js +81 -32
- package/lib/rsyncExec.js +27 -31
- package/lib/sshExec.js +76 -74
- package/lib/util.js +41 -51
- package/package.json +31 -30
- package/test/getSshOption.js +12 -12
- package/test/interface.js +77 -74
- package/test/rsyncExec.js +86 -86
- package/test/sanityCheck.js +19 -19
- package/test/sshExec.js +47 -75
- package/test/testUtil/hostInfo.js +1 -2
- package/test/testUtil/testFiles.js +33 -22
- package/.eslintrc.json +0 -34
- package/.github/workflows/npm_publish.yml +0 -22
- package/test/.eslintrc.json +0 -28
package/lib/util.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { EOL } from "os";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import Debug from "debug";
|
|
4
|
+
import { setTimeout as setTimeoutPromise } from "timers/promises";
|
|
5
|
+
import Ajv from "ajv";
|
|
6
|
+
import ajvKeywords from "ajv-keywords";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
const rsyncCmd = "rsync";
|
|
9
|
-
const acceptableRsyncRetrunCodes = [10, 11, 12, 13, 14];
|
|
8
|
+
const debug = Debug("sshClientWrapper:debug:util");
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
10
|
+
export const sshCmd = "ssh";
|
|
11
|
+
export const rsyncCmd = "rsync";
|
|
12
|
+
export const acceptableRsyncRetrunCodes = [10, 11, 12, 13, 14];
|
|
13
|
+
|
|
14
|
+
export const rePwPrompt = /password:/;
|
|
15
|
+
export const rePhPrompt = /Enter passphrase for key/;
|
|
16
|
+
export const reNewHostPrompt = /Are you sure you want to continue connecting/;
|
|
17
|
+
export const reRemoteHostIdentificationHasChanged = /WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! /;
|
|
16
18
|
|
|
17
19
|
const ajv = new Ajv({
|
|
18
20
|
allErrors: true,
|
|
@@ -24,9 +26,9 @@ const ajv = new Ajv({
|
|
|
24
26
|
error: debug
|
|
25
27
|
}
|
|
26
28
|
});
|
|
27
|
-
|
|
29
|
+
ajvKeywords(ajv, "transform");
|
|
28
30
|
|
|
29
|
-
//
|
|
31
|
+
//never validate password and passphrase value to avoid security insident
|
|
30
32
|
const hostInfoSchema = {
|
|
31
33
|
type: "object",
|
|
32
34
|
properties: {
|
|
@@ -42,7 +44,11 @@ const hostInfoSchema = {
|
|
|
42
44
|
retryDuration: { type: "number", minimum: 0, default: 1000 },
|
|
43
45
|
rcfile: { type: "string", pattern: "\\S+", transform: ["trim"] },
|
|
44
46
|
prependCmd: { type: "string", pattern: "\\S+", transform: ["trim"] },
|
|
45
|
-
sshOpt: {
|
|
47
|
+
sshOpt: {
|
|
48
|
+
type: "array",
|
|
49
|
+
minItems: 1,
|
|
50
|
+
items: { type: "string", pattern: "\\S+", transform: ["trim"] }
|
|
51
|
+
}
|
|
46
52
|
},
|
|
47
53
|
required: ["host"]
|
|
48
54
|
};
|
|
@@ -66,20 +72,20 @@ const validate = ajv.compile(hostInfoSchema);
|
|
|
66
72
|
* @param {Function} func - function
|
|
67
73
|
* @param {data} data - binary data which can be converted to string
|
|
68
74
|
*/
|
|
69
|
-
const buff2String = (func, data)
|
|
75
|
+
export const buff2String = (func, data)=>{
|
|
70
76
|
func(data.toString().replace(/\r\n/g, EOL));
|
|
71
77
|
};
|
|
72
78
|
|
|
73
79
|
/**
|
|
74
80
|
* check if given hostInfo object is OK
|
|
75
|
-
* @param {
|
|
81
|
+
* @param {object} hostInfo - host information object defined in index.js
|
|
76
82
|
*/
|
|
77
|
-
const sanityCheck = (hostInfo)
|
|
83
|
+
export const sanityCheck = (hostInfo)=>{
|
|
78
84
|
debug("sanityCheck called", hostInfo);
|
|
79
85
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
86
|
+
//keep password, passphrase, and masterPty before validate.
|
|
87
|
+
//these properties can have a iellegal value as JSON data
|
|
88
|
+
//so we keep them before ajv validation and put them back
|
|
83
89
|
|
|
84
90
|
const { password, passphrase, masterPty } = hostInfo;
|
|
85
91
|
validate(hostInfo);
|
|
@@ -102,7 +108,7 @@ const sanityCheck = (hostInfo) => {
|
|
|
102
108
|
}
|
|
103
109
|
if (prop.startsWith("sshOpt")) {
|
|
104
110
|
debug("remove empty member of sshOpt");
|
|
105
|
-
hostInfo.sshOpt = hostInfo.sshOpt.filter((opt)
|
|
111
|
+
hostInfo.sshOpt = hostInfo.sshOpt.filter((opt)=>{
|
|
106
112
|
return opt !== "";
|
|
107
113
|
});
|
|
108
114
|
continue;
|
|
@@ -134,7 +140,7 @@ const sanityCheck = (hostInfo) => {
|
|
|
134
140
|
return hostInfo;
|
|
135
141
|
};
|
|
136
142
|
|
|
137
|
-
function getControlPersistDir
|
|
143
|
+
function getControlPersistDir(hostInfo) {
|
|
138
144
|
for (const candidate of [hostInfo.ControlPersistDir, process.env.SSH_CONTROL_PERSIST_DIR]) {
|
|
139
145
|
if (typeof candidate === "string") {
|
|
140
146
|
const stat = fs.statSync(candidate, { throwIfNoEntry: false });
|
|
@@ -148,10 +154,10 @@ function getControlPersistDir (hostInfo) {
|
|
|
148
154
|
|
|
149
155
|
/**
|
|
150
156
|
* return ssh option with some hard corded options
|
|
151
|
-
* @param {
|
|
157
|
+
* @param {object} hostInfo - host information object defined in index.js
|
|
152
158
|
* @param {boolean} withoutDestination - return except for hostname
|
|
153
159
|
*/
|
|
154
|
-
const getSshOption = (hostInfo, withoutDestination)
|
|
160
|
+
export const getSshOption = (hostInfo, withoutDestination)=>{
|
|
155
161
|
debug("getSshOption called");
|
|
156
162
|
const args = [];
|
|
157
163
|
|
|
@@ -205,7 +211,7 @@ const getSshOption = (hostInfo, withoutDestination) => {
|
|
|
205
211
|
args.push(`-oConnectTimeout=${hostInfo.ConnectTimeout}`);
|
|
206
212
|
}
|
|
207
213
|
if (Array.isArray(hostInfo.sshOpt)) {
|
|
208
|
-
args.push(...hostInfo.sshOpt.filter((e)
|
|
214
|
+
args.push(...hostInfo.sshOpt.filter((e)=>{
|
|
209
215
|
return typeof e === "string";
|
|
210
216
|
}));
|
|
211
217
|
}
|
|
@@ -213,24 +219,24 @@ const getSshOption = (hostInfo, withoutDestination) => {
|
|
|
213
219
|
return args;
|
|
214
220
|
};
|
|
215
221
|
|
|
216
|
-
const isArrayOfString = (target)
|
|
222
|
+
export const isArrayOfString = (target)=>{
|
|
217
223
|
if (!Array.isArray(target)) {
|
|
218
224
|
return false;
|
|
219
225
|
}
|
|
220
|
-
return !target.some((e)
|
|
221
|
-
//
|
|
226
|
+
return !target.some((e)=>{
|
|
227
|
+
//target has non string member
|
|
222
228
|
return typeof e !== "string";
|
|
223
229
|
});
|
|
224
230
|
};
|
|
225
231
|
|
|
226
232
|
/**
|
|
227
233
|
* log and send message to pty
|
|
228
|
-
* @param {
|
|
234
|
+
* @param {object} pty - pty object which message will be send to
|
|
229
235
|
* @param {string} message - message
|
|
230
236
|
* @param {Function} logger - logging function
|
|
231
237
|
* @param {string} label - where this function is called
|
|
232
238
|
*/
|
|
233
|
-
const sendPty = async (pty, message, logger, label)
|
|
239
|
+
export const sendPty = async (pty, message, logger, label)=>{
|
|
234
240
|
logger(`${label}: ${message}`);
|
|
235
241
|
pty.write(message);
|
|
236
242
|
};
|
|
@@ -242,15 +248,15 @@ const sendPty = async (pty, message, logger, label) => {
|
|
|
242
248
|
* @param {Function} cb - call back function which will be called after timeout
|
|
243
249
|
* @returns {AbortController} - ac to stop watch dog timer
|
|
244
250
|
*/
|
|
245
|
-
const watchDogTimer = (timeout, label, cb)
|
|
251
|
+
export const watchDogTimer = (timeout, label, cb)=>{
|
|
246
252
|
const ac = new AbortController();
|
|
247
253
|
const signal = ac.signal;
|
|
248
254
|
setTimeoutPromise(timeout * 1000, "neverUsedValue", { signal })
|
|
249
|
-
.then(()
|
|
255
|
+
.then(()=>{
|
|
250
256
|
debug(`can not finish ${label} within ${timeout} sec`);
|
|
251
257
|
return typeof cb === "function" ? cb() : true;
|
|
252
258
|
})
|
|
253
|
-
.catch((err)
|
|
259
|
+
.catch((err)=>{
|
|
254
260
|
if (err.name === "AbortError") {
|
|
255
261
|
debug(`timeout was aborted because ${label} has finished or canceled`);
|
|
256
262
|
} else {
|
|
@@ -259,19 +265,3 @@ const watchDogTimer = (timeout, label, cb) => {
|
|
|
259
265
|
});
|
|
260
266
|
return ac;
|
|
261
267
|
};
|
|
262
|
-
|
|
263
|
-
module.exports = {
|
|
264
|
-
sshCmd,
|
|
265
|
-
rsyncCmd,
|
|
266
|
-
acceptableRsyncRetrunCodes,
|
|
267
|
-
rePwPrompt,
|
|
268
|
-
rePhPrompt,
|
|
269
|
-
reNewHostPrompt,
|
|
270
|
-
reRemoteHostIdentificationHasChanged,
|
|
271
|
-
buff2String,
|
|
272
|
-
getSshOption,
|
|
273
|
-
sanityCheck,
|
|
274
|
-
isArrayOfString,
|
|
275
|
-
sendPty,
|
|
276
|
-
watchDogTimer
|
|
277
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh-client-wrapper",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "wrapper library for open ssh client and rsync",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ssh",
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"wrapper",
|
|
12
12
|
"rsync"
|
|
13
13
|
],
|
|
14
|
+
"type": "module",
|
|
14
15
|
"main": "lib/index.js",
|
|
15
16
|
"scripts": {
|
|
16
17
|
"mocha": "mocha --recursive test",
|
|
17
|
-
"lint": "eslint --fix
|
|
18
|
-
"test": "
|
|
19
|
-
"coverage": "
|
|
20
|
-
"
|
|
21
|
-
"prepare": "husky install"
|
|
18
|
+
"lint": "eslint --fix ./",
|
|
19
|
+
"test": "c8 --reporter=html --reporter=text mocha --recursive test",
|
|
20
|
+
"coverage": "c8 report --reporter=text-lcov > coverage-report.lcov",
|
|
21
|
+
"prepare": "husky"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
24
|
+
"node": ">=16.0.0"
|
|
25
25
|
},
|
|
26
26
|
"author": "Naoyuki Sogo <sogo@longtail-software.co.jp>",
|
|
27
27
|
"license": "MIT",
|
|
@@ -34,31 +34,32 @@
|
|
|
34
34
|
"*.js": "eslint --fix"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"ajv": "^8.
|
|
37
|
+
"ajv": "^8.17.1",
|
|
38
38
|
"ajv-keywords": "^5.1.0",
|
|
39
|
-
"debug": "^4.
|
|
40
|
-
"glob": "^
|
|
41
|
-
"node-pty": "^0.10.1"
|
|
42
|
-
"promise-retry": "^2.0.1"
|
|
39
|
+
"debug": "^4.4.1",
|
|
40
|
+
"glob": "^11.0.3",
|
|
41
|
+
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"eslint-plugin
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"eslint-plugin-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
44
|
+
"@eslint/js": "^9.33.0",
|
|
45
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
46
|
+
"@semantic-release/git": "^10.0.1",
|
|
47
|
+
"@semantic-release/github": "^11.0.4",
|
|
48
|
+
"@semantic-release/npm": "^12.0.2",
|
|
49
|
+
"@stylistic/eslint-plugin": "^5.2.3",
|
|
50
|
+
"c8": "^10.1.3",
|
|
51
|
+
"chai": "^5.3.1",
|
|
52
|
+
"chai-as-promised": "^8.0.1",
|
|
53
|
+
"eslint-plugin-chai-friendly": "^1.1.0",
|
|
54
|
+
"eslint-plugin-jsdoc": "^54.1.0",
|
|
55
|
+
"eslint-plugin-node": "^11.1.0",
|
|
56
|
+
"fs-extra": "^11.3.1",
|
|
57
|
+
"globals": "^16.3.0",
|
|
58
|
+
"husky": "^9.1.7",
|
|
59
|
+
"lint-staged": "^16.1.5",
|
|
60
|
+
"mocha": "^11.7.1",
|
|
61
|
+
"semantic-release": "^24.2.7",
|
|
62
|
+
"sinon": "^21.0.0",
|
|
63
|
+
"sinon-chai": "^4.0.0"
|
|
63
64
|
}
|
|
64
65
|
}
|
package/test/getSshOption.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
"
|
|
2
|
-
process.on("unhandledRejection", console.dir); // eslint-disable-line no-console
|
|
1
|
+
process.on("unhandledRejection", console.dir);
|
|
3
2
|
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
//setup test framework
|
|
4
|
+
import * as chai from "chai";
|
|
5
|
+
import { expect } from "chai";
|
|
6
|
+
import chaiAsPromised from "chai-as-promised";
|
|
7
|
+
chai.use(chaiAsPromised);
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
9
|
+
//testee
|
|
10
|
+
import { getSshOption } from "../lib/util.js";
|
|
11
11
|
|
|
12
|
-
describe("test for getSshOption", ()
|
|
12
|
+
describe("test for getSshOption", ()=>{
|
|
13
13
|
const defaultValues = {
|
|
14
14
|
host: "testHostName",
|
|
15
15
|
ControlPersist: 180,
|
|
16
16
|
maxRetry: 3,
|
|
17
17
|
retryDuration: 1000
|
|
18
18
|
};
|
|
19
|
-
it("should return minimum ssh option array", ()
|
|
19
|
+
it("should return minimum ssh option array", ()=>{
|
|
20
20
|
const sshOpts = getSshOption(defaultValues);
|
|
21
21
|
expect(sshOpts).to.be.an("array");
|
|
22
22
|
expect(sshOpts).to.have.lengthOf(4);
|
|
@@ -25,7 +25,7 @@ describe("test for getSshOption", () => {
|
|
|
25
25
|
expect(sshOpts[2]).to.equal("-oControlPath=~/.ssh/ssh-client-wrapper-%r@%h:%p");
|
|
26
26
|
expect(sshOpts[3]).to.equal("-oControlPersist=180");
|
|
27
27
|
});
|
|
28
|
-
it("should change ControlPath by ControlPersistDir", ()
|
|
28
|
+
it("should change ControlPath by ControlPersistDir", ()=>{
|
|
29
29
|
const hostInfo = { ...defaultValues };
|
|
30
30
|
hostInfo.ControlPersistDir = "/tmp";
|
|
31
31
|
const sshOpts = getSshOption(hostInfo);
|
|
@@ -36,7 +36,7 @@ describe("test for getSshOption", () => {
|
|
|
36
36
|
expect(sshOpts[2]).to.equal("-oControlPath=/tmp/ssh-client-wrapper-%r@%h:%p");
|
|
37
37
|
expect(sshOpts[3]).to.equal("-oControlPersist=180");
|
|
38
38
|
});
|
|
39
|
-
it("should change ControlPath by Environment variable", ()
|
|
39
|
+
it("should change ControlPath by Environment variable", ()=>{
|
|
40
40
|
process.env.SSH_CONTROL_PERSIST_DIR = "/tmp";
|
|
41
41
|
const sshOpts = getSshOption(defaultValues);
|
|
42
42
|
expect(sshOpts).to.be.an("array");
|
package/test/interface.js
CHANGED
|
@@ -1,53 +1,56 @@
|
|
|
1
|
-
"
|
|
2
|
-
process.on("unhandledRejection", console.dir); // eslint-disable-line no-console
|
|
1
|
+
process.on("unhandledRejection", console.dir);
|
|
3
2
|
Error.traceLimit = 100000;
|
|
4
3
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
//setup test framework
|
|
5
|
+
import * as chai from "chai";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import sinon from "sinon";
|
|
8
|
+
import sinonChai from "sinon-chai";
|
|
9
|
+
import chaiAsPromised from "chai-as-promised";
|
|
10
|
+
chai.use(sinonChai);
|
|
11
|
+
chai.use(chaiAsPromised);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// testee
|
|
15
|
-
const SshClientWrapper = rewire("../lib/index.js");
|
|
13
|
+
//testee
|
|
14
|
+
import SshClientWrapper from "../lib/index.js";
|
|
16
15
|
|
|
17
16
|
const sshExec = sinon.stub();
|
|
18
17
|
const canConnect = sinon.stub();
|
|
19
18
|
const disconnect = sinon.stub();
|
|
20
|
-
const
|
|
19
|
+
const expectStub = sinon.stub();
|
|
21
20
|
const send = sinon.stub();
|
|
22
21
|
const recv = sinon.stub();
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const deps = {
|
|
24
|
+
sshExec,
|
|
25
|
+
canConnect,
|
|
26
|
+
disconnect,
|
|
27
|
+
expect: expectStub,
|
|
28
|
+
send,
|
|
29
|
+
recv
|
|
30
|
+
};
|
|
28
31
|
|
|
29
|
-
describe("test for interface", ()
|
|
30
|
-
describe("test for constructor", ()
|
|
31
|
-
it("should throw error when instanciate with non-empty string host", ()
|
|
32
|
-
expect(()
|
|
33
|
-
|
|
34
|
-
new SshClientWrapper({});
|
|
32
|
+
describe("test for interface", ()=>{
|
|
33
|
+
describe("test for constructor", ()=>{
|
|
34
|
+
it("should throw error when instanciate with non-empty string host", ()=>{
|
|
35
|
+
expect(()=>{
|
|
36
|
+
new SshClientWrapper({}, deps);
|
|
35
37
|
}).to.throw("host is required");
|
|
36
|
-
expect(()
|
|
37
|
-
|
|
38
|
-
new SshClientWrapper({ host: "" });
|
|
38
|
+
expect(()=>{
|
|
39
|
+
new SshClientWrapper({ host: "" }, deps);
|
|
39
40
|
}).to.throw("empty host is not allowed");
|
|
40
41
|
});
|
|
41
|
-
it("should successfully instanciate only with host prop", ()
|
|
42
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("exec");
|
|
43
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("
|
|
44
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("
|
|
45
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("
|
|
46
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("
|
|
47
|
-
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("
|
|
42
|
+
it("should successfully instanciate only with host prop", ()=>{
|
|
43
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("exec");
|
|
44
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("execAndGetOutput");
|
|
45
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("ls");
|
|
46
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("expect");
|
|
47
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("send");
|
|
48
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("recv");
|
|
49
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("canConnect");
|
|
50
|
+
expect(new SshClientWrapper({ host: "hoge" }, deps)).to.have.property("disconnect");
|
|
48
51
|
});
|
|
49
52
|
});
|
|
50
|
-
describe("test for public method", ()
|
|
53
|
+
describe("test for public method", ()=>{
|
|
51
54
|
let ssh;
|
|
52
55
|
const hostInfoOrg = {
|
|
53
56
|
host: "hoge",
|
|
@@ -56,122 +59,122 @@ describe("test for interface", () => {
|
|
|
56
59
|
retryDuration: 1000
|
|
57
60
|
};
|
|
58
61
|
const hostInfo = { ...hostInfoOrg, masterPty: null, rsyncVersion: null };
|
|
59
|
-
beforeEach(()
|
|
60
|
-
ssh = new SshClientWrapper(hostInfoOrg);
|
|
62
|
+
beforeEach(()=>{
|
|
63
|
+
ssh = new SshClientWrapper(hostInfoOrg, deps);
|
|
61
64
|
sshExec.reset();
|
|
62
65
|
canConnect.reset();
|
|
63
66
|
disconnect.reset();
|
|
64
|
-
ls.reset();
|
|
65
67
|
send.reset();
|
|
66
68
|
recv.reset();
|
|
69
|
+
expectStub.reset();
|
|
67
70
|
});
|
|
68
|
-
describe("test for exec", ()
|
|
69
|
-
it("should call sshExec with cmd", ()
|
|
71
|
+
describe("test for exec", ()=>{
|
|
72
|
+
it("should call sshExec with cmd", ()=>{
|
|
70
73
|
ssh.exec("ls");
|
|
71
74
|
expect(sshExec).to.be.calledWith(hostInfo, "ls", 0, null);
|
|
72
75
|
});
|
|
73
|
-
it("should call sshExec with cmd and timeout", ()
|
|
76
|
+
it("should call sshExec with cmd and timeout", ()=>{
|
|
74
77
|
ssh.exec("ls", 10);
|
|
75
78
|
expect(sshExec).to.be.calledWith(hostInfo, "ls", 10, null);
|
|
76
79
|
});
|
|
77
|
-
it("should call sshExec with cmd, timeout and call back function", ()
|
|
78
|
-
const cb = ()
|
|
80
|
+
it("should call sshExec with cmd, timeout and call back function", ()=>{
|
|
81
|
+
const cb = ()=>{};
|
|
79
82
|
ssh.exec("ls", 10, cb);
|
|
80
83
|
expect(sshExec).to.be.calledWith(hostInfo, "ls", 10, cb);
|
|
81
84
|
});
|
|
82
|
-
it("should reject if cmd is not string (number)", ()
|
|
85
|
+
it("should reject if cmd is not string (number)", ()=>{
|
|
83
86
|
return expect(ssh.exec(0)).to.be.rejectedWith("cmd must be string");
|
|
84
87
|
});
|
|
85
|
-
it("should reject if cmd is not string (null)", ()
|
|
88
|
+
it("should reject if cmd is not string (null)", ()=>{
|
|
86
89
|
return expect(ssh.exec(null)).to.be.rejectedWith("cmd must be string");
|
|
87
90
|
});
|
|
88
|
-
it("should reject if cmd is empty string", ()
|
|
91
|
+
it("should reject if cmd is empty string", ()=>{
|
|
89
92
|
return expect(ssh.exec("")).to.be.rejectedWith("cmd must be string");
|
|
90
93
|
});
|
|
91
94
|
});
|
|
92
|
-
describe("test for ls", ()
|
|
93
|
-
it("should call ls without timeout", ()
|
|
95
|
+
describe("test for ls", ()=>{
|
|
96
|
+
it("should call ls without timeout", ()=>{
|
|
94
97
|
ssh.ls("hoge");
|
|
95
|
-
expect(
|
|
98
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls hoge", 0);
|
|
96
99
|
});
|
|
97
|
-
it("should call ls with option", ()
|
|
100
|
+
it("should call ls with option", ()=>{
|
|
98
101
|
ssh.ls("hoge", ["-l", "-r", "-t"]);
|
|
99
|
-
expect(
|
|
102
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls -l -r -t hoge", 0);
|
|
100
103
|
});
|
|
101
|
-
it("should call ls with option and timeout", ()
|
|
104
|
+
it("should call ls with option and timeout", ()=>{
|
|
102
105
|
ssh.ls("hoge", ["-l", "-r", "-t"], 10);
|
|
103
|
-
expect(
|
|
106
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls -l -r -t hoge", 10);
|
|
104
107
|
});
|
|
105
108
|
});
|
|
106
|
-
describe("test for send", ()
|
|
107
|
-
it("should call send with src and dst", ()
|
|
109
|
+
describe("test for send", ()=>{
|
|
110
|
+
it("should call send with src and dst", ()=>{
|
|
108
111
|
ssh.send(["src"], "dst");
|
|
109
112
|
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", [], 0);
|
|
110
113
|
});
|
|
111
|
-
it("should call send with src, dst, and opt", ()
|
|
114
|
+
it("should call send with src, dst, and opt", ()=>{
|
|
112
115
|
ssh.send(["src"], "dst", ["--dry-run", "--delete"]);
|
|
113
116
|
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 0);
|
|
114
117
|
});
|
|
115
|
-
it("should call send with src, dst, opt, and timeout", ()
|
|
118
|
+
it("should call send with src, dst, opt, and timeout", ()=>{
|
|
116
119
|
ssh.send(["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
117
120
|
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
118
121
|
});
|
|
119
|
-
it("should be rejected if src is not array of string", ()
|
|
122
|
+
it("should be rejected if src is not array of string", ()=>{
|
|
120
123
|
return expect(ssh.send(1)).to.be.rejectedWith("src must be array of string");
|
|
121
124
|
});
|
|
122
|
-
it("should be rejected if src has only empty string element", ()
|
|
125
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
123
126
|
return expect(ssh.send(["", ""])).to.be.rejectedWith("src must contain non-empty string");
|
|
124
127
|
});
|
|
125
|
-
it("should be rejected if src has only empty string element", ()
|
|
128
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
126
129
|
return expect(ssh.send([""])).to.be.rejectedWith("src must contain non-empty string");
|
|
127
130
|
});
|
|
128
|
-
it("should be called if src includes empty string ", ()
|
|
131
|
+
it("should be called if src includes empty string ", ()=>{
|
|
129
132
|
ssh.send(["src", ""], "dst");
|
|
130
133
|
expect(send).to.be.calledWith(hostInfo, ["src", ""], "dst", [], 0);
|
|
131
134
|
});
|
|
132
|
-
it("should be rejected if dst is not string", ()
|
|
135
|
+
it("should be rejected if dst is not string", ()=>{
|
|
133
136
|
return expect(ssh.send(["src"], 0)).to.be.rejectedWith("dst must be string");
|
|
134
137
|
});
|
|
135
|
-
it("should be rejected if dst is empty string", ()
|
|
138
|
+
it("should be rejected if dst is empty string", ()=>{
|
|
136
139
|
return expect(ssh.send(["src"], "")).to.be.rejectedWith("dst must be non-empty string");
|
|
137
140
|
});
|
|
138
|
-
it("should be rejected if opt is not array of string", ()
|
|
141
|
+
it("should be rejected if opt is not array of string", ()=>{
|
|
139
142
|
return expect(ssh.send(["src"], "dst", [null])).to.be.rejectedWith("opt must be array of string");
|
|
140
143
|
});
|
|
141
144
|
});
|
|
142
|
-
describe("test for recv", ()
|
|
143
|
-
it("should call recv with src and dst", ()
|
|
145
|
+
describe("test for recv", ()=>{
|
|
146
|
+
it("should call recv with src and dst", ()=>{
|
|
144
147
|
ssh.recv(["src"], "dst");
|
|
145
148
|
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", [], 0);
|
|
146
149
|
});
|
|
147
|
-
it("should call recv with src, dst, and opt", ()
|
|
150
|
+
it("should call recv with src, dst, and opt", ()=>{
|
|
148
151
|
ssh.recv(["src"], "dst", ["--dry-run", "--delete"]);
|
|
149
152
|
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 0);
|
|
150
153
|
});
|
|
151
|
-
it("should call recv with src, dst, opt, and timeout", ()
|
|
154
|
+
it("should call recv with src, dst, opt, and timeout", ()=>{
|
|
152
155
|
ssh.recv(["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
153
156
|
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
154
157
|
});
|
|
155
|
-
it("should be rejected if src is not array of string", ()
|
|
158
|
+
it("should be rejected if src is not array of string", ()=>{
|
|
156
159
|
return expect(ssh.recv(1)).to.be.rejectedWith("src must be array of string");
|
|
157
160
|
});
|
|
158
|
-
it("should be rejected if src has only empty string element", ()
|
|
161
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
159
162
|
return expect(ssh.recv(["", ""])).to.be.rejectedWith("src must contain non-empty string");
|
|
160
163
|
});
|
|
161
|
-
it("should be rejected if src has only empty string element", ()
|
|
164
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
162
165
|
return expect(ssh.recv([""])).to.be.rejectedWith("src must contain non-empty string");
|
|
163
166
|
});
|
|
164
|
-
it("should be called if src includes empty string ", ()
|
|
167
|
+
it("should be called if src includes empty string ", ()=>{
|
|
165
168
|
ssh.recv(["src", ""], "dst");
|
|
166
169
|
expect(recv).to.be.calledWith(hostInfo, ["src", ""], "dst", [], 0);
|
|
167
170
|
});
|
|
168
|
-
it("should be rejected if dst is not string", ()
|
|
171
|
+
it("should be rejected if dst is not string", ()=>{
|
|
169
172
|
return expect(ssh.recv(["src"], 0)).to.be.rejectedWith("dst must be string");
|
|
170
173
|
});
|
|
171
|
-
it("should be rejected if dst is empty string", ()
|
|
174
|
+
it("should be rejected if dst is empty string", ()=>{
|
|
172
175
|
return expect(ssh.recv(["src"], "")).to.be.rejectedWith("dst must be non-empty string");
|
|
173
176
|
});
|
|
174
|
-
it("should be rejected if opt is not array of string", ()
|
|
177
|
+
it("should be rejected if opt is not array of string", ()=>{
|
|
175
178
|
return expect(ssh.recv(["src"], "dst", [null])).to.be.rejectedWith("opt must be array of string");
|
|
176
179
|
});
|
|
177
180
|
});
|