Compare commits

...

23 Commits

Author SHA1 Message Date
Tyler Stewart
580b8d6eae Merge pull request #12 from stewarttylerr/fix-compatability-bugs
Fix compatability bugs
2017-05-05 18:29:00 -06:00
Tyler Stewart
a75d63df92 fix(fs): resolve paths correctly
Should solve win32 issues

fix-compatability-bugs

fix-compatability-bugs
2017-05-05 18:08:47 -06:00
Tyler Stewart
301ae110e8 test: update tests and directory structure 2017-05-05 18:08:30 -06:00
Tyler Stewart
4d69b48466 chore: update mocha reporter 2017-05-05 18:08:30 -06:00
Tyler Stewart
ec010697bb feat(commands): remove minimist command parser for basic parsing
Tokenizes command string based on spaces, concats remaining arguments

This allows filesystem commands to handle directories/files with spaces.

fix-compatability-bugs
2017-05-05 18:08:30 -06:00
Tyler Stewart
cf3d543f1a fix(commands): correctly clone command for log 2017-05-05 18:04:54 -06:00
Tyler Stewart
69bec2b01c fix(fs): normalize fs paths
- Attempting to fix compatability on windows
2017-05-04 17:43:46 -06:00
Tyler Stewart
2eac41d127 test: update test setup
master
2017-05-04 17:43:41 -06:00
Tyler Stewart
eb32f93fc6 feat: close server on SIGINT (ctrl+c) 2017-05-04 17:42:47 -06:00
Tyler Stewart
095423606e fix(find-port): stop check at 65535 2017-05-04 17:42:10 -06:00
Tyler Stewart
61cf1bda39 feat(connection): add helper get for socket remote address 2017-05-04 17:40:37 -06:00
Tyler Stewart
75f847ed5d feat(commands): obfuscate password from logs 2017-05-04 17:40:01 -06:00
Tyler Stewart
ad4b32fc13 chore: add .env to github for tests 2017-05-04 17:39:15 -06:00
Tyler Stewart
be3c57bed0 Merge pull request #10 from stewarttylerr/sandbotorg-master
Sandbotorg master
2017-04-27 13:27:46 -06:00
Tyler Stewart
dc7dd1075c test(passive): merge master 2017-04-27 13:22:57 -06:00
salper
543e6cc1cc chore(readme): fix grammar 2017-04-27 13:22:39 -06:00
salper
5c1f8f7a65 fix: plug QUIT command
master
2017-04-27 13:19:29 -06:00
Tyler Stewart
557995a1a9 test(travis): add env variables 2017-04-27 13:17:01 -06:00
Tyler Stewart
45eca5afe0 test(passive): fix test 2017-04-27 12:49:55 -06:00
Tyler Stewart
695e594d97 Merge pull request #6 from stewarttylerr/migate-to-moment
feat: migrate to moment from date-fns, fix ls format
2017-03-31 17:14:44 -06:00
Tyler Stewart
97b55fc92c feat: migrate to moment from date-fns, fix ls format
Date-fns is great, but too early for use
2017-03-31 17:12:57 -06:00
Tyler Stewart
577066850b fix: improve getting current directory 2017-03-30 12:26:04 -06:00
Tyler Stewart
0ec989cf1e docs: update login event for new root option 2017-03-29 10:20:22 -06:00
71 changed files with 402 additions and 247 deletions

2
.env Executable file
View File

@@ -0,0 +1,2 @@
FTP_URL=ftp://127.0.0.1:8880
PASV_RANGE=8881

1
.gitignore vendored
View File

@@ -2,5 +2,4 @@ node_modules/
dist/
reports/
.env
npm-debug.log

View File

@@ -2,6 +2,10 @@ language: node_js
node_js:
- "6"
env:
FTP_URL: ftp://127.0.0.1:8880
PASV_RANGE: 8881
install: npm install
script:

View File

@@ -89,10 +89,13 @@ ftpServer.listen()
- __password__
- Password provided in the `PASS` command
- Only provided if `anonymous` is set to `false`
- __resolve ({fs, cwd, blacklist, whitelist})__
- __resolve ({fs, root, cwd, blacklist, whitelist})__
- __fs__ _[optional]_
- Optional file system class for connection to use
- See [File System](#file-system) for implementation details
- __root__ _[optional]_
- If `fs` not provided, will set the root directory for the connection
- The user cannot traverse lower than this directory
- __cwd__ _[optional]_
- If `fs` not provided, will set the starting directory for the connection
- __blacklist__ _[optional]_
@@ -129,7 +132,7 @@ Returns new directory relative to cwd
> Used in `CWD`, `CDUP`
`mkdir(path)`
Return a path to a newly created directory
Returns a path to a newly created directory
> Used in `MKD`
@@ -161,7 +164,7 @@ Modify a file or directory's permissions
> Used in `SITE CHMOD`
`getUniqueName()`
Return a unique file name to write to
Returns a unique file name to write to
> Used in `STOU`

View File

@@ -1,4 +1,3 @@
test/**/*.spec.js
--reporter list
--no-timeouts
--reporter mocha-pretty-bunyan-nyan
--ui bdd

View File

@@ -1,14 +1,14 @@
{
"global": {
"statements": 70,
"branches": 60,
"functions": 80,
"lines": 80
"statements": 90,
"branches": 80,
"functions": 90,
"lines": 90
},
"each": {
"statements": 0,
"branches": 0,
"functions": 0,
"lines": 0
"statements": 70,
"branches": 40,
"functions": 60,
"lines": 70
}
}

View File

@@ -47,9 +47,8 @@
},
"dependencies": {
"bunyan": "^1.8.9",
"date-fns": "^1.28.2",
"lodash": "^4.17.4",
"minimist-string": "^1.0.2",
"moment": "^2.18.1",
"uuid": "^3.0.1",
"when": "^3.7.8"
},
@@ -68,6 +67,7 @@
"husky": "0.13.1",
"istanbul": "0.4.5",
"mocha": "3.2.0",
"mocha-pretty-bunyan-nyan": "^1.0.4",
"npm-run-all": "4.0.1",
"rimraf": "2.5.4",
"semantic-release": "^6.3.2",

View File

@@ -11,9 +11,25 @@ class FtpCommands {
this.whitelist = _.get(this.connection, 'server.options.whitelist', []).map(cmd => _.upperCase(cmd));
}
parse(message) {
const [directive, ...args] = message.replace(/"/g, '').split(' ');
const command = {
directive: _.chain(directive).trim().toUpper().value(),
arg: _.compact(args).join(' ') || null,
raw: message
};
return command;
}
handle(command) {
const log = this.connection.log.child({command});
log.trace('Handle command');
if (typeof command === 'string') command = this.parse(command);
// Obfuscate password from logs
const logCommand = _.clone(command);
if (logCommand.directive === 'PASS') logCommand.arg = '********';
const log = this.connection.log.child({directive: command.directive});
log.trace({command: logCommand}, 'Handle command');
if (!REGISTRY.hasOwnProperty(command.directive)) {
return this.connection.reply(402, 'Command not allowed');

View File

@@ -3,7 +3,7 @@ const _ = require('lodash');
module.exports = {
directive: 'AUTH',
handler: function ({command} = {}) {
const method = _.upperCase(command._[1]);
const method = _.upperCase(command.arg);
switch (method) {
case 'TLS': return handleTLS.call(this);

View File

@@ -3,7 +3,7 @@ const cwd = require('./cwd').handler;
module.exports = {
directive: ['CDUP', 'XCUP'],
handler: function (args) {
args.command._ = [args.command._[0], '..'];
args.command.arg = '..';
return cwd.call(this, args);
},
syntax: '{{cmd}}',

View File

@@ -7,7 +7,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.chdir) return this.reply(402, 'Not supported by file system');
return when.try(this.fs.chdir.bind(this.fs), command._[1])
return when.try(this.fs.chdir.bind(this.fs), command.arg)
.then(cwd => {
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
return this.reply(250, path);

View File

@@ -6,7 +6,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.delete) return this.reply(402, 'Not supported by file system');
return when.try(this.fs.delete.bind(this.fs), command._[1])
return when.try(this.fs.delete.bind(this.fs), command.arg)
.then(() => {
return this.reply(250);
})

View File

@@ -4,7 +4,7 @@ module.exports = {
directive: 'HELP',
handler: function ({command} = {}) {
const registry = require('../registry');
const directive = _.upperCase(command._[1]);
const directive = _.upperCase(command.arg);
if (directive) {
if (!registry.hasOwnProperty(directive)) return this.reply(502, `Unknown command ${directive}.`);

View File

@@ -13,7 +13,7 @@ module.exports = {
const simple = command.directive === 'NLST';
let dataSocket;
const directory = command._[1] || '.';
const directory = command.arg || '.';
return this.connector.waitForConnection()
.then(socket => {
this.commandSocket.pause();

View File

@@ -1,5 +1,5 @@
const when = require('when');
const format = require('date-fns/format');
const moment = require('moment');
module.exports = {
directive: 'MDTM',
@@ -7,9 +7,9 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
return when.try(this.fs.get.bind(this.fs), command._[1])
return when.try(this.fs.get.bind(this.fs), command.arg)
.then(fileStat => {
const modificationTime = format(fileStat.mtime, 'YYYYMMDDHHmmss.SSS');
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS');
return this.reply(213, modificationTime);
})
.catch(err => {

View File

@@ -7,7 +7,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.mkdir) return this.reply(402, 'Not supported by file system');
return when.try(this.fs.mkdir.bind(this.fs), command._[1])
return when.try(this.fs.mkdir.bind(this.fs), command.arg)
.then(dir => {
const path = dir ? `"${escapePath(dir)}"` : undefined;
return this.reply(257, path);

View File

@@ -1,7 +1,7 @@
module.exports = {
directive: 'MODE',
handler: function ({command} = {}) {
return this.reply(/^S$/i.test(command._[1]) ? 200 : 504);
return this.reply(/^S$/i.test(command.arg) ? 200 : 504);
},
syntax: '{{cmd}} [mode]',
description: 'Sets the transfer mode (Stream, Block, or Compressed)',

View File

@@ -9,7 +9,7 @@ module.exports = {
// 332 : require account name (ACCT)
const password = command._[1];
const password = command.arg;
return this.login(this.username, password)
.then(() => {
return this.reply(230);

View File

@@ -5,7 +5,7 @@ module.exports = {
directive: 'PORT',
handler: function ({command} = {}) {
this.connector = new ActiveConnector(this);
const rawConnection = _.get(command, '_[1]', '').split(',');
const rawConnection = _.get(command, 'arg', '').split(',');
if (rawConnection.length !== 6) return this.reply(425);
const ip = rawConnection.slice(0, 4).join('.');

View File

@@ -12,7 +12,7 @@ module.exports = {
this.commandSocket.pause();
dataSocket = socket;
})
.then(() => when.try(this.fs.read.bind(this.fs), command._[1]))
.then(() => when.try(this.fs.read.bind(this.fs), command.arg))
.then(stream => {
return when.promise((resolve, reject) => {
dataSocket.on('error', err => stream.emit('error', err));

View File

@@ -6,7 +6,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
const fileName = command._[1];
const fileName = command.arg;
return when.try(this.fs.get.bind(this.fs), fileName)
.then(() => {
this.renameFrom = fileName;

View File

@@ -9,7 +9,7 @@ module.exports = {
if (!this.fs.rename) return this.reply(402, 'Not supported by file system');
const from = this.renameFrom;
const to = command._[1];
const to = command.arg;
return when.try(this.fs.rename.bind(this.fs), from, to)
.then(() => {

View File

@@ -4,7 +4,8 @@ module.exports = function ({log, command} = {}) {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.chmod) return this.reply(402, 'Not supported by file system');
const [, mode, fileName] = command._;
const [mode, ...fileNameParts] = command.arg.split(' ');
const fileName = fileNameParts.join(' ');
return when.try(this.fs.chmod.bind(this.fs), fileName, parseInt(mode, 8))
.then(() => {
return this.reply(200);

View File

@@ -1,21 +1,15 @@
const _ = require('lodash');
const when = require('when');
module.exports = {
directive: 'SITE',
handler: function ({log, command} = {}) {
const registry = require('./registry');
let [, subverb, ...subparameters] = command._;
subverb = _.upperCase(subverb);
const subLog = log.child({subverb});
const subCommand = this.commands.parse(command.arg);
const subLog = log.child({subverb: subCommand.directive});
if (!registry.hasOwnProperty(subverb)) return this.reply(502);
if (!registry.hasOwnProperty(subCommand.directive)) return this.reply(502);
const subCommand = {
_: [subverb, ...subparameters],
directive: subverb
};
const handler = registry[subverb].handler.bind(this);
const handler = registry[subCommand.directive].handler.bind(this);
return when.try(handler, { log: subLog, command: subCommand });
},
syntax: '{{cmd}} [subVerb] [subParams]',

View File

@@ -6,7 +6,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
return when.try(this.fs.get.bind(this.fs), command._[1])
return when.try(this.fs.get.bind(this.fs), command.arg)
.then(fileStat => {
return this.reply(213, {message: fileStat.size});
})

View File

@@ -6,7 +6,7 @@ module.exports = {
directive: 'STAT',
handler: function (args = {}) {
const {log, command} = args;
const path = _.get(command, '_[1]');
const path = _.get(command, 'arg');
if (path) {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get) return this.reply(402, 'Not supported by file system');

View File

@@ -7,7 +7,7 @@ module.exports = {
if (!this.fs.write) return this.reply(402, 'Not supported by file system');
const append = command.directive === 'APPE';
const fileName = command._[1];
const fileName = command.arg;
let dataSocket;
return this.connector.waitForConnection()

View File

@@ -8,14 +8,14 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get || !this.fs.getUniqueName) return this.reply(402, 'Not supported by file system');
const fileName = args.command._[1];
const fileName = args.command.arg;
return when.try(() => {
return when.try(this.fs.get.bind(this.fs), fileName)
.then(() => when.try(this.fs.getUniqueName.bind(this.fs)))
.catch(() => when.resolve(fileName));
})
.then(name => {
args.command._[1] = name;
args.command.arg = name;
return stor.call(this, args);
});
},

View File

@@ -1,7 +1,7 @@
module.exports = {
directive: 'STRU',
handler: function ({command} = {}) {
return this.reply(/^F$/i.test(command._[1]) ? 200 : 504);
return this.reply(/^F$/i.test(command.arg) ? 200 : 504);
},
syntax: '{{cmd}} [structure]',
description: 'Set file transfer structure',

View File

@@ -9,7 +9,7 @@ const ENCODING_TYPES = {
module.exports = {
directive: 'TYPE',
handler: function ({command} = {}) {
const encoding = _.upperCase(command._[1]);
const encoding = _.upperCase(command.arg);
if (!ENCODING_TYPES.hasOwnProperty(encoding)) return this.reply(501);
this.encoding = ENCODING_TYPES[encoding];

View File

@@ -3,7 +3,7 @@ module.exports = {
handler: function ({log, command} = {}) {
if (this.username) return this.reply(530, 'Username already set');
this.username = command._[1];
this.username = command.arg;
if (!this.username) return this.reply(501, 'Must send username requirement');
if (this.server.options.anonymous === true) {

View File

@@ -20,6 +20,7 @@ const commands = [
require('./registration/pasv'),
require('./registration/port'),
require('./registration/pwd'),
require('./registration/quit'),
require('./registration/retr'),
require('./registration/rmd'),
require('./registration/rnfr'),

View File

@@ -2,7 +2,6 @@ const _ = require('lodash');
const uuid = require('uuid');
const when = require('when');
const sequence = require('when/sequence');
const parseCommandString = require('minimist-string');
const BaseConnector = require('./connector/base');
const FileSystem = require('./fs');
@@ -15,7 +14,7 @@ class FtpConnection {
this.server = server;
this.commandSocket = options.socket;
this.id = uuid.v4();
this.log = options.log.child({id: this.id});
this.log = options.log.child({id: this.id, ip: this.ip});
this.commands = new Commands(this);
this.encoding = 'utf-8';
@@ -26,13 +25,7 @@ class FtpConnection {
});
this.commandSocket.on('data', data => {
const messages = _.compact(data.toString('utf-8').split('\r\n'));
const handleMessage = message => {
const command = parseCommandString(message);
command.directive = _.upperCase(command._[0]);
return this.commands.handle(command);
};
return sequence(messages.map(message => handleMessage.bind(this, message)));
return sequence(messages.map(message => this.commands.handle.bind(this.commands, message)));
});
this.commandSocket.on('timeout', () => {});
this.commandSocket.on('close', () => {
@@ -41,13 +34,19 @@ class FtpConnection {
});
}
get ip() {
try {
return this.commandSocket.remoteAddress;
} catch (ex) {
return null;
}
}
close(code = 421, message = 'Closing connection') {
return when(() => {
if (code) return this.reply(code, message);
})
.then(() => {
if (this.commandSocket) this.commandSocket.end();
});
return when
.resolve(code)
.then(_code => _code && this.reply(_code, message))
.then(() => this.commandSocket && this.commandSocket.end());
}
login(username, password) {
@@ -59,7 +58,7 @@ class FtpConnection {
return this.server.emit('login', {connection: this, username, password});
}
})
.then(({root = '/', cwd = '/', fs, blacklist = [], whitelist = []} = {}) => {
.then(({root, cwd, fs, blacklist = [], whitelist = []} = {}) => {
this.authenticated = true;
this.commands.blacklist = _.concat(this.commands.blacklist, blacklist);
this.commands.whitelist = _.concat(this.commands.whitelist, whitelist);

View File

@@ -8,22 +8,22 @@ const fs = whenNode.liftAll(syncFs);
const errors = require('./errors');
class FileSystem {
constructor(connection, {
root = '/',
cwd = '/'
} = {}) {
constructor(connection, { root, cwd } = {}) {
this.connection = connection;
this.cwd = cwd;
this.root = root;
this.cwd = cwd || nodePath.sep;
this.root = root || process.cwd();
}
_resolvePath(path) {
const pathParts = {
root: this.root,
base: nodePath.resolve(this.cwd, path)
_resolvePath(path = '') {
const isFromRoot = _.startsWith(path, '/') || _.startsWith(path, nodePath.sep);
const cwd = isFromRoot ? nodePath.sep : this.cwd || nodePath.sep;
const serverPath = nodePath.join(nodePath.sep, cwd, path);
const fsPath = nodePath.join(this.root, serverPath);
return {
serverPath,
fsPath
};
path = nodePath.format(pathParts);
return path;
}
currentDirectory() {
@@ -31,17 +31,17 @@ class FileSystem {
}
get(fileName) {
const path = this._resolvePath(fileName);
return fs.stat(path)
const {fsPath} = this._resolvePath(fileName);
return fs.stat(fsPath)
.then(stat => _.set(stat, 'name', fileName));
}
list(path = '.') {
path = this._resolvePath(path);
return fs.readdir(path)
const {fsPath} = this._resolvePath(path);
return fs.readdir(fsPath)
.then(fileNames => {
return when.map(fileNames, fileName => {
const filePath = nodePath.join(path, fileName);
const filePath = nodePath.join(fsPath, fileName);
return fs.access(filePath, syncFs.constants.F_OK)
.then(() => {
return fs.stat(filePath)
@@ -54,60 +54,60 @@ class FileSystem {
}
chdir(path = '.') {
path = this._resolvePath(path);
return fs.stat(path)
const {fsPath, serverPath} = this._resolvePath(path);
return fs.stat(fsPath)
.tap(stat => {
if (!stat.isDirectory()) throw new errors.FileSystemError('Not a valid directory');
})
.then(() => {
this.cwd = path.replace(new RegExp(`^${this.root}`), '') || '/';
this.cwd = serverPath;
return this.currentDirectory();
});
}
write(fileName, {append = false} = {}) {
const path = this._resolvePath(fileName);
const stream = syncFs.createWriteStream(path, {flags: !append ? 'w+' : 'a+'});
stream.on('error', () => fs.unlink(path));
const {fsPath} = this._resolvePath(fileName);
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+'});
stream.on('error', () => fs.unlink(fsPath));
return stream;
}
read(fileName) {
const path = this._resolvePath(fileName);
return fs.stat(path)
const {fsPath} = this._resolvePath(fileName);
return fs.stat(fsPath)
.tap(stat => {
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
})
.then(() => {
const stream = syncFs.createReadStream(path, {flags: 'r'});
const stream = syncFs.createReadStream(fsPath, {flags: 'r'});
return stream;
});
}
delete(path) {
path = this._resolvePath(path);
return fs.stat(path)
const {fsPath} = this._resolvePath(path);
return fs.stat(fsPath)
.then(stat => {
if (stat.isDirectory()) return fs.rmdir(path);
else return fs.unlink(path);
if (stat.isDirectory()) return fs.rmdir(fsPath);
else return fs.unlink(fsPath);
});
}
mkdir(path) {
path = this._resolvePath(path);
return fs.mkdir(path)
.then(() => path);
const {fsPath} = this._resolvePath(path);
return fs.mkdir(fsPath)
.then(() => fsPath);
}
rename(from, to) {
const fromPath = this._resolvePath(from);
const toPath = this._resolvePath(to);
const {fsPath: fromPath} = this._resolvePath(from);
const {fsPath: toPath} = this._resolvePath(to);
return fs.rename(fromPath, toPath);
}
chmod(path, mode) {
path = this._resolvePath(path);
return fs.chmod(path, mode);
const {fsPath} = this._resolvePath(path);
return fs.chmod(fsPath, mode);
}
getUniqueName() {

View File

@@ -1,5 +1,5 @@
const _ = require('lodash');
const dateFns = require('date-fns');
const moment = require('moment');
const errors = require('../errors');
module.exports = function (fileStat, format = 'ls') {
@@ -16,6 +16,10 @@ module.exports = function (fileStat, format = 'ls') {
};
function ls(fileStat) {
const now = moment.utc();
const mtime = moment.utc(new Date(fileStat.mtime));
const dateFormat = now.diff(mtime, 'months') < 6 ? 'MMM DD HH:mm' : 'MMM DD YYYY';
return [
fileStat.mode !== null
? [
@@ -35,7 +39,7 @@ function ls(fileStat) {
fileStat.uid,
fileStat.gid,
_.padStart(fileStat.size, 12),
_.padStart(dateFns.format(fileStat.mtime, 'MMM DD HH:mm'), 12),
_.padStart(mtime.format(dateFormat), 12),
fileStat.name
].join(' ');
}
@@ -44,7 +48,7 @@ function ep(fileStat) {
const facts = [
fileStat.dev && fileStat.ino ? `i${fileStat.dev.toString(16)}.${fileStat.ino.toString(16)}` : null,
fileStat.size ? `s${fileStat.size}` : null,
fileStat.mtime ? `m${dateFns.format(dateFns.parse(fileStat.mtime), 'X')}` : null,
fileStat.mtime ? `m${moment.utc(new Date(fileStat.mtime)).format('X')}` : null,
fileStat.mode ? `up${fileStat.mode.toString(8).substr(fileStat.mode.toString(8).length - 3)}` : null,
fileStat.isDirectory() ? 'r' : '/'
].join(',');

View File

@@ -2,15 +2,15 @@ const net = require('net');
const when = require('when');
const errors = require('../errors');
module.exports = function (min = 22, max = undefined) {
module.exports = function (min = 1, max = undefined) {
return when.promise((resolve, reject) => {
let port = min;
let checkPort = min;
let portCheckServer = net.createServer();
portCheckServer.maxConnections = 0;
portCheckServer.on('error', () => {
if (!max || port < max) {
port = port + 1;
portCheckServer.listen(port);
if (checkPort < 65535 && (!max || checkPort < max)) {
checkPort = checkPort + 1;
portCheckServer.listen(checkPort);
} else {
reject(new errors.GeneralError('Unable to find open port', 500));
}
@@ -22,6 +22,6 @@ module.exports = function (min = 22, max = undefined) {
resolve(port);
});
});
portCheckServer.listen(port);
portCheckServer.listen(checkPort);
});
};

View File

@@ -37,6 +37,8 @@ class FtpServer {
});
this.on = this.server.on.bind(this.server);
this.listeners = this.server.listeners.bind(this.server);
process.on('SIGINT', () => this.close());
}
listen() {

View File

@@ -0,0 +1,95 @@
const {expect} = require('chai');
const when = require('when');
const bunyan = require('bunyan');
const sinon = require('sinon');
const FtpCommands = require('../../src/commands');
describe('FtpCommands', function () {
let sandbox;
let commands;
let mockConnection = {
authenticated: false,
log: bunyan.createLogger({name: 'FtpCommands'}),
reply: () => when.resolve({}),
server: {
options: {
blacklist: ['allo']
}
}
};
beforeEach(() => {
sandbox = sinon.sandbox.create();
commands = new FtpCommands(mockConnection);
sandbox.spy(mockConnection, 'reply');
sandbox.spy(commands, 'handle');
sandbox.spy(commands, 'parse');
});
afterEach(() => {
sandbox.restore();
});
describe('parse', function () {
it('no args: test', () => {
const cmd = commands.parse('test');
expect(cmd.directive).to.equal('TEST');
expect(cmd.arg).to.equal(null);
expect(cmd.raw).to.equal('test');
});
it('one arg: test arg', () => {
const cmd = commands.parse('test arg');
expect(cmd.directive).to.equal('TEST');
expect(cmd.arg).to.equal('arg');
expect(cmd.raw).to.equal('test arg');
});
it('two args: test arg1 arg2', () => {
const cmd = commands.parse('test arg1 arg2');
expect(cmd.directive).to.equal('TEST');
expect(cmd.arg).to.equal('arg1 arg2');
expect(cmd.raw).to.equal('test arg1 arg2');
});
});
describe('handle', function () {
it('fails with unsupported command', () => {
return commands.handle('bad')
.then(() => {
expect(mockConnection.reply.callCount).to.equal(1);
expect(mockConnection.reply.args[0][0]).to.equal(402);
});
});
it('fails with blacklisted command', () => {
return commands.handle('allo')
.then(() => {
expect(mockConnection.reply.callCount).to.equal(1);
expect(mockConnection.reply.args[0][0]).to.equal(502);
expect(mockConnection.reply.args[0][1]).to.match(/blacklisted/);
});
});
it('fails with non whitelisted command', () => {
commands.whitelist.push('USER');
return commands.handle('auth')
.then(() => {
expect(mockConnection.reply.callCount).to.equal(1);
expect(mockConnection.reply.args[0][0]).to.equal(502);
expect(mockConnection.reply.args[0][1]).to.match(/whitelisted/);
});
});
it('fails due to being unauthenticated', () => {
return commands.handle('stor')
.then(() => {
expect(mockConnection.reply.callCount).to.equal(1);
expect(mockConnection.reply.args[0][0]).to.equal(530);
expect(mockConnection.reply.args[0][1]).to.match(/authentication/);
});
});
});
});

View File

@@ -12,7 +12,7 @@ describe(CMD, function () {
end: () => when.resolve()
}
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -20,7 +20,7 @@ describe(CMD, function () {
});
it('TLS // not supported', done => {
cmdFn({command: {_: [CMD, 'TLS'], directive: CMD}})
cmdFn({command: { arg: 'TLS', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
@@ -29,7 +29,7 @@ describe(CMD, function () {
});
it('SSL // not supported', done => {
cmdFn({command: {_: [CMD, 'SSL'], directive: CMD}})
cmdFn({command: { arg: 'SSL', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
@@ -38,7 +38,7 @@ describe(CMD, function () {
});
it('bad // bad', done => {
cmdFn({command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
chdir: () => when.resolve()
}
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -26,7 +26,7 @@ describe(CMD, function () {
});
it('.. // successful', done => {
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.chdir.args[0][0]).to.equal('..');

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
reply: () => {},
fs: { chdir: () => {} }
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('fails on no fs chdir command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('test // successful', done => {
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: { arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
@@ -61,7 +61,7 @@ describe(CMD, function () {
it('test // successful', done => {
mockClient.fs.chdir.restore();
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: { arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
@@ -74,7 +74,7 @@ describe(CMD, function () {
mockClient.fs.chdir.restore();
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
expect(mockClient.fs.chdir.args[0][0]).to.equal('bad');

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
reply: () => {},
fs: { delete: () => {} }
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('fails on no fs delete command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('test // successful', done => {
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: { arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.delete.args[0][0]).to.equal('test');
@@ -62,7 +62,7 @@ describe(CMD, function () {
mockClient.fs.delete.restore();
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
expect(mockClient.fs.delete.args[0][0]).to.equal('bad');

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -20,7 +20,7 @@ describe(CMD, function () {
});
it('// successful', done => {
cmdFn({command: {_: [CMD], directive: CMD}})
cmdFn({command: { directive: CMD }})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(211);
done();
@@ -29,7 +29,7 @@ describe(CMD, function () {
});
it('help // successful', done => {
cmdFn({command: {_: [CMD, 'help'], directive: CMD}})
cmdFn({command: { arg: 'help', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(214);
done();
@@ -38,7 +38,7 @@ describe(CMD, function () {
});
it('help // successful', done => {
cmdFn({command: {_: [CMD, 'allo'], directive: CMD}})
cmdFn({command: { arg: 'allo', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(214);
done();
@@ -47,7 +47,7 @@ describe(CMD, function () {
});
it('bad // unsuccessful', done => {
cmdFn({command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(502);
done();

View File

@@ -19,7 +19,7 @@ describe(CMD, function () {
pause: () => {}
}
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -51,7 +51,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -63,7 +63,7 @@ describe(CMD, function () {
it('fails on no fs list command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -75,7 +75,7 @@ describe(CMD, function () {
});
it('. // successful', done => {
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1][1]).to.have.property('raw');
@@ -91,7 +91,7 @@ describe(CMD, function () {
mockClient.fs.list.restore();
sandbox.stub(mockClient.fs, 'list').rejects(new Error());
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(451);
done();
@@ -102,7 +102,7 @@ describe(CMD, function () {
it('. // unsuccessful (timeout)', done => {
sandbox.stub(mockClient.connector, 'waitForConnection').returns(when.reject(new when.TimeoutError()));
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
done();

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
reply: () => {},
fs: { get: () => {} }
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('fails on no fs get command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('. // successful', done => {
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(213);
//expect(mockClient.reply.args[0][1]).to.equal('20111010172411.000');
@@ -62,7 +62,7 @@ describe(CMD, function () {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects(new Error());
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
reply: () => {},
fs: { mkdir: () => {} }
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('fails on no fs mkdir command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('test // successful', done => {
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: {arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(257);
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
@@ -61,7 +61,7 @@ describe(CMD, function () {
it('test // successful', done => {
mockClient.fs.mkdir.restore();
sandbox.stub(mockClient.fs, 'mkdir').resolves('test');
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: {arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(257);
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
@@ -74,7 +74,7 @@ describe(CMD, function () {
mockClient.fs.mkdir.restore();
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'));
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
expect(mockClient.fs.mkdir.args[0][0]).to.equal('bad');

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -20,7 +20,7 @@ describe(CMD, function () {
});
it('S // successful', done => {
cmdFn({command: {_: [CMD, 'S']}})
cmdFn({command: {arg: 'S'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
@@ -29,7 +29,7 @@ describe(CMD, function () {
});
it('Q // unsuccessful', done => {
cmdFn({command: {_: [CMD, 'Q']}})
cmdFn({command: {arg: 'Q'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();

View File

@@ -19,7 +19,7 @@ describe(CMD, function () {
pause: () => {}
}
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('. // successful', done => {
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1][1]).to.have.property('raw');

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -12,7 +12,7 @@ describe(CMD, function () {
server: { options: { anonymous: false } },
username: 'user'
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
});
it('pass // successful', done => {
cmdFn({log, command: {_: [CMD, 'pass'], directive: CMD}})
cmdFn({log, command: {arg: 'pass', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.args[0]).to.eql(['user', 'pass']);
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('// successful (anonymous)', done => {
mockClient.server.options.anonymous = true;
mockClient.authenticated = true;
cmdFn({log, command: {_: [CMD], directive: CMD}})
cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.callCount).to.equal(0);
@@ -52,7 +52,7 @@ describe(CMD, function () {
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects('bad');
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
done();
@@ -64,7 +64,7 @@ describe(CMD, function () {
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects({});
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
done();
@@ -74,7 +74,7 @@ describe(CMD, function () {
it('bad // unsuccessful', done => {
delete mockClient.username;
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(503);
done();

View File

@@ -2,7 +2,7 @@ const when = require('when');
const {expect} = require('chai');
const sinon = require('sinon');
const ActiveConnector = require('../../src/connector/active');
const ActiveConnector = require('../../../src/connector/active');
const CMD = 'PORT';
describe(CMD, function () {
@@ -10,7 +10,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -32,7 +32,7 @@ describe(CMD, function () {
});
it('// unsuccessful | invalid argument', done => {
cmdFn({ command: { _: [CMD, '1,2,3,4,5'] } })
cmdFn({ command: { arg: '1,2,3,4,5' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
done();
@@ -41,7 +41,7 @@ describe(CMD, function () {
});
it('// successful', done => {
cmdFn({ command: { _: [CMD, '192,168,0,100,137,214'] } })
cmdFn({ command: { arg: '192,168,0,100,137,214' } })
.then(() => {
const [ip, port] = ActiveConnector.prototype.setupConnection.args[0];
expect(mockClient.reply.args[0][0]).to.equal(200);

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
reply: () => {},
fs: { currentDirectory: () => {} }
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -25,7 +25,7 @@ describe(CMD, function () {
describe('// check', function () {
it('fails on no fs', done => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -37,7 +37,7 @@ describe(CMD, function () {
it('fails on no fs currentDirectory command', done => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
.then(() => {
@@ -49,7 +49,7 @@ describe(CMD, function () {
});
it('// successful', done => {
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: { arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(257);
done();
@@ -61,7 +61,7 @@ describe(CMD, function () {
mockClient.fs.currentDirectory.restore();
sandbox.stub(mockClient.fs, 'currentDirectory').resolves('/test');
cmdFn({log, command: {_: [CMD, 'test'], directive: CMD}})
cmdFn({log, command: {arg: 'test', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(257);
done();
@@ -73,7 +73,7 @@ describe(CMD, function () {
mockClient.fs.currentDirectory.restore();
sandbox.stub(mockClient.fs, 'currentDirectory').rejects(new Error('Bad'));
cmdFn({log, command: {_: [CMD, 'bad'], directive: CMD}})
cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
const mockClient = {
close: () => {}
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
let sandbox;
const mockLog = { error: () => {} };
const mockClient = { reply: () => when.resolve() };
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -50,7 +50,7 @@ describe(CMD, function () {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, 'test'] } })
cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
@@ -59,7 +59,7 @@ describe(CMD, function () {
});
it('test // successful', done => {
cmdFn({ log: mockLog, command: { _: [CMD, 'test'] } })
cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.fs.get.args[0][0]).to.equal('test');
expect(mockClient.reply.args[0][0]).to.equal(350);

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
let sandbox;
const mockLog = { error: () => {} };
const mockClient = { reply: () => when.resolve() };
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -62,7 +62,7 @@ describe(CMD, function () {
mockClient.fs.rename.restore();
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, 'new'] } })
cmdFn({ log: mockLog, command: { arg: 'new' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
@@ -71,7 +71,7 @@ describe(CMD, function () {
});
it('new // successful', done => {
cmdFn({ command: { _: [CMD, 'new'] } })
cmdFn({ command: { arg: 'new' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new']);

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
let sandbox;
const mockLog = { error: () => {} };
const mockClient = { reply: () => when.resolve() };
const cmdFn = require(`../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient);
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -49,7 +49,7 @@ describe(CMD, function () {
mockClient.fs.chmod.restore();
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, '777', 'test'] } })
cmdFn({ log: mockLog, command: { arg: '777 test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(500);
done();
@@ -58,7 +58,7 @@ describe(CMD, function () {
});
it('777 test // successful', done => {
cmdFn({ log: mockLog, command: { _: [CMD, '777', 'test'] } })
cmdFn({ log: mockLog, command: { arg: '777 test' } })
.then(() => {
expect(mockClient.fs.chmod.args[0]).to.eql(['test', 511]);
expect(mockClient.reply.args[0][0]).to.equal(200);

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
let sandbox;
const mockLog = { error: () => {} };
const mockClient = { reply: () => when.resolve() };
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -47,7 +47,7 @@ describe(CMD, function () {
it('// unsuccessful | file get fails', done => {
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, 'test'] } })
cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
@@ -56,7 +56,7 @@ describe(CMD, function () {
});
it('// successful', done => {
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(213);
done();

View File

@@ -7,7 +7,7 @@ describe(CMD, function () {
let sandbox;
const mockLog = { error: () => {} };
const mockClient = { reply: () => when.resolve() };
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -35,7 +35,7 @@ describe(CMD, function () {
it('// unsuccessful | no file system', done => {
delete mockClient.fs;
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
@@ -46,7 +46,7 @@ describe(CMD, function () {
it('// unsuccessful | file system does not have functions', done => {
mockClient.fs = {};
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
@@ -57,7 +57,7 @@ describe(CMD, function () {
it('// unsuccessful | file get fails', done => {
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, 'test'] } })
cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(450);
done();
@@ -85,7 +85,7 @@ describe(CMD, function () {
isDirectory: () => false
});
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(212);
done();
@@ -132,7 +132,7 @@ describe(CMD, function () {
isDirectory: () => true
});
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(213);
done();

View File

@@ -2,7 +2,7 @@ const when = require('when');
const {expect} = require('chai');
const sinon = require('sinon');
const stor = require('../../src/commands/registration/stor');
const stor = require('../../../src/commands/registration/stor');
const CMD = 'STOU';
describe(CMD, function () {
@@ -10,7 +10,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -56,12 +56,12 @@ describe(CMD, function () {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects({});
cmdFn({ command: { _: [CMD, 'good'] } })
cmdFn({ command: { arg: 'good' } })
.then(() => {
const call = stor.handler.call.args[0][1];
expect(call).to.have.property('command');
expect(call.command).to.have.property('_');
expect(call.command._).to.eql([CMD, 'good']);
expect(call.command).to.have.property('arg');
expect(call.command.arg).to.eql('good');
expect(mockClient.fs.getUniqueName.callCount).to.equal(0);
done();
})
@@ -69,12 +69,12 @@ describe(CMD, function () {
});
it('// successful | generates unique name', done => {
cmdFn({ command: { _: [CMD, 'bad'] } })
cmdFn({ command: { arg: 'bad' } })
.then(() => {
const call = stor.handler.call.args[0][1];
expect(call).to.have.property('command');
expect(call.command).to.have.property('_');
expect(call.command._).to.eql([CMD, '4']);
expect(call.command).to.have.property('arg');
expect(call.command.arg).to.eql('4');
expect(mockClient.fs.getUniqueName.callCount).to.equal(1);
done();
})

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -20,7 +20,7 @@ describe(CMD, function () {
});
it('// successful', done => {
cmdFn({command: { _: [CMD, 'F'] } })
cmdFn({command: { arg: 'F' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
@@ -29,7 +29,7 @@ describe(CMD, function () {
});
it('// unsuccessful', done => {
cmdFn({command: { _: [CMD, 'X'] } })
cmdFn({command: { arg: 'X' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();

View File

@@ -8,7 +8,7 @@ describe(CMD, function () {
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -21,7 +21,7 @@ describe(CMD, function () {
});
it('A // successful', done => {
cmdFn({ command: { _: [CMD, 'A'] } })
cmdFn({ command: { arg: 'A' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('utf-8');
@@ -31,7 +31,7 @@ describe(CMD, function () {
});
it('I // successful', done => {
cmdFn({ command: { _: [CMD, 'I'] } })
cmdFn({ command: { arg: 'I' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('binary');
@@ -41,7 +41,7 @@ describe(CMD, function () {
});
it('L // successful', done => {
cmdFn({ command: { _: [CMD, 'L'] } })
cmdFn({ command: { arg: 'L' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('binary');
@@ -51,7 +51,7 @@ describe(CMD, function () {
});
it('X // successful', done => {
cmdFn({ command: { _: [CMD, 'X'] } })
cmdFn({ command: { arg: 'X' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
expect(mockClient.encoding).to.equal(null);

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
server: { options: {} },
login: () => when.resolve()
};
const cmdFn = require(`../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
@@ -29,7 +29,7 @@ describe(CMD, function () {
});
it('test // successful | prompt for password', done => {
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(331);
done();
@@ -40,7 +40,7 @@ describe(CMD, function () {
it('test // successful | anonymous login', done => {
mockClient.server.options = {anonymous: true};
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.callCount).to.equal(1);
@@ -50,7 +50,7 @@ describe(CMD, function () {
});
it('test // unsuccessful | no username provided', done => {
cmdFn({ command: { _: [CMD] } })
cmdFn({ command: { } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
expect(mockClient.login.callCount).to.equal(0);
@@ -62,7 +62,7 @@ describe(CMD, function () {
it('test // unsuccessful | already set username', done => {
mockClient.username = 'test';
cmdFn({ command: { _: [CMD, 'test'] } })
cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
expect(mockClient.login.callCount).to.equal(0);
@@ -77,7 +77,7 @@ describe(CMD, function () {
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { _: [CMD, 'test'] } })
cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
expect(mockClient.login.callCount).to.equal(1);

View File

@@ -27,6 +27,7 @@ describe('Connector - Passive //', function () {
sandbox = sinon.sandbox.create();
sandbox.spy(mockConnection, 'reply');
sandbox.spy(mockConnection, 'close');
mockConnection.commandSocket.remoteAddress = '::ffff:127.0.0.1';
mockConnection.server.options.pasv_range = '8000';
@@ -45,7 +46,7 @@ describe('Connector - Passive //', function () {
});
it('has invalid pasv range', function (done) {
delete mockConnection.server.options.pasv_range;
mockConnection.server.options.pasv_range = -1;
passive.setupServer()
.then(() => done('should not happen'))
@@ -84,10 +85,13 @@ describe('Connector - Passive //', function () {
expect(passive.dataServer).to.exist;
const {port} = passive.dataServer.address();
net.createConnection(port, () => {
expect(mockConnection.reply.callCount).to.equal(1);
expect(mockConnection.reply.args[0][0]).to.equal(550);
done();
net.createConnection(port);
passive.dataServer.once('connection', () => {
setTimeout(() => {
expect(passive.connection.reply.callCount).to.equal(1);
expect(passive.connection.reply.args[0][0]).to.equal(550);
done();
}, 100);
});
})
.catch(done);

View File

@@ -1,5 +1,4 @@
const {expect} = require('chai');
const dateFns = require('date-fns');
const fileStat = require('../../src/helpers/file-stat');
const errors = require('../../src/errors');
@@ -17,24 +16,48 @@ describe('helpers // file-stat', function () {
size: 527,
blksize: 4096,
blocks: 8,
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
atime: 'Mon, 10 Oct 2017 23:24:11 GMT',
mtime: 'Mon, 10 Oct 2017 23:24:11 GMT',
ctime: 'Mon, 10 Oct 2017 23:24:11 GMT',
birthtime: 'Mon, 10 Oct 2017 23:24:11 GMT',
isDirectory: () => false
};
describe.skip('format - ls //', function () {
const STAT_OLD = {
name: 'test2',
dev: 2114,
ino: 48064969,
mode: 33188,
nlink: 1,
uid: 84,
gid: 101,
rdev: 0,
size: 530,
blksize: 4096,
blocks: 8,
atime: 'Mon, 10 Oct 2011 14:05:12 GMT',
mtime: 'Mon, 10 Oct 2011 14:05:12 GMT',
ctime: 'Mon, 10 Oct 2011 14:05:12 GMT',
birthtime: 'Mon, 10 Oct 2011 14:05:12 GMT',
isDirectory: () => false
};
describe('format - ls //', function () {
it('formats correctly', () => {
const format = fileStat(STAT, 'ls');
expect(format).to.equal('-rwxrw-r-- 1 85 100 527 Oct 10 17:24 test1');
expect(format).to.equal('-rwxrw-r-- 1 85 100 527 Oct 10 23:24 test1');
});
it('formats correctly for files over 6 months old', () => {
const format = fileStat(STAT_OLD, 'ls');
expect(format).to.equal('-rwxrw-r-- 1 84 101 530 Oct 10 2011 test2');
});
});
describe.skip('format - ep //', function () {
describe('format - ep //', function () {
it('formats correctly', () => {
const format = fileStat(STAT, 'ep');
expect(format).to.equal('+i842.2dd69c9,s527,m1318289051,up644,/ test1');
expect(format).to.equal('+i842.2dd69c9,s527,m1507677851,up644,/ test1');
});
});

View File

@@ -1,5 +1,4 @@
/* eslint no-unused-expressions: 0 */
require('dotenv').load();
const {expect} = require('chai');
const bunyan = require('bunyan');
const fs = require('fs');
@@ -7,9 +6,11 @@ const fs = require('fs');
const FtpServer = require('../src');
const FtpClient = require('ftp');
before(() => require('dotenv').load());
describe('FtpServer', function () {
this.timeout(2000);
let log = bunyan.createLogger({name: 'test', level: 10});
let log = bunyan.createLogger({name: 'test'});
let server;
let client;
@@ -21,14 +22,9 @@ describe('FtpServer', function () {
server.on('login', (data, resolve) => {
resolve({root: process.cwd()});
});
process.on('SIGINT', function () {
server.close();
});
require('child_process').exec(`sudo kill $(sudo lsof -t -i:${server.url.port})`, () => {
server.listen()
.finally(() => done());
});
server.listen()
.then(() => done());
});
after(() => {
server.close();
@@ -38,6 +34,7 @@ describe('FtpServer', function () {
expect(server).to.exist;
client = new FtpClient();
client.once('ready', () => done());
client.once('error', err => done(err));
client.connect({
host: server.url.hostname,
port: server.url.port,
@@ -235,4 +232,10 @@ describe('FtpServer', function () {
});
});
it('QUIT', done => {
client.once('close', done);
client.logout(err => {
expect(err).to.be.undefined;
});
});
});

5
test/mochabunyan.opts Normal file
View File

@@ -0,0 +1,5 @@
{
"mute":false,
"level":"fatal",
"reporter":"spec"
}

View File

@@ -4,12 +4,13 @@ const bunyan = require('bunyan');
const FtpServer = require('../src');
const log = bunyan.createLogger({name: 'test'});
log.level(process.env.LOG_LEVEL || 'trace');
const server = new FtpServer(process.env.FTP_URL, {
log.level('info');
const server = new FtpServer('ftp://127.0.0.1:8880', {
log,
pasv_range: process.env.PASV_RANGE
pasv_range: 8881
});
server.on('login', ({username, password}, resolve, reject) => {
console.log(username, password);
if (username === 'test' && password === 'test') resolve({ root: require('os').homedir() });
else reject('Bad username or password');
});