From 606978c37ec1fb52b23bf8fa0c8aae20df8de33a Mon Sep 17 00:00:00 2001 From: David Kebler Date: Thu, 14 Feb 2019 14:01:09 -0800 Subject: [PATCH] ssh split off removed other files, ssh cli operational, fixed one off shell command issues --- .gitignore | 1 + .npmignore | 1 + bin/ssh.js | 14 +-- bin/ssh.sh | 6 +- package.json | 26 +++--- readme.md | 17 +--- src/index.js | 9 +- src/read-lines.js | 54 ----------- src/remote-code.js | 123 -------------------------- src/ssh.js | 45 +++++----- src/sync.js | 113 ----------------------- src/watcher.js | 41 --------- test/config/ssh.yaml | 8 +- test/config/sync.yaml | 6 -- test/read-lines.test.js.off | 54 ----------- test/repo/.gitignore | 2 - test/repo/.npmignore | 5 -- test/repo/.rcignore | 2 - test/repo/.testignore | 1 - test/repo/combined.list | 7 -- test/{ssh.test.js.off => ssh.test.js} | 16 +++- test/sync.test.js | 48 ---------- test/watcher.test.js.off | 49 ---------- 23 files changed, 72 insertions(+), 576 deletions(-) delete mode 100644 src/read-lines.js delete mode 100644 src/remote-code.js delete mode 100644 src/sync.js delete mode 100644 src/watcher.js delete mode 100644 test/config/sync.yaml delete mode 100644 test/read-lines.test.js.off delete mode 100644 test/repo/.gitignore delete mode 100644 test/repo/.npmignore delete mode 100644 test/repo/.rcignore delete mode 100644 test/repo/.testignore delete mode 100644 test/repo/combined.list rename test/{ssh.test.js.off => ssh.test.js} (65%) delete mode 100644 test/sync.test.js delete mode 100644 test/watcher.test.js.off diff --git a/.gitignore b/.gitignore index e61051f..5793926 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ /coverage/ +*.log diff --git a/.npmignore b/.npmignore index 02078d5..90e0970 100644 --- a/.npmignore +++ b/.npmignore @@ -3,3 +3,4 @@ test/ *.test.js testing/ example/ +nodemon.json diff --git a/bin/ssh.js b/bin/ssh.js index 2daa7cd..16ddf98 100644 --- a/bin/ssh.js +++ b/bin/ssh.js @@ -1,27 +1,29 @@ #!/usr/bin/env node +// third party imports import Ssh from '../src/ssh' import to from 'await-to-js' -import logger from '@uci/logger' +import logger from '@uci-utils/logger' import yargs from 'yargs' +const log = logger({appName:'sshu', package:'ssh', bin:'./bin/ssh.js', class:'Ssh', id: 'ssh-binary' }) -console.log(process.argv) +log.debug({arguments:process.argv, msg:'arguments passed to ssh cli'}) let arg = yargs .alias('c','config') .argv ; (async () => { - const log = logger({ repo: 'remote-code', bin:'./bin/ssh.js', class:'Ssh', file:'src/ssh.js', id: 'binary' }) const ssh = new Ssh() - console.log('config file', arg) - await ssh.configure(arg.c ? arg.c : null) // default config + arg.c ? log.debug(`loading ssh config file ${arg.c}`) : log.debug('loading ssh default config file') + await ssh.configure(arg.c) // null = default config log.info(`making connection to ${ssh.opts.host}`) let [err] = await to(ssh.connect()) if (err) { log.info({err:err, msg:'unable to connect'}) return err } + // let cmd = arg._.join(' ') // console.log(cmd) // let [err2,res] = await to(ssh.exec(cmd)) @@ -33,7 +35,7 @@ let arg = yargs // return res let res = await ssh.shell({cli:true}) - console.log('ALL DONE', res.command, res.cmds, ) + log.info({history:res.cmds, msg:'returned from remote shell'}) })().catch(err => { diff --git a/bin/ssh.sh b/bin/ssh.sh index dded45b..4fb203e 100755 --- a/bin/ssh.sh +++ b/bin/ssh.sh @@ -1,2 +1,4 @@ -DEV_BIN=/mnt/238/nas/david/hacking/active-dev-repos/uci/lib/uci-remote-code/bin -UCI_DEV=true /usr/bin/node $DEV_BIN/ssh-cli $@ +#!/bin/bash +LINK=`readlink -f $0` +DEV_BIN=`dirname $LINK` +UCI_DEV=true /usr/bin/node $DEV_BIN/ssh-cli "$@" diff --git a/package.json b/package.json index c4c8e31..c6f3c36 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,22 @@ { - "name": "@uci/remote-code", - "version": "0.0.2", - "description": "module to copy, maintain, and launch hardware modules on other machines", - "main": "src/index.js", + "name": "@uci-utils/ssh", + "version": "0.0.4", + "description": "ssh client cli and api, a wrapper on ssh2", + "main": "src/ssh.js", "bin": { "ssh": "./bin/ssh.js" }, "scripts": { "ssh": "node -r esm ./bin/ssh", - "testd": "UCI_DEV=true ./node_modules/.bin/nodemon --exec './node_modules/.bin/mocha -r esm --timeout 30000'", - "test": "UCI_PRO=./test/test.log ./node_modules/.bin/mocha -r esm --timeout 30000 || exit 0", + "testd": "UCI_ENV=dev ./node_modules/.bin/nodemon --exec './node_modules/.bin/mocha -r esm --timeout 30000'", + "test": "UCI_ENV=pro UCI_LOG_PATH=./test/test.log ./node_modules/.bin/mocha -r esm --timeout 30000 || exit 0", "testibc": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true" }, "author": "David Kebler", "license": "MIT", "repository": { "type": "git", - "url": "git+https://github.com/uCOMmandIt/uci-remote-code.git" + "url": "git+https://github.com/uCOMmandIt/uci-ssh.git" }, "keywords": [ "node.js", @@ -24,24 +24,22 @@ "raspberryPi" ], "bugs": { - "url": "https://github.com/uCOMmandIt/uci-remote-code/issues" + "url": "https://github.com/uCOMmandIt/uci-ssh/issues" }, - "homepage": "https://github.com/uCOMmandIt/uci-remote-code#readme", + "homepage": "https://github.com/uCOMmandIt/uci-ssh#readme", "dependencies": { - "@uci/logger": "0.0.9", + "@uci-utils/logger": "0.0.13", "await-to-js": "^2.1.1", - "chokidar": "^2.0.4", "conf": "^2.2.0", + "esm": "^3.2.4", "fs-read-data": "^1.0.4", - "p-settle": "^2.1.0", "ssh2": "^0.8.2", - "yargs": "^12.0.5" + "yargs": "^13.1.0" }, "devDependencies": { "chai": "^4.2.0", "chai-arrays": "^2.0.0", "codecov": "^3.1.0", - "esm": "^3.1.4", "istanbul": "^0.4.5", "mocha": "^5.x", "nodemon": "^1.18.9" diff --git a/readme.md b/readme.md index 83b6323..394cb37 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,6 @@ -# uCOMmandIt Template Package Repository +# uCOMmandIt SSH Client + +## Secure Shell (ssh) API and CLI client based on ssh2 [![Build Status](https://img.shields.io/travis/uCOMmandIt/uci-pkg-template.svg?branch=master)](https://travis-ci.org/uCOMmandIt/uci-pkg-template) @@ -6,16 +8,3 @@ [![Dependencies](https://img.shields.io/david/uCOMmandIt/uci-pkg-template.svg)](https://david-dm.org/uCOMmandIt/uci-pkg-template) [![devDependencies](https://img.shields.io/david/dev/uCOMmandIt/uci-pkg-template.svg)](https://david-dm.org/uCOMmandIt/uci-pkg-template?type=dev) [![codecov](https://img.shields.io/codecov/c/github/uCOMmandIt/uci-pkg-template/master.svg)](https://codecov.io/gh/uCOMmandIt/uci-pkg-template) - -Clone this to get a quick start on a new uci package. It has all the testing ready to go with Travis-CI and code coverage. All the readme badges are included as well - -Clone it for as a starting place for your own package! - -You'll need codecov and travis-ci accounts - -##Steps - -1. Clone repo -2. Edit package.json -3. Edit badge urls above changing to repo path -4. Start Coding diff --git a/src/index.js b/src/index.js index aadf982..0f05872 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,3 @@ -import RemoteCode from './remote-code' import Ssh from './ssh' -import Sync from './sync' -import Watcher from './watcher' -import ignore from './ignore' - -export { RemoteCode, Ssh, Sync, Watcher, ignore } -export default RemoteCode +export { Ssh } +export default Ssh diff --git a/src/read-lines.js b/src/read-lines.js deleted file mode 100644 index b02832e..0000000 --- a/src/read-lines.js +++ /dev/null @@ -1,54 +0,0 @@ - -import { promisify } from 'util' -import { readFile, writeFile } from 'fs' -const read = promisify(readFile) -const write = promisify(writeFile) -// alternative with new promise experimental fs -// import fs from 'fs' -// const read = fs.promises.readFile -import settle from 'p-settle' -import path from 'path' -import logger from '@uci/logger' -let log = logger({ name: 'remote-code', file:'src/read-lines.js'}) - -// A promise helper function to return a list of paths to ignore from .npmignore, .gitignore, .rcignore -function readLines (files=[],dir) { - // console.log('additional files', files) - let list = [] - if (!Array.isArray(files)) files=[files] - - // each set in an the array is new line delimited set of ignore patterns - // settle returns array of error,value pairs - return settle(files.map(file => { - // console.log('directory',path.dirname(file)) - if (path.dirname(file)==='.') file = dir+'/'+file - log.info({function:'readLines',file:file,msg:'reading a file of lines into array'}) - return read(file) - })) - .then((sets) => { - sets.forEach( set => { - if (set.isFulfilled) list.push(...set.value.toString().match(/.+/g)) - else log.warn({function:'readLines', error:set.reason, msg:' was unable to read file'}) - }) - return Promise.resolve(list) - }) - .catch((err) => { - // only returned when something horrible is wrong - return Promise.reject(err) - }) -} - -// an ignore list should not be huge so can serailize at once -function writeLines (filePath,list) { - - return write(filePath,list.join('\n')) - .then(() => { - log.info({function:'writeLines', file:filePath, msg:'wrote array to file of lines'}) - }) - .catch( err => { - log.fatal({function:'writeLines', error:err, msg:'unable to write array to file of lines'}) - }) -} - -export default readLines -export { readLines, writeLines } diff --git a/src/remote-code.js b/src/remote-code.js deleted file mode 100644 index 46cc93f..0000000 --- a/src/remote-code.js +++ /dev/null @@ -1,123 +0,0 @@ -import { EventEmitter as Emitter } from 'events' -import stream from 'stream' -import Watcher from './watcher' -import Ssh from './ssh' -import Sync from './lib/sync' - -const devNull = new stream.Writable() -devNull._write = () => null - -class RemoteCode { - constructor(opts = {}) { - // Set defaults - // a id emitted with every event emit that can identify which remote code emitted - this.id = opts.id - this.watch = opts.watch || false - this.options.ssh.keepaliveInterval = opts.ssh.keepaliveInterval || 500 - this.options.ssh.readyTimeout = opts.ssh.readyTimeout || 2000 - this.options.ssh.port = opts.ssh.port || 22 - this.options.source = opts.source || '.' - this.options.install = opts.install || 'yarn' - this.options.install = opts.registry ? `${this.options.install} --registry ${opts.registry}` : this.options.install - this.options.start = opts.start || 'nodemon .' - if (!(opts.ssh.keyfilePath || opts.ssh.password)) { - this.options.ssh.agent = process.env[opts.ssh.agent] || process.env.SSH_AUTH_SOCK || process.env.SSH_AGENT_SOCK - if (!this.options.ssh.agent) { - return new Error('no ssh authentification method provided') - } - } - this.ssh = new Ssh() - this.verbose = opts.verbose || false - this.stdout = opts.stdout instanceof stream.Writable ? opts.stdout : new stream.Writable() - this.stderr = opts.stderr instanceof stream.Writable ? opts.stderr : new stream.Writable() - this.watcher = new Watcher(this.options) - this.sync = new Sync(this.options) - .addStdOutStream(this.stdout) - .addStdErrStream(this.stderr) - } - - - - _getStdOut() { - if (this.verbose) { - return this.stdout - } - return devNull - } - - _getStdErr() { - if (this.verbose) { - return this.stderr - } - return devNull - } - - sync() { - this.emit('sync',{}) - return this.sync.execute() - } - - watch() { - this.watcher.start() - const watchEmitter = this.watcher.getEventEmitter() - watchEmitter.on('sync', () => { - this.syncCode() - }) - watchEmitter.on('install', () => { - return this.install() - .then(() => { - this.ssh.liveReload.send('rs') - }) - }) - return this.emitter - } - - async init() { - this.emitter.emit('start') - const sshSettings = this.options.ssh - return Promise.all([this.syncCode(), this.watch()]) - .then(() => this.install()) - .then(() => this.ssh.liveReload.connect(sshSettings)) - .then(() => { - this.emitter.emit('nodemon', 'start') - this.ssh.liveReload.send(`cd ${this.options.target} && ${this.options.start}`) - }) - .catch(this._abort.bind(this)) - } - - // execute a single command and then resolve - execute(cmd, stdout, stderr) { - console.log('remote codes exec', cmd) - this.emitter.emit('exec', cmd) - const ssh = new Ssh() - const result = ssh.exec(this.options.ssh, cmd, stdout, stderr) - return result - } - - async install() { - this.emit('install', 'triggered') - if (!this.installInProgress) { - this.emitter.emit('install', 'started') - console.log('calling execute from install') - return this.execute(`cd ${this.options.target} && ${this.options.install}`, this._getStdOut(), this._getStdErr()) - .then(res => { - this.emit('install', 'done', res) - this.installInProgress = false - return res - }) - } - return Promise.resolve() - } - - close() { - this.emitter.emit('close') - return Promise.all([this.watcher.close(), - this.ssh.liveReload.close()]) - } - - _abort(err) { - this.emitter.emit('error', err) - } -} - -export default RemoteCode diff --git a/src/ssh.js b/src/ssh.js index 1e81f10..1c21525 100644 --- a/src/ssh.js +++ b/src/ssh.js @@ -1,12 +1,14 @@ +// native imports import { promisify } from 'util' import readline from 'readline' import { dirname } from 'path' -import to from 'await-to-js' import { readFile } from 'fs' const read = promisify(readFile) +// third party imports +import to from 'await-to-js' import { readFile as readConfig } from 'fs-read-data' import { Client } from 'ssh2' -import logger from '@uci/logger' +import logger from '@uci-utils/logger' import Conf from 'conf' let log = {} // declare module wide log to be set during construction @@ -15,11 +17,11 @@ class Ssh extends Client { constructor(opts={}) { super() this.opts = opts - log = logger({ name: 'remote-code', file:'src/ssh.js', class:'Ssh', id: opts.id }) + log = logger({id: opts.id }) this.ready = false this.config = new Conf({projectName:'ssh-uci'}) this.configsDir = process.env.SSH_CONFIG_DIR || this.config.get('configsDir') || dirname(this.config.path) - console.log(this.configsDir) + log.debug({configsDir:this.configsDir, msg:'configuration files directory'}) this.rpath = opts.rpath || '/opt' // can be used to make all commands run in a particular remote path } @@ -35,16 +37,15 @@ class Ssh extends Client { opts = await readConfig(configPath) } - console.log('configPath',configPath) - console.log('opts',opts) + log.debug({configPath:this.configPath, msg:'path to actual configuration file used'}) + log.debug({opts:opts, msg:'connection options as read in from file'}) this.opts = overwrite ? Object.assign(this.opts,opts) : Object.assign(opts, this.opts) - - + log.debug({opts:opts, msg:'connection options as ammended from configure() argument'}) } async connect(copts = {}) { const opts = Object.assign({},this.opts,copts) // merge any changes pasted via connect - // log.info('connection options ', opts) + log.debug({opts:opts, msg:'final connection options as ammend via connect() argument and as used'}) if (opts.agentenv) opts.agent = process.env[opts.agentenv] opts.readyTimeout = opts.readyTimeout | 5000 // default was 20 seconds...too long opts.privateKey = opts.privateKey || opts.privateKeyPath ? await read(opts.privateKeyPath) : null @@ -57,7 +58,7 @@ class Ssh extends Client { resolve(`connected to ${this.opts.host}`) }) this.on('error', (err) => { - log.info({err:err, opts:this.opts, msg:'connection error'}) + log.warn({err:err, opts:this.opts, msg:'connection error'}) reject(err) }) }) @@ -90,21 +91,20 @@ class Ssh extends Client { let [err] = await to(this.connect()) if (err) return err } - // log.info('executing command ', command) - const remote = this + const remote = this // TODO bind this on promise instead const superexec = opts.shell? super.shell.bind(this) : super.exec.bind(this) let command = opts.shell ? opts : cmd + // log.info(`executing command ${command} on shell? ${opts.shell}`) return new Promise(function(resolve, reject) { - let reply - let error + let reply ='' + let error = '' superexec(command, function(err, stream) { - console.log('command,cmd,opts',command,cmd,opts) + log.info({command:command, cmd:cmd, opts:opts, msg:'before ssh exec/shell'}) const done = () => { if (error) remote.emit('remote:error',error) resolve({command:command,cmds:cmd,reply:reply,error:error}) } if (err) reject(err) - // listerners stream.once('close', () => { if (remote.opts.close || opts.close || opts.cli) { remote.on('end', done) @@ -125,21 +125,22 @@ class Ssh extends Client { const cli= readline.createInterface({input:process.stdin, output:process.stdout}) cli.on('line', line => { cmd.push(line) - console.log('storedcmds=>', cmd) if(line.trim() === 'exit') { - console.log('ending stream') cli.removeAllListeners() cli.close() stream.end(`${line}\n`) } else { - console.log('not exit') stream.write(`${line}\n`) } }) // end terminal listener } - } - else { - stream.end(cmd) // one off shell command + else { + // give a bit of time for MOTD to display so it's not part of reply + setTimeout(() => { + reply='' + stream.end(`${cmd}\nexit\n`) + }, opts.delay || 300) + } } // end shell processing }) // end super }) // end Promise diff --git a/src/sync.js b/src/sync.js deleted file mode 100644 index 46f963e..0000000 --- a/src/sync.js +++ /dev/null @@ -1,113 +0,0 @@ -import { EventEmitter as Emitter } from 'events' -import path from 'path' -// import promisify from 'util' -import { readFile as readConfig } from 'fs-read-data' -import { exec as child } from 'child_process' -// import ignoreFiles from './ignore' -import logger from '@uci/logger' -let log = {} // declare module wide log to be set during construction - -class Sync extends Emitter { - constructor(opts = {}) { - super() - log = logger({ name: 'remote-code', file:'src/sync.js', class:'Sync'}) - this.opts = opts - // this.opts.more = this.opts.more || [] - // this.opts.ssh = this.opts.ssh || [] - this.jobOpts = {} - } - - async configure(opts,overwrite) { - if (typeof opts === 'string') opts = await readConfig(opts) // opts here is path to configuration file - this.opts = overwrite ? Object.assign(this.opts,opts) : Object.assign(opts, this.opts) - } - - addOpt(option,value) { - this.opts.more = this.opts.more || [] - this.opts.more.push(`${(option.length > 1)?'--':'-'}${option} ${value}`) - log.info (this.opts.more) - } - - addSshOpt(option,value) { - this.opts.ssh = this.opts.ssh || [] - this.opts.ssh.push(`${(option.length > 1)?'--':'-'}${option} ${value}`) - log.info (this.opts.ssh) - } - - command(src, dest) { - this.opts.src = src || this.opts.src || process.cwd() - this.opts.dest = dest || this.opts.dest || `/opt/${process.cwd().match(/([^/]*)\/*$/)[1]}` - this.cmd=`rsync ${this._opts} ${this._ssh()} ${this.opts.src}/ ${this.opts.dest}` - // this.cmd = JSON.stringify(this.opts) - return this.cmd - } - - _ssh() { - if (!(this.opts.username && this.opts.host)) return '' - this.opts.dest = `${this.opts.username}@${this.opts.host}:${this.opts.dest}` - if (this.opts.keyFilePath) this.addSshOpt('i',this.opts.keyFilePath) - if (this.opts.port) this.addSshOpt('p',this.opts.port) - if (!this.opts.ssh) return '' - return `-e "ssh ${this.opts.ssh.join(' ')}"` - } - - _opts() { - if (typeof opts.mirror==='boolean' && !opts.mirror) - - - // if ssh agent is not available in child process can make it so - _agent(path) { - // this.jobOpts.env - } - - // returns a promise when job is done - execute(src,dest) { - const sync = this - let reply=[] - let error=[] - sync.emit('busy') - return new Promise((resolve, reject) => { - // may need to be sure that child has agent socket envirnoment variable - if (this.opts.agent) this._agent(this.opts.agent) - const job = child(this.command(src,dest), this.jobOpts) - - job.on('error', (err) => { - log.fatal({err:err, opts:sync.opts, msg:'failed to spawn rsync job'}) - reject(err) - }) - - job.on('close', (code) => { - log.info(`sync job exited with code ${code}`) - if (error.length!==0) sync.emit('remote:error',error) - sync.emit('done',{command:sync.cmd, reply:reply}) - resolve({command:sync.cmd,reply:reply,error:error}) - }) - - job.stdout.on('data', (data) => { - log.info({remote:sync.opts.host, console:data, msg:'console data from remote'}) - reply.push(data) - }) - - job.stderr.on('data', (data) => { - log.warn({remote:sync.opts.host, error:data, msg:'error from remote'}) - error.push(data) - }) - - }) - } - -}// end Class Sync - -export default Sync - -function isPlainObject (obj) { - return Object.prototype.toString.call(obj) === '[object Object]' -} - -function escapeSpaces (str) { - if (typeof str === 'string') { - return str.replace(/\b\s/g, '\\ ') - } else { - return path - } -} diff --git a/src/watcher.js b/src/watcher.js deleted file mode 100644 index f7a3ad8..0000000 --- a/src/watcher.js +++ /dev/null @@ -1,41 +0,0 @@ -import { EventEmitter as Emitter } from 'events' -import path from 'path' -import chokidar from 'chokidar' - -class Watcher extends Emitter { - constructor(options) { - super() - // pass in ignores - const opts = { - ignored: '**/node_modules/**', - ignoreInitial: true - } - const watcher = chokidar.watch(options.source, opts) - this.watcher = watcher - } - - start() { - const handler = (type, f) => { - const fname = path.basename(f) - if ( fname.toLowerCase() === 'package.json') - if (type !=='change') { - this.emit('error',new Error('package.json was added or removed, ignoring sync and reinstall')) - return - } else{ - this.emit('install', f) - } - this.emit('sync', f) - } // end handler - this.watcher - .on('add', handler.bind(this, 'add')) - .on('change', handler.bind(this, 'change')) - .on('unlink', handler.bind(this, 'remove')) - } - - stop() { - this.watcher.close() - } - -} - -export default Watcher diff --git a/test/config/ssh.yaml b/test/config/ssh.yaml index 1ff04f2..e51f990 100644 --- a/test/config/ssh.yaml +++ b/test/config/ssh.yaml @@ -1,6 +1,6 @@ host: switches.kebler.net username: sysadmin -# agent: /run/user/1000/ssh-agent.socket -# agentenv: SSH_AUTH_SOCK -#privateKeyPath: /home/david/.ssh/privatekeys/sysadmin.kebler.net -#passphrase: '51535560' +#agent: /run/user/1000/ssh-agent.socket +#agentenv: SSH_AUTH_SOCK +privateKeyPath: /home/david/.ssh/privatekeys/sysadmin.kebler.net +passphrase: '51535560' diff --git a/test/config/sync.yaml b/test/config/sync.yaml deleted file mode 100644 index 4d536fe..0000000 --- a/test/config/sync.yaml +++ /dev/null @@ -1,6 +0,0 @@ -host: giskard.kebler.net -#port: 33 -username: sysadmin -keyFilePath: '~/.ssh/keyfile' -src: ./test/repo -dest: /opt/repo diff --git a/test/read-lines.test.js.off b/test/read-lines.test.js.off deleted file mode 100644 index 8baad10..0000000 --- a/test/read-lines.test.js.off +++ /dev/null @@ -1,54 +0,0 @@ -// let ignoreFiles = ['.npmignore','.gitignore'] - -import { readLines, writeLines } from '../src/read-lines' -import chai from 'chai' -import assertArrays from 'chai-arrays' -import { it } from 'mocha' - -chai.use(assertArrays) -const expect = chai.expect - -describe ( - 'Read a File of Lines to Array and vice versa', - function () { - readList() - writeList() - }) - -//****************** TESTS ********************** -function readList() { - it('==> can read one or more files (no order) each line as element in an array with common directory', async function () { - const shouldbe = [ 'tests/', - 'test/', - '*.test.js', - 'testing/', - 'example/', - '/node_modules/', - '/coverage/' ] - let result = await readLines(['.gitignore','.npmignore'],__dirname+'/repo') - expect(result, 'list build failed').to.be.containingAllOf(shouldbe) - }) - - it('==> can read two files one relative the other absolute', async function () { - const shouldbe = [ 'tests/', - 'test/', - '*.test.js', - 'testing/', - 'example/', - '/node_modules/', - '/coverage/' ] - let result = await readLines(['./test/repo/.gitignore',__dirname+'/repo/.npmignore']) - expect(result, 'list build failed').to.be.containingAllOf(shouldbe) - }) -} - - -function writeList() { - - it('==> can write an array items as lines in a file', async function () { - const shouldbe = await readLines(['.gitignore','.npmignore'],__dirname+'/repo') - await writeLines(__dirname+'/repo/combined.list',shouldbe) - const result = await readLines(['combined.list'],__dirname+'/repo') - expect(result, 'list build failed').to.be.containingAllOf(shouldbe) - }) -} diff --git a/test/repo/.gitignore b/test/repo/.gitignore deleted file mode 100644 index e61051f..0000000 --- a/test/repo/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/node_modules/ -/coverage/ diff --git a/test/repo/.npmignore b/test/repo/.npmignore deleted file mode 100644 index 02078d5..0000000 --- a/test/repo/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -tests/ -test/ -*.test.js -testing/ -example/ diff --git a/test/repo/.rcignore b/test/repo/.rcignore deleted file mode 100644 index c3dfa01..0000000 --- a/test/repo/.rcignore +++ /dev/null @@ -1,2 +0,0 @@ -foo/** -bar/*.js diff --git a/test/repo/.testignore b/test/repo/.testignore deleted file mode 100644 index 6d2e0a4..0000000 --- a/test/repo/.testignore +++ /dev/null @@ -1 +0,0 @@ -bah/*/*.js diff --git a/test/repo/combined.list b/test/repo/combined.list deleted file mode 100644 index 6115502..0000000 --- a/test/repo/combined.list +++ /dev/null @@ -1,7 +0,0 @@ -/node_modules/ -/coverage/ -tests/ -test/ -*.test.js -testing/ -example/ \ No newline at end of file diff --git a/test/ssh.test.js.off b/test/ssh.test.js similarity index 65% rename from test/ssh.test.js.off rename to test/ssh.test.js index 9590a96..d98eb80 100644 --- a/test/ssh.test.js.off +++ b/test/ssh.test.js @@ -16,7 +16,7 @@ describe('SSH Class Testing ',async ()=> { let [err] = await to(remote.connect()) if (err) { log.info('unable to connect aborting test', err) - return err + throw new Error (err) } log.info('ready for testing') }) @@ -25,7 +25,7 @@ describe('SSH Class Testing ',async ()=> { remote.close() }) - it('simple connect and reply to "cd /opt && pwd"' , async function () { + it('simple connect and execute "cd /opt && pwd" on remote' , async function () { let [err,res] = await to(remote.exec('cd /opt && pwd')) if (err) { log.info('error running command aborting test', err) @@ -35,6 +35,18 @@ describe('SSH Class Testing ',async ()=> { expect(res.reply.toString().trim(), 'test failed').to.equal('/opt') }) + it('connect via an "interactive shell" and cat the test.txt file in home directory"' , async function () { + let [err,res] = await to(remote.shell('cat test.txt')) // cd /opt && pwd + if (err) { + log.info('error running shell command aborting test', err) + throw new Error(err) + } + let passed = false + if (res.reply.indexOf('47fciogjei439djfjla') > 0) passed=true + log.info({remoteHost:remote.opts.host, reply:res.reply.toString().trim(), err:err, cmd:res.command, msg:`remote command completed ${res.command}`}) + expect(passed, 'test failed').to.equal(true) + }) + }) // function hooks(remote) { diff --git a/test/sync.test.js b/test/sync.test.js deleted file mode 100644 index 964afda..0000000 --- a/test/sync.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import Sync from '../src/sync' -import to from 'await-to-js' -import { expect } from 'chai' -import { it } from 'mocha' -import logger from '@uci/logger' -// pause = require('@uci/utils').pPause - -describe('Sync Class Testing ',async ()=> { - - let sync - let log - before(async () => { - log = logger({ name: 'remote-code', test:'/test/sync.test.js', class:'sync',file:'src/sync.js', id: 'testing' }) - sync = new Sync() - await sync.configure('./test/repo/config') - log.info({cmd:sync.command(), msg:'Rsync Command that will Run'}) - // log.info(`making connection to ${remote.opts.host}`) - // log.info('ready for testing') - }) - - // after(async () => { - // remote.close() - // }) - - it('can sync a directory' , async function () { - // let [err,res] = await to(remote.exec('cd /opt && pwd')) - // if (err) { - // log.info('error running command aborting test', err) - // return - // } - // log.info(`result of remote command ${res.command} => ${res.reply.toString().trim()}`) - expect('test', 'test failed').to.equal('/opt') - }) - -}) - -// function hooks(remote) { -// - -// // beforeEach(async() => { -// // await someasyncfunctiontodobeforeeachtest() -// // }) -// -// // after(async() => { -// // await someasyncfunctiontodoaftereeachtest() -// // }) -// -// } diff --git a/test/watcher.test.js.off b/test/watcher.test.js.off deleted file mode 100644 index 7b589c1..0000000 --- a/test/watcher.test.js.off +++ /dev/null @@ -1,49 +0,0 @@ -import events from 'events'; -import test from 'ava'; -import td from 'testdouble'; - -const chokidarStub = {}; - -td.replace('chokidar', chokidarStub); -const Fn = require('./watcher'); - -function setup(stubs = {}) { - chokidarStub.watch = stubs.watch || (() => new events.EventEmitter()); - return new Fn({source: '.'}); -} - -test('should emit "sync" if file was added/changed/deleted', t => { - t.plan(3); - const fn = setup(); - fn.start(); - fn.getEventEmitter().on('sync', () => t.pass('file change detected')); - fn.watcher.emit('add', 'file.txt'); - fn.watcher.emit('change', 'file.txt'); - fn.watcher.emit('unlink', 'file.txt'); -}); - -test('should emit "install" if "package.json" or "yarn.lock" was added/changed/deleted', t => { - t.plan(6); - const fn = setup(); - fn.start(); - fn.getEventEmitter().on('install', () => t.pass('file change detected')); - fn.watcher.emit('add', 'package.json'); - fn.watcher.emit('change', 'package.json'); - fn.watcher.emit('unlink', 'package.json'); - fn.watcher.emit('add', 'yarn.lock'); - fn.watcher.emit('change', 'yarn.lock'); - fn.watcher.emit('unlink', 'yarn.lock'); -}); - -test('should emit "sync" if "package.json" or "yarn.lock" was added/changed/deleted', t => { - t.plan(6); - const fn = setup(); - fn.start(); - fn.getEventEmitter().on('sync', () => t.pass('file change detected')); - fn.watcher.emit('add', 'package.json'); - fn.watcher.emit('change', 'package.json'); - fn.watcher.emit('unlink', 'package.json'); - fn.watcher.emit('add', 'yarn.lock'); - fn.watcher.emit('change', 'yarn.lock'); - fn.watcher.emit('unlink', 'yarn.lock'); -});