Compare commits

...

21 Commits

Author SHA1 Message Date
Tyler Stewart
c8526be1f4 Merge pull request #30 from trs/fix-utf8
Fix utf8
2017-06-26 19:54:51 -06:00
Tyler Stewart
e0b11ff480 fix: cleanup server 2017-06-26 16:39:00 -06:00
Tyler Stewart
58b9d8db9d chore: update fs readme 2017-06-26 16:39:00 -06:00
Tyler Stewart
fa121ba0fd test(REST): add tests 2017-06-26 16:39:00 -06:00
Tyler Stewart
2e02dc20ad feat(REST): add support for REST command
Allows the client to resume a transfer at the specified bytes
2017-06-26 16:38:59 -06:00
Tyler Stewart
8aeb6976d2 fix(auth): update checks, ensure secure is set using ftps 2017-06-26 16:38:59 -06:00
Tyler Stewart
84a68ae03c chore: dont append branch name to commit 2017-06-26 12:34:21 -06:00
Tyler Stewart
9dfc80b99d test(OPTS): update tests
master
2017-06-26 11:19:42 -06:00
Tyler Stewart
090e3d8105 chore: update log levels 2017-06-26 11:13:55 -06:00
Tyler Stewart
c3b0dbf5b0 feat(OPTS): add opts command handler for utf8
master
2017-06-26 11:13:49 -06:00
Tyler Stewart
69a5133936 fix(FEAT): correctly display feature list 2017-06-26 11:13:00 -06:00
Tyler Stewart
5394908a6b Merge pull request #29 from trs/fix-tls-check
Fix encoding/transfer type
2017-06-20 17:20:23 -06:00
Tyler Stewart
3e7bd5bcf9 chore: update licence format
Still MIT, just updating to ensure GitHub recognizes it
2017-06-20 17:09:49 -06:00
Tyler Stewart
175b422c5f chore: update dependencies 2017-06-20 17:02:05 -06:00
Tyler Stewart
b2a9851204 fix: ensure utf8 support; allows accent characters 2017-06-20 16:56:26 -06:00
Tyler Stewart
977dd1579a fix(TYPE): correctly set types, only use for data connections 2017-06-20 16:55:37 -06:00
Tyler Stewart
176b2b7ca8 fix: actually check _tls 2017-06-20 14:47:44 -06:00
Tyler Stewart
63777c0d74 Merge pull request #26 from trs/test-updates
Test updates
2017-06-16 15:17:31 -06:00
Tyler Stewart
9be8ffa60d test: add and update tests 2017-06-09 15:00:01 -06:00
Tyler Stewart
b8cd6022e1 fix: assign tls options to empty object 2017-06-09 14:40:19 -06:00
Tyler Stewart
0618a3c675 fix(passive): throw error on invalid port range 2017-06-09 14:39:21 -06:00
58 changed files with 843 additions and 659 deletions

22
LICENSE
View File

@@ -1,9 +1,21 @@
ftp-srv Copyright (c) 2017 Tyler Stewart
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Copyright (c) 2017 Tyler Stewart
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -192,13 +192,17 @@ __Used in:__ `CWD`, `CDUP`
Returns a path to a newly created directory
__Used in:__ `MKD`
#### [`write(fileName, {append = false})`](src/fs.js#L68)
#### [`write(fileName, {append, start})`](src/fs.js#L68)
Returns a writable stream
Options: `append` if true, append to existing file
Options:
`append` if true, append to existing file
`start` if set, specifies the byte offset to write to
__Used in:__ `STOR`, `APPE`
#### [`read(fileName)`](src/fs.js#L75)
#### [`read(fileName, {start})`](src/fs.js#L75)
Returns a readable stream
Options:
`start` if set, specifies the byte offset to read from
__Used in:__ `RETR`
#### [`delete(path)`](src/fs.js#L87)
@@ -206,11 +210,11 @@ Delete a file or directory
__Used in:__ `DELE`
#### [`rename(from, to)`](src/fs.js#L102)
Rename a file or directory
Renames a file or directory
__Used in:__ `RNFR`, `RNTO`
#### [`chmod(path)`](src/fs.js#L108)
Modify a file or directory's permissions
Modifies a file or directory's permissions
__Used in:__ `SITE CHMOD`
#### [`getUniqueName()`](src/fs.js#L113)

View File

@@ -15,12 +15,7 @@ module.exports = {
{value: 'WIP', name: 'WIP: Work in progress'}
],
scopes: [
{name: 'accounts'},
{name: 'admin'},
{name: 'exampleScope'},
{name: 'changeMe'}
],
scopes: [],
// it needs to match the value for field type. Eg.: 'fix'
/*
@@ -39,5 +34,5 @@ module.exports = {
allowBreakingChanges: ['feat', 'fix'],
// Appends the branch name to the footer of the commit. Useful for tracking commits after branches have been merged
appendBranchNameToCommitMessage: true
appendBranchNameToCommitMessage: false
};

12
package-lock.json generated
View File

@@ -3722,9 +3722,9 @@
"dev": true
},
"sinon": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.2.tgz",
"integrity": "sha1-xDqcVw8yuqwRWVBc/u0ZEIhV34k=",
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.5.tgz",
"integrity": "sha1-mi/A/41SbacW8wlTqixl1RiRf2w=",
"dev": true
},
"slash": {
@@ -4210,9 +4210,9 @@
"dev": true
},
"uuid": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
"integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE="
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
},
"validate-npm-package-license": {
"version": "3.0.1",

View File

@@ -53,7 +53,7 @@
"bunyan": "^1.8.10",
"lodash": "^4.17.4",
"moment": "^2.18.1",
"uuid": "^3.0.1",
"uuid": "^3.1.0",
"when": "^3.7.8"
},
"devDependencies": {
@@ -76,7 +76,7 @@
"npm-run-all": "4.0.2",
"rimraf": "2.6.1",
"semantic-release": "^6.3.6",
"sinon": "^2.3.2"
"sinon": "^2.3.5"
},
"engines": {
"node": ">=6.x",

View File

@@ -20,7 +20,8 @@ module.exports = {
};
function handleTLS() {
if (!this.server._tls) return this.reply(504);
if (!this.server._tls) return this.reply(502);
if (this.secure) return this.reply(202);
return this.reply(234)
.then(() => {

View File

@@ -9,8 +9,11 @@ module.exports = {
const feat = _.get(registry[cmd], 'flags.feat', null);
if (feat) return _.concat(feats, feat);
return feats;
}, [])
.map(feat => ` ${feat}`);
}, ['UTF8'])
.map(feat => ({
message: ` ${feat}`,
raw: true
}));
return features.length
? this.reply(211, 'Extensions supported', ...features, 'End')
: this.reply(211, 'No features');

View File

@@ -1,8 +1,34 @@
const _ = require('lodash');
const OPTIONS = {
UTF8: utf8,
'UTF-8': utf8
};
module.exports = {
directive: 'OPTS',
handler: function () {
return this.reply(501);
handler: function ({command} = {}) {
if (!_.has(command, 'arg')) return this.reply(501);
const [_option, ...args] = command.arg.split(' ');
const option = _.toUpper(_option);
if (!OPTIONS.hasOwnProperty(option)) return this.reply(500);
return OPTIONS[option].call(this, args);
},
syntax: '{{cmd}}',
description: 'Select options for a feature'
};
function utf8([setting] = []) {
switch (_.toUpper(setting)) {
case 'ON':
this.encoding = 'utf8';
return this.reply(200, 'UTF8 encoding on');
case 'OFF':
this.encoding = 'ascii';
return this.reply(200, 'UTF8 encoding off');
default:
return this.reply(501, 'Unknown setting for option');
}
}

View File

@@ -8,6 +8,7 @@ module.exports = {
syntax: '{{cmd}}',
description: 'Protection Buffer Size',
flags: {
no_auth: true
no_auth: true,
feat: 'PBSZ'
}
};

View File

@@ -17,6 +17,7 @@ module.exports = {
syntax: '{{cmd}}',
description: 'Data Channel Protection Level',
flags: {
no_auth: true
no_auth: true,
feat: 'PROT'
}
};

View File

@@ -0,0 +1,16 @@
const _ = require('lodash');
module.exports = {
directive: 'REST',
handler: function ({command} = {}) {
const arg = _.get(command, 'arg');
const byteCount = parseInt(arg, 10);
if (isNaN(byteCount) || byteCount < 0) return this.reply(501, 'Byte count must be 0 or greater');
this.restByteCount = byteCount;
return this.reply(350, `Resarting next transfer at ${byteCount}`);
},
syntax: '{{cmd}} <byte-count>',
description: 'Restart transfer from the specified point. Resets after any STORE or RETRIEVE'
};

View File

@@ -12,12 +12,13 @@ module.exports = {
this.commandSocket.pause();
dataSocket = socket;
})
.then(() => when.try(this.fs.read.bind(this.fs), command.arg))
.then(() => when.try(this.fs.read.bind(this.fs), command.arg, {start: this.restByteCount}))
.then(stream => {
this.restByteCount = 0;
return when.promise((resolve, reject) => {
dataSocket.on('error', err => stream.emit('error', err));
stream.on('data', data => dataSocket.write(data, this.encoding));
stream.on('data', data => dataSocket.write(data, this.transferType));
stream.on('end', () => resolve(this.reply(226)));
stream.on('error', err => reject(err));
this.reply(150).then(() => dataSocket.resume());

View File

@@ -15,8 +15,9 @@ module.exports = {
this.commandSocket.pause();
dataSocket = socket;
})
.then(() => when.try(this.fs.write.bind(this.fs), fileName, {append}))
.then(() => when.try(this.fs.write.bind(this.fs), fileName, {append, start: this.restByteCount}))
.then(stream => {
this.restByteCount = 0;
return when.promise((resolve, reject) => {
stream.once('error', err => dataSocket.emit('error', err));
stream.once('finish', () => resolve(this.reply(226, fileName)));
@@ -25,7 +26,7 @@ module.exports = {
// It is assumed that the `close` handler will call the end() method
dataSocket.once('end', () => stream.listenerCount('close') ? stream.emit('close') : stream.end());
dataSocket.once('error', err => reject(err));
dataSocket.on('data', data => stream.write(data, this.encoding));
dataSocket.on('data', data => stream.write(data, this.transferType));
this.reply(150).then(() => dataSocket.resume());
})

View File

@@ -1,20 +1,19 @@
const _ = require('lodash');
const ENCODING_TYPES = {
A: 'utf8',
I: 'binary',
L: 'binary'
};
module.exports = {
directive: 'TYPE',
handler: function ({command} = {}) {
const encoding = _.upperCase(command.arg);
if (!ENCODING_TYPES.hasOwnProperty(encoding)) return this.reply(501);
this.encoding = ENCODING_TYPES[encoding];
return this.reply(200);
if (/^A[0-9]?$/i.test(command.arg)) {
this.transferType = 'ascii';
} else if (/^L[0-9]?$/i.test(command.arg) || /^I$/i.test(command.arg)) {
this.transferType = 'binary';
} else {
return this.reply(501);
}
return this.reply(200, `Switch to "${this.transferType}" transfer mode.`);
},
syntax: '{{cmd}} <mode>',
description: 'Set the transfer mode, binary (I) or utf8 (A)'
description: 'Set the transfer mode, binary (I) or ascii (A)',
flags: {
feat: 'TYPE A,I,L'
}
};

View File

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

View File

@@ -15,8 +15,11 @@ class FtpConnection {
this.id = uuid.v4();
this.log = options.log.child({id: this.id, ip: this.ip});
this.commands = new Commands(this);
this.transferType = 'binary';
this.encoding = 'utf8';
this.bufferSize = false;
this._restByteCount = 0;
this._secure = false;
this.connector = new BaseConnector(this);
@@ -34,7 +37,7 @@ class FtpConnection {
}
_handleData(data) {
const messages = _.compact(data.toString('utf8').split('\r\n'));
const messages = _.compact(data.toString(this.encoding).split('\r\n'));
this.log.trace(messages);
return sequence(messages.map(message => this.commands.handle.bind(this.commands, message)));
}
@@ -47,6 +50,20 @@ class FtpConnection {
}
}
get restByteCount() {
return this._restByteCount > 0 ? this._restByteCount : undefined;
}
set restByteCount(rbc) {
this._restByteCount = rbc;
}
get secure() {
return this.server.isTLS || this._secure;
}
set secure(sec) {
this._secure = sec;
}
close(code = 421, message = 'Closing connection') {
return when
.resolve(code)
@@ -58,7 +75,7 @@ class FtpConnection {
return when.try(() => {
const loginListeners = this.server.listeners('login');
if (!loginListeners || !loginListeners.length) {
if (!this.server.options.anoymous) throw new errors.GeneralError('No "login" listener setup', 500);
if (!this.server.options.anonymous) throw new errors.GeneralError('No "login" listener setup', 500);
} else {
return this.server.emitPromise('login', {connection: this, username, password});
}
@@ -102,7 +119,7 @@ class FtpConnection {
const packet = !letter.raw ? _.compact([letter.code || options.code, letter.message]).join(seperator) : letter.message;
if (letter.socket && letter.socket.writable) {
this.log.trace({port: letter.socket.address().port, packet}, 'Reply');
this.log.trace({port: letter.socket.address().port, encoding: letter.encoding, packet}, 'Reply');
letter.socket.write(packet + '\r\n', letter.encoding, err => {
if (err) {
this.log.error(err);

View File

@@ -26,7 +26,7 @@ class Active extends Connector {
return closeExistingServer()
.then(() => {
this.dataSocket = new Socket();
this.dataSocket.setEncoding(this.encoding);
this.dataSocket.setEncoding(this.connection.transferType);
this.dataSocket.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
this.dataSocket.connect({ host, port, family }, () => {
this.dataSocket.pause();

View File

@@ -41,7 +41,7 @@ class Passive extends Connector {
return this.connection.reply(550, 'Remote addresses do not match')
.finally(() => this.connection.close());
}
this.log.debug({port}, 'Passive connection fulfilled.');
this.log.trace({port, remoteAddress: socket.remoteAddress}, 'Passive connection fulfilled.');
if (this.connection.secure) {
const secureContext = tls.createSecureContext(this.server._tls);
@@ -54,10 +54,10 @@ class Passive extends Connector {
this.dataSocket = socket;
}
this.dataSocket.connected = true;
this.dataSocket.setEncoding(this.connection.encoding);
this.dataSocket.setEncoding(this.connection.transferType);
this.dataSocket.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
this.dataSocket.on('close', () => {
this.log.debug('Passive connection closed');
this.log.trace('Passive connection closed');
this.end();
});
};
@@ -67,7 +67,7 @@ class Passive extends Connector {
this.dataServer.maxConnections = 1;
this.dataServer.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataServer', error: err}));
this.dataServer.on('close', () => {
this.log.debug('Passive server closed');
this.log.trace('Passive server closed');
this.dataServer = null;
});
@@ -75,7 +75,7 @@ class Passive extends Connector {
this.dataServer.listen(port, err => {
if (err) reject(err);
else {
this.log.info({port}, 'Passive connection listening');
this.log.debug({port}, 'Passive connection listening');
resolve(this.dataServer);
}
});
@@ -89,7 +89,8 @@ class Passive extends Connector {
this.server.options.pasv_range.split('-').map(v => v ? parseInt(v) : v) :
[this.server.options.pasv_range];
return findPort(min, max);
} else return undefined;
}
throw new errors.ConnectorError('Invalid pasv_range');
}
}

View File

@@ -65,22 +65,22 @@ class FileSystem {
});
}
write(fileName, {append = false} = {}) {
write(fileName, {append = false, start = undefined} = {}) {
const {fsPath} = this._resolvePath(fileName);
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+'});
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+', start});
stream.once('error', () => fs.unlink(fsPath));
stream.once('close', () => stream.end());
return stream;
}
read(fileName) {
read(fileName, {start = undefined} = {}) {
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(fsPath, {flags: 'r'});
const stream = syncFs.createReadStream(fsPath, {flags: 'r', start});
return stream;
});
}

View File

@@ -47,17 +47,13 @@ class FtpServer {
this.server = (this.isTLS ? tls : net).createServer(serverOptions, serverConnectionHandler);
this.server.on('error', err => this.log.error(err, '[Event] error'));
if (this.isTLS) {
this.server.on('tlsClientError', err => this.log.error(err, '[Event] tlsClientError'));
}
this.on = this.server.on.bind(this.server);
this.once = this.server.once.bind(this.server);
this.listeners = this.server.listeners.bind(this.server);
process.on('SIGTERM', () => this.close());
process.on('SIGINT', () => this.close());
process.on('SIGBREAK', () => this.close());
process.on('SIGHUP', () => this.close());
process.on('SIGQUIT', () => this.close());
}
get isTLS() {
@@ -94,8 +90,8 @@ class FtpServer {
}
setupTLS(_tls) {
if (!tls) return false;
return _.assign(_tls, {
if (!_tls) return false;
return _.assign({}, _tls, {
cert: _tls.cert ? fs.readFileSync(_tls.cert) : undefined,
key: _tls.key ? fs.readFileSync(_tls.key) : undefined,
ca: _tls.ca ? Array.isArray(_tls.ca) ? _tls.ca.map(_ca => fs.readFileSync(_ca)) : [fs.readFileSync(_tls.ca)] : undefined

View File

@@ -25,29 +25,25 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful | no active connection', done => {
it('// successful | no active connection', () => {
mockClient.connector.waitForConnection.restore();
sandbox.stub(mockClient.connector, 'waitForConnection').rejects();
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
expect(mockClient.connector.end.callCount).to.equal(0);
expect(mockClient.reply.args[0][0]).to.equal(226);
done();
})
.catch(done);
});
});
it('// successful | active connection', done => {
cmdFn()
it('// successful | active connection', () => {
return cmdFn()
.then(() => {
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
expect(mockClient.connector.end.callCount).to.equal(1);
expect(mockClient.reply.args[0][0]).to.equal(426);
expect(mockClient.reply.args[1][0]).to.equal(226);
done();
})
.catch(done);
});
});
});

View File

@@ -19,12 +19,10 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// successful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(202);
done();
})
.catch(done);
});
});
});

View File

@@ -22,31 +22,25 @@ describe(CMD, function () {
sandbox.restore();
});
it('TLS // supported', done => {
cmdFn({command: { arg: 'TLS', directive: CMD}})
it('TLS // supported', () => {
return cmdFn({command: { arg: 'TLS', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(234);
expect(mockClient.secure).to.equal(true);
done();
})
.catch(done);
});
});
it('SSL // not supported', done => {
cmdFn({command: { arg: 'SSL', directive: CMD}})
it('SSL // not supported', () => {
return cmdFn({command: { arg: 'SSL', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
})
.catch(done);
});
});
it('bad // bad', done => {
cmdFn({command: { arg: 'bad', directive: CMD}})
it('bad // bad', () => {
return cmdFn({command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
})
.catch(done);
});
});
});

View File

@@ -25,13 +25,11 @@ describe(CMD, function () {
sandbox.restore();
});
it('.. // successful', done => {
cmdFn({log, command: {directive: CMD}})
it('.. // successful', () => {
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.chdir.args[0][0]).to.equal('..');
done();
})
.catch(done);
});
});
});

View File

@@ -23,63 +23,56 @@ describe(CMD, function () {
});
describe('// check', function () {
it('fails on no fs', done => {
it('fails on no fs', () => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('fails on no fs chdir command', done => {
it('fails on no fs chdir command', () => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
});
it('test // successful', done => {
cmdFn({log, command: { arg: 'test', directive: CMD}})
it('test // successful', () => {
return 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');
done();
})
.catch(done);
});
});
it('test // successful', done => {
it('test // successful', () => {
mockClient.fs.chdir.restore();
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
cmdFn({log, command: { arg: 'test', directive: CMD}})
return 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');
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
mockClient.fs.chdir.restore();
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
cmdFn({log, command: { arg: 'bad', directive: CMD}})
return 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');
done();
})
.catch(done);
});
});
});

View File

@@ -23,51 +23,45 @@ describe(CMD, function () {
});
describe('// check', function () {
it('fails on no fs', done => {
it('fails on no fs', () => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('fails on no fs delete command', done => {
it('fails on no fs delete command', () => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
});
it('test // successful', done => {
cmdFn({log, command: { arg: 'test', directive: CMD}})
it('test // successful', () => {
return 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');
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
mockClient.fs.delete.restore();
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
cmdFn({log, command: { arg: 'bad', directive: CMD}})
return 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');
done();
})
.catch(done);
});
});
});

View File

@@ -19,39 +19,31 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn({command: { directive: CMD }})
it('// successful', () => {
return cmdFn({command: { directive: CMD }})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(211);
done();
})
.catch(done);
});
});
it('help // successful', done => {
cmdFn({command: { arg: 'help', directive: CMD}})
it('help // successful', () => {
return cmdFn({command: { arg: 'help', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(214);
done();
})
.catch(done);
});
});
it('help // successful', done => {
cmdFn({command: { arg: 'allo', directive: CMD}})
it('allo // successful', () => {
return cmdFn({command: { arg: 'allo', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(214);
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
cmdFn({command: { arg: 'bad', directive: CMD}})
it('bad // unsuccessful', () => {
return cmdFn({command: { arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(502);
done();
})
.catch(done);
});
});
});

View File

@@ -87,33 +87,31 @@ describe(CMD, function () {
});
describe('// check', function () {
it('fails on no fs', done => {
it('fails on no fs', () => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('fails on no fs list command', done => {
it('fails on no fs list command', () => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
});
it('. // successful', done => {
cmdFn({log, command: {directive: CMD}})
it('. // successful', () => {
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1].length).to.equal(3);
@@ -121,12 +119,10 @@ describe(CMD, function () {
expect(mockClient.reply.args[1][1]).to.have.property('message');
expect(mockClient.reply.args[1][1]).to.have.property('socket');
expect(mockClient.reply.args[2][0]).to.equal(226);
done();
})
.catch(done);
});
});
it('testfile.txt // successful', done => {
it('testfile.txt // successful', () => {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').resolves({
name: 'testfile.txt',
@@ -147,7 +143,7 @@ describe(CMD, function () {
isDirectory: () => false
});
cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1].length).to.equal(2);
@@ -155,31 +151,25 @@ describe(CMD, function () {
expect(mockClient.reply.args[1][1]).to.have.property('message');
expect(mockClient.reply.args[1][1]).to.have.property('socket');
expect(mockClient.reply.args[2][0]).to.equal(226);
done();
})
.catch(done);
});
});
it('. // unsuccessful', done => {
it('. // unsuccessful', () => {
mockClient.fs.list.restore();
sandbox.stub(mockClient.fs, 'list').rejects(new Error());
cmdFn({log, command: {directive: CMD}})
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(451);
done();
})
.catch(done);
});
});
it('. // unsuccessful (timeout)', done => {
it('. // unsuccessful (timeout)', () => {
sandbox.stub(mockClient.connector, 'waitForConnection').returns(when.reject(new when.TimeoutError()));
cmdFn({log, command: {directive: CMD}})
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
done();
})
.catch(done);
});
});
});

View File

@@ -23,50 +23,44 @@ describe(CMD, function () {
});
describe('// check', function () {
it('fails on no fs', done => {
it('fails on no fs', () => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('fails on no fs get command', done => {
it('fails on no fs get command', () => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
});
it('. // successful', done => {
cmdFn({log, command: {directive: CMD}})
it('. // successful', () => {
return 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');
done();
})
.catch(done);
});
});
it('. // unsuccessful', done => {
it('. // unsuccessful', () => {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects(new Error());
cmdFn({log, command: {directive: CMD}})
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
});

View File

@@ -23,63 +23,56 @@ describe(CMD, function () {
});
describe('// check', function () {
it('fails on no fs', done => {
it('fails on no fs', () => {
const badMockClient = { reply: () => {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('fails on no fs mkdir command', done => {
it('fails on no fs mkdir command', () => {
const badMockClient = { reply: () => {}, fs: {} };
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
sandbox.stub(badMockClient, 'reply').resolves();
badCmdFn()
return badCmdFn()
.then(() => {
expect(badMockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
});
it('test // successful', done => {
cmdFn({log, command: {arg: 'test', directive: CMD}})
it('test // successful', () => {
return 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');
done();
})
.catch(done);
});
});
it('test // successful', done => {
it('test // successful', () => {
mockClient.fs.mkdir.restore();
sandbox.stub(mockClient.fs, 'mkdir').resolves('test');
cmdFn({log, command: {arg: 'test', directive: CMD}})
return 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');
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
mockClient.fs.mkdir.restore();
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'));
cmdFn({log, command: {arg: 'bad', directive: CMD}})
return 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');
done();
})
.catch(done);
});
});
});

View File

@@ -19,21 +19,17 @@ describe(CMD, function () {
sandbox.restore();
});
it('S // successful', done => {
cmdFn({command: {arg: 'S'}})
it('S // successful', () => {
return cmdFn({command: {arg: 'S'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
})
.catch(done);
});
});
it('Q // unsuccessful', done => {
cmdFn({command: {arg: 'Q'}})
it('Q // unsuccessful', () => {
return cmdFn({command: {arg: 'Q'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
})
.catch(done);
});
});
});

View File

@@ -86,8 +86,8 @@ describe(CMD, function () {
sandbox.restore();
});
it('. // successful', done => {
cmdFn({log, command: {directive: CMD}})
it('. // successful', () => {
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1].length).to.equal(3);
@@ -95,12 +95,10 @@ describe(CMD, function () {
expect(mockClient.reply.args[1][1]).to.have.property('message');
expect(mockClient.reply.args[1][1]).to.have.property('socket');
expect(mockClient.reply.args[2][0]).to.equal(226);
done();
})
.catch(done);
});
});
it('testfile.txt // successful', done => {
it('testfile.txt // successful', () => {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').resolves({
name: 'testfile.txt',
@@ -121,7 +119,7 @@ describe(CMD, function () {
isDirectory: () => false
});
cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(150);
expect(mockClient.reply.args[1].length).to.equal(2);
@@ -129,8 +127,6 @@ describe(CMD, function () {
expect(mockClient.reply.args[1][1]).to.have.property('message');
expect(mockClient.reply.args[1][1]).to.have.property('socket');
expect(mockClient.reply.args[2][0]).to.equal(226);
done();
})
.catch(done);
});
});
});

View File

@@ -19,12 +19,10 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// successful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
})
.catch(done);
});
});
});

View File

@@ -19,12 +19,40 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// unsuccessful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
done();
})
.catch(done);
});
});
it('BAD // unsuccessful', () => {
return cmdFn({command: {arg: 'BAD', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(500);
});
});
it('UTF8 BAD // unsuccessful', () => {
return cmdFn({command: {arg: 'UTF8 BAD', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
});
});
it('UTF8 OFF // successful', () => {
return cmdFn({command: {arg: 'UTF8 OFF', directive: CMD}})
.then(() => {
expect(mockClient.encoding).to.equal('ascii');
expect(mockClient.reply.args[0][0]).to.equal(200);
});
});
it('UTF8 ON // successful', () => {
return cmdFn({command: {arg: 'UTF8 ON', directive: CMD}})
.then(() => {
expect(mockClient.encoding).to.equal('utf8');
expect(mockClient.reply.args[0][0]).to.equal(200);
});
});
});

View File

@@ -24,61 +24,51 @@ describe(CMD, function () {
sandbox.restore();
});
it('pass // successful', done => {
cmdFn({log, command: {arg: 'pass', directive: CMD}})
it('pass // successful', () => {
return cmdFn({log, command: {arg: 'pass', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.args[0]).to.eql(['anonymous', 'pass']);
done();
})
.catch(done);
});
});
it('// successful (already authenticated)', done => {
it('// successful (already authenticated)', () => {
mockClient.server.options.anonymous = true;
mockClient.authenticated = true;
cmdFn({log, command: {directive: CMD}})
return cmdFn({log, command: {directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(202);
expect(mockClient.login.callCount).to.equal(0);
mockClient.server.options.anonymous = false;
mockClient.authenticated = false;
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects('bad');
cmdFn({log, command: {arg: 'bad', directive: CMD}})
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects({});
cmdFn({log, command: {arg: 'bad', directive: CMD}})
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
done();
})
.catch(done);
});
});
it('bad // unsuccessful', done => {
it('bad // unsuccessful', () => {
delete mockClient.username;
cmdFn({log, command: {arg: 'bad', directive: CMD}})
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(503);
done();
})
.catch(done);
});
});
});

View File

@@ -20,38 +20,32 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful', done => {
cmdFn()
it('// unsuccessful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(202);
done();
})
.catch(done);
});
});
it('// successful', done => {
it('// successful', () => {
mockClient.secure = true;
mockClient.server._tls = {};
cmdFn({command: {arg: '0'}})
return cmdFn({command: {arg: '0'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.bufferSize).to.equal(0);
done();
})
.catch(done);
});
});
it('// successful', done => {
it('// successful', () => {
mockClient.secure = true;
mockClient.server._tls = {};
cmdFn({command: {arg: '10'}})
return cmdFn({command: {arg: '10'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.bufferSize).to.equal(10);
done();
})
.catch(done);
});
});
});

View File

@@ -22,33 +22,27 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no argument', done => {
cmdFn()
it('// unsuccessful | no argument', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
done();
})
.catch(done);
});
});
it('// unsuccessful | invalid argument', done => {
cmdFn({ command: { arg: '1,2,3,4,5' } })
it('// unsuccessful | invalid argument', () => {
return cmdFn({ command: { arg: '1,2,3,4,5' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
done();
})
.catch(done);
});
});
it('// successful', done => {
cmdFn({ command: { arg: '192,168,0,100,137,214' } })
it('// successful', () => {
return 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);
expect(ip).to.equal('192.168.0.100');
expect(port).to.equal(35286);
done();
})
.catch(done);
});
});
});

View File

@@ -20,56 +20,46 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful', done => {
cmdFn()
it('// unsuccessful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(202);
done();
})
.catch(done);
});
});
it('// unsuccessful - no bufferSize', done => {
it('// unsuccessful - no bufferSize', () => {
mockClient.server._tls = {};
mockClient.secure = true;
cmdFn({command: {arg: 'P'}})
return cmdFn({command: {arg: 'P'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(503);
done();
})
.catch(done);
});
});
it('// successful', done => {
it('// successful', () => {
mockClient.bufferSize = 0;
mockClient.secure = true;
cmdFn({command: {arg: 'p'}})
return cmdFn({command: {arg: 'p'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
})
.catch(done);
});
});
it('// unsuccessful - unsupported', done => {
it('// unsuccessful - unsupported', () => {
mockClient.secure = true;
cmdFn({command: {arg: 'C'}})
return cmdFn({command: {arg: 'C'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(536);
done();
})
.catch(done);
});
});
it('// unsuccessful - unknown', done => {
it('// unsuccessful - unknown', () => {
mockClient.secure = true;
cmdFn({command: {arg: 'QQ'}})
return cmdFn({command: {arg: 'QQ'}})
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
})
.catch(done);
});
});
});

View File

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

View File

@@ -18,12 +18,10 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// successful', () => {
return cmdFn()
.then(() => {
expect(mockClient.close.callCount).to.equal(1);
done();
})
.catch(done);
});
});
});

View File

@@ -0,0 +1,58 @@
const {expect} = require('chai');
const sinon = require('sinon');
const when = require('when');
const CMD = 'REST';
describe(CMD, function () {
let sandbox;
const mockClient = {
reply: () => when.resolve()
};
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
sandbox.spy(mockClient, 'reply');
});
afterEach(() => {
sandbox.restore();
});
it('// unsuccessful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
});
});
it('-1 // unsuccessful', () => {
return cmdFn({command: { arg: '-1', directive: CMD } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
});
});
it('bad // unsuccessful', () => {
return cmdFn({command: { arg: 'bad', directive: CMD } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
});
});
it('1 // successful', () => {
return cmdFn({command: { arg: '1', directive: CMD } })
.then(() => {
expect(mockClient.restByteCount).to.equal(1);
expect(mockClient.reply.args[0][0]).to.equal(350);
});
});
it('0 // successful', () => {
return cmdFn({command: { arg: '0', directive: CMD } })
.then(() => {
expect(mockClient.restByteCount).to.equal(0);
expect(mockClient.reply.args[0][0]).to.equal(350);
});
});
});

View File

@@ -0,0 +1,75 @@
const when = require('when');
const bunyan = require('bunyan');
const {expect} = require('chai');
const sinon = require('sinon');
const CMD = 'RETR';
describe(CMD, function () {
let sandbox;
let log = bunyan.createLogger({name: CMD});
const mockClient = {
commandSocket: {
pause: () => {},
resume: () => {}
},
reply: () => when.resolve(),
connector: {
waitForConnection: () => when.resolve({
resume: () => {}
}),
end: () => {}
}
};
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
mockClient.fs = {
read: () => {}
};
sandbox.spy(mockClient, 'reply');
});
afterEach(() => sandbox.restore());
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
});
});
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
});
});
it('// unsuccessful | connector times out', () => {
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
return when.reject(new when.TimeoutError());
});
return cmdFn({log, command: {arg: 'test.txt'} })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
});
});
it('// unsuccessful | connector errors out', () => {
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
return when.reject(new Error('test'));
});
return cmdFn({log, command: {arg: 'test.txt'} })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(551);
});
});
});

View File

@@ -24,47 +24,39 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
it('test // unsuccessful | file get fails', done => {
it('test // unsuccessful | file get fails', () => {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { arg: 'test' } })
return cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('test // successful', done => {
cmdFn({ log: mockLog, command: { arg: 'test' } })
it('test // successful', () => {
return 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);
done();
})
.catch(done);
});
});
});

View File

@@ -25,58 +25,48 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no renameFrom set', done => {
it('// unsuccessful | no renameFrom set', () => {
delete mockClient.renameFrom;
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(503);
done();
})
.catch(done);
});
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
it('new // unsuccessful | rename fails', done => {
it('new // unsuccessful | rename fails', () => {
mockClient.fs.rename.restore();
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { arg: 'new' } })
return cmdFn({ log: mockLog, command: { arg: 'new' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('new // successful', done => {
cmdFn({ command: { arg: 'new' } })
it('new // successful', () => {
return cmdFn({ command: { arg: 'new' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(250);
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new']);
done();
})
.catch(done);
});
});
});

View File

@@ -22,45 +22,37 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
it('// unsuccessful | file get fails', done => {
it('// unsuccessful | file get fails', () => {
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { arg: 'test' } })
return cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// successful', done => {
cmdFn({ command: { arg: 'test' } })
it('// successful', () => {
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(213);
done();
})
.catch(done);
});
});
});

View File

@@ -23,49 +23,41 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// successful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(211);
done();
})
.catch(done);
});
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
cmdFn({ command: { arg: 'test' } })
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
cmdFn({ command: { arg: 'test' } })
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
it('// unsuccessful | file get fails', done => {
it('// unsuccessful | file get fails', () => {
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
cmdFn({ log: mockLog, command: { arg: 'test' } })
return cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(450);
done();
})
.catch(done);
});
});
it('// successful | file', done => {
it('// successful | file', () => {
sandbox.stub(mockClient.fs, 'get').returns({
name: 'test_file',
dev: 2114,
@@ -85,15 +77,13 @@ describe(CMD, function () {
isDirectory: () => false
});
cmdFn({ command: { arg: 'test' } })
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(212);
done();
})
.catch(done);
});
});
it('// successful | directory', done => {
it('// successful | directory', () => {
sandbox.stub(mockClient.fs, 'list').returns([{
name: 'test_file',
dev: 2114,
@@ -132,11 +122,9 @@ describe(CMD, function () {
isDirectory: () => true
});
cmdFn({ command: { arg: 'test' } })
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(213);
done();
})
.catch(done);
});
});
});

View File

@@ -0,0 +1,75 @@
const when = require('when');
const bunyan = require('bunyan');
const {expect} = require('chai');
const sinon = require('sinon');
const CMD = 'STOR';
describe(CMD, function () {
let sandbox;
let log = bunyan.createLogger({name: CMD});
const mockClient = {
commandSocket: {
pause: () => {},
resume: () => {}
},
reply: () => when.resolve(),
connector: {
waitForConnection: () => when.resolve({
resume: () => {}
}),
end: () => {}
}
};
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create();
mockClient.fs = {
write: () => {}
};
sandbox.spy(mockClient, 'reply');
});
afterEach(() => sandbox.restore());
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
});
});
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
});
});
it('// unsuccessful | connector times out', () => {
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
return when.reject(new when.TimeoutError());
});
return cmdFn({log, command: {arg: 'test.txt'} })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(425);
});
});
it('// unsuccessful | connector errors out', () => {
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
return when.reject(new Error('test'));
});
return cmdFn({log, command: {arg: 'test.txt'} })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
});
});
});

View File

@@ -30,54 +30,46 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', () => {
delete mockClient.fs;
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(550);
done();
})
.catch(done);
});
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', () => {
mockClient.fs = {};
cmdFn()
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(402);
done();
})
.catch(done);
});
});
it('// successful | given name is unique', done => {
it('// successful | given name is unique', () => {
mockClient.fs.get.restore();
sandbox.stub(mockClient.fs, 'get').rejects({});
cmdFn({ command: { arg: 'good' } })
return 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('arg');
expect(call.command.arg).to.eql('good');
expect(mockClient.fs.getUniqueName.callCount).to.equal(0);
done();
})
.catch(done);
});
});
it('// successful | generates unique name', done => {
cmdFn({ command: { arg: 'bad' } })
it('// successful | generates unique name', () => {
return 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('arg');
expect(call.command.arg).to.eql('4');
expect(mockClient.fs.getUniqueName.callCount).to.equal(1);
done();
})
.catch(done);
});
});
});

View File

@@ -19,21 +19,17 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn({command: { arg: 'F' } })
it('// successful', () => {
return cmdFn({command: { arg: 'F' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
done();
})
.catch(done);
});
});
it('// unsuccessful', done => {
cmdFn({command: { arg: 'X' } })
it('// unsuccessful', () => {
return cmdFn({command: { arg: 'X' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(504);
done();
})
.catch(done);
});
});
});

View File

@@ -19,12 +19,10 @@ describe(CMD, function () {
sandbox.restore();
});
it('// successful', done => {
cmdFn()
it('// successful', () => {
return cmdFn()
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(215);
done();
})
.catch(done);
});
});
});

View File

@@ -13,50 +13,42 @@ describe(CMD, function () {
beforeEach(() => {
sandbox = sinon.sandbox.create();
mockClient.encoding = null;
mockClient.transferType = null;
sandbox.spy(mockClient, 'reply');
});
afterEach(() => {
sandbox.restore();
});
it('A // successful', done => {
cmdFn({ command: { arg: 'A' } })
it('A // successful', () => {
return cmdFn({ command: { arg: 'A' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('utf8');
done();
})
.catch(done);
expect(mockClient.transferType).to.equal('ascii');
});
});
it('I // successful', done => {
cmdFn({ command: { arg: 'I' } })
it('I // successful', () => {
return cmdFn({ command: { arg: 'I' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('binary');
done();
})
.catch(done);
expect(mockClient.transferType).to.equal('binary');
});
});
it('L // successful', done => {
cmdFn({ command: { arg: 'L' } })
it('L // successful', () => {
return cmdFn({ command: { arg: 'L' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(200);
expect(mockClient.encoding).to.equal('binary');
done();
})
.catch(done);
expect(mockClient.transferType).to.equal('binary');
});
});
it('X // successful', done => {
cmdFn({ command: { arg: 'X' } })
it('X // successful', () => {
return cmdFn({ command: { arg: 'X' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
expect(mockClient.encoding).to.equal(null);
done();
})
.catch(done);
expect(mockClient.transferType).to.equal(null);
});
});
});

View File

@@ -28,70 +28,80 @@ describe(CMD, function () {
sandbox.restore();
});
it('test // successful | prompt for password', done => {
cmdFn({ command: { arg: 'test' } })
it('test // successful | prompt for password', () => {
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(331);
done();
})
.catch(done);
});
});
it('test // successful | anonymous login', done => {
it('test // successful | anonymous login', () => {
mockClient.server.options = {anonymous: true};
cmdFn({ command: { arg: 'anonymous' } })
return cmdFn({ command: { arg: 'anonymous' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.callCount).to.equal(1);
done();
})
.catch(done);
});
});
it('test // unsuccessful | no username provided', done => {
cmdFn({ command: { } })
it('test // unsuccessful | no username provided', () => {
return cmdFn({ command: { } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(501);
expect(mockClient.login.callCount).to.equal(0);
done();
})
.catch(done);
});
});
it('test // unsuccessful | already set username', done => {
it('test // unsuccessful | already set username', () => {
mockClient.username = 'test';
cmdFn({ command: { arg: 'test' } })
return cmdFn({ command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
expect(mockClient.login.callCount).to.equal(0);
done();
})
.catch(done);
});
});
it('test // successful | regular login if anonymous is true', done => {
it('test // successful | regular login if anonymous is true', () => {
mockClient.server.options = {anonymous: true};
cmdFn({ log: mockLog, command: { arg: 'test' } })
return cmdFn({ log: mockLog, command: { arg: 'test' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(331);
expect(mockClient.login.callCount).to.equal(0);
done();
})
.catch(done);
});
});
it('test // successful | anonymous login with set username', done => {
it('test // successful | anonymous login with set username', () => {
mockClient.server.options = {anonymous: 'sillyrabbit'};
cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
return cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.callCount).to.equal(1);
done();
})
.catch(done);
});
});
it('test // unsuccessful | anonymous login fails', () => {
mockClient.server.options = {anonymous: true};
mockClient.login.restore();
sandbox.stub(mockClient, 'login').rejects(new Error('test'));
return cmdFn({ log: mockLog, command: { arg: 'anonymous' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(530);
expect(mockClient.login.callCount).to.equal(1);
});
});
it('test // successful | does not login if already authenticated', () => {
mockClient.authenticated = true;
return cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
.then(() => {
expect(mockClient.reply.args[0][0]).to.equal(230);
expect(mockClient.login.callCount).to.equal(0);
});
});
});

View File

@@ -3,6 +3,7 @@ const {expect} = require('chai');
const sinon = require('sinon');
const net = require('net');
const tls = require('tls');
const ActiveConnector = require('../../src/connector/active');
const findPort = require('../../src/helpers/find-port');
@@ -33,34 +34,49 @@ describe('Connector - Active //', function () {
server.close(done);
});
it('sets up a connection', function (done) {
active.setupConnection('127.0.0.1', PORT)
it('sets up a connection', function () {
return active.setupConnection('127.0.0.1', PORT)
.then(() => {
expect(active.dataSocket).to.exist;
done();
})
.catch(done);
});
});
it('destroys existing connection, then sets up a connection', function (done) {
it('destroys existing connection, then sets up a connection', function () {
const destroyFnSpy = sandbox.spy(active.dataSocket, 'destroy');
active.setupConnection('127.0.0.1', PORT)
return active.setupConnection('127.0.0.1', PORT)
.then(() => {
expect(destroyFnSpy.callCount).to.equal(1);
expect(active.dataSocket).to.exist;
done();
})
.catch(done);
});
});
it('waits for connection', function (done) {
active.setupConnection('127.0.0.1', PORT)
it('waits for connection', function () {
return active.setupConnection('127.0.0.1', PORT)
.then(() => {
expect(active.dataSocket).to.exist;
return active.waitForConnection();
})
.then(() => done())
.catch(done);
.then(dataSocket => {
expect(dataSocket.connected).to.equal(true);
expect(dataSocket instanceof net.Socket).to.equal(true);
expect(dataSocket instanceof tls.TLSSocket).to.equal(false);
});
});
it('upgrades to a secure connection', function () {
mockConnection.secure = true;
mockConnection.server = { _tls: {} };
return active.setupConnection('127.0.0.1', PORT)
.then(() => {
expect(active.dataSocket).to.exist;
return active.waitForConnection();
})
.then(dataSocket => {
expect(dataSocket.connected).to.equal(true);
expect(dataSocket instanceof net.Socket).to.equal(true);
expect(dataSocket instanceof tls.TLSSocket).to.equal(true);
});
});
});

View File

@@ -20,6 +20,10 @@ describe('Connector - Passive //', function () {
};
let sandbox;
function shouldNotResolve() {
throw new Error('Should not resolve');
}
before(() => {
passive = new PassiveConnector(mockConnection);
});
@@ -36,45 +40,49 @@ describe('Connector - Passive //', function () {
sandbox.restore();
});
it('cannot wait for connection with no server', function (done) {
passive.waitForConnection()
.then(() => done('should not happen'))
it('cannot wait for connection with no server', function () {
return passive.waitForConnection()
.then(shouldNotResolve)
.catch(err => {
expect(err.name).to.equal('ConnectorError');
done();
});
});
it('has invalid pasv range', function (done) {
it('no pasv range provided', function () {
delete mockConnection.server.options.pasv_range;
return passive.setupServer()
.then(shouldNotResolve)
.catch(err => {
expect(err.name).to.equal('ConnectorError');
});
});
it('has invalid pasv range', function () {
mockConnection.server.options.pasv_range = -1;
passive.setupServer()
.then(() => done('should not happen'))
return passive.setupServer()
.then(shouldNotResolve)
.catch(err => {
expect(err.name).to.equal('RangeError');
done();
});
});
it('sets up a server', function (done) {
passive.setupServer()
it('sets up a server', function () {
return passive.setupServer()
.then(() => {
expect(passive.dataServer).to.exist;
done();
})
.catch(done);
});
});
it('destroys existing server, then sets up a server', function (done) {
it('destroys existing server, then sets up a server', function () {
const closeFnSpy = sandbox.spy(passive.dataServer, 'close');
passive.setupServer()
return passive.setupServer()
.then(() => {
expect(closeFnSpy.callCount).to.equal(1);
expect(passive.dataServer).to.exist;
done();
})
.catch(done);
});
});
it('refuses connection with different remote address', function (done) {
@@ -97,8 +105,8 @@ describe('Connector - Passive //', function () {
.catch(done);
});
it('accepts connection', function (done) {
passive.setupServer()
it('accepts connection', function () {
return passive.setupServer()
.then(() => {
expect(passive.dataServer).to.exist;
@@ -109,8 +117,6 @@ describe('Connector - Passive //', function () {
.then(() => {
expect(passive.dataSocket).to.exist;
passive.end();
done();
})
.catch(done);
});
});
});

View File

@@ -2,10 +2,9 @@ const {expect} = require('chai');
const escapePath = require('../../src/helpers/escape-path');
describe('helpers // escape-path', function () {
it('escapes quotes', done => {
it('escapes quotes', () => {
const string = '"test"';
const escapedString = escapePath(string);
expect(escapedString).to.equal('""test""');
done();
});
});

View File

@@ -17,22 +17,19 @@ describe('helpers // find-port', function () {
sandbox.restore();
});
it('finds a port', done => {
findPort(1)
it('finds a port', () => {
return findPort(1)
.then(port => {
expect(Server.prototype.listen.callCount).to.be.above(1);
expect(port).to.be.above(1);
done();
})
.catch(done);
});
});
it('does not find a port', done => {
findPort(1, 2)
.then(() => done('no'))
it('does not find a port', () => {
return findPort(1, 2)
.then(() => expect(1).to.equal(2)) // should not happen
.catch(err => {
expect(err).to.exist;
done();
});
});
});

View File

@@ -1,36 +1,51 @@
const {expect} = require('chai');
const sinon = require('sinon');
const resolveHost = require('../../src/helpers/resolve-host');
describe('helpers //resolve-host', function () {
this.timeout(4000);
it('fetches ip address', done => {
let sandbox;
beforeEach(() => {
sandbox = sinon.sandbox.create();
});
afterEach(() => sandbox.restore());
it('fetches ip address', () => {
const hostname = '0.0.0.0';
resolveHost(hostname)
return resolveHost(hostname)
.then(resolvedHostname => {
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
done();
})
.catch(done);
});
});
it('fetches ip address', done => {
it('fetches ip address', () => {
const hostname = null;
resolveHost(hostname)
return resolveHost(hostname)
.then(resolvedHostname => {
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
done();
})
.catch(done);
});
});
it('does nothing', done => {
it('does nothing', () => {
const hostname = '127.0.0.1';
resolveHost(hostname)
return resolveHost(hostname)
.then(resolvedHostname => {
expect(resolvedHostname).to.equal(hostname);
done();
})
.catch(done);
});
});
it('fails on getting hostname', () => {
sandbox.stub(require('http'), 'get').callsFake(function (url, cb) {
cb({
statusCode: 420
});
});
return resolveHost(null)
.then(() => expect(1).to.equal(2))
.catch(err => {
expect(err.code).to.equal(420);
});
});
});

View File

@@ -14,7 +14,7 @@ describe('FtpServer', function () {
let server;
let client;
before(done => {
before(() => {
server = new FtpServer(process.env.FTP_URL, {
log,
pasv_range: process.env.PASV_RANGE,
@@ -22,14 +22,14 @@ describe('FtpServer', function () {
key: `${process.cwd()}/test/cert/server.key`,
cert: `${process.cwd()}/test/cert/server.crt`,
ca: `${process.cwd()}/test/cert/server.csr`
}
},
greeting: ['hello', 'world']
});
server.on('login', (data, resolve) => {
resolve({root: process.cwd()});
});
server.listen()
.then(() => done());
return server.listen();
});
after(() => {
server.close();
@@ -109,12 +109,12 @@ describe('FtpServer', function () {
});
});
it('STOR test.txt', done => {
it('STOR tést.txt', done => {
const buffer = Buffer.from('test text file');
client.put(buffer, 'test.txt', err => {
client.put(buffer, 'tést.txt', err => {
expect(err).to.not.exist;
expect(fs.existsSync('./test/test.txt')).to.equal(true);
fs.readFile('./test/test.txt', (fserr, data) => {
expect(fs.existsSync('./test/tést.txt')).to.equal(true);
fs.readFile('./test/tést.txt', (fserr, data) => {
expect(fserr).to.not.exist;
expect(data.toString()).to.equal('test text file');
done();
@@ -122,11 +122,11 @@ describe('FtpServer', function () {
});
});
it('APPE test.txt', done => {
it('APPE tést.txt', done => {
const buffer = Buffer.from(', awesome!');
client.append(buffer, 'test.txt', err => {
client.append(buffer, 'tést.txt', err => {
expect(err).to.not.exist;
fs.readFile('./test/test.txt', (fserr, data) => {
fs.readFile('./test/tést.txt', (fserr, data) => {
expect(fserr).to.not.exist;
expect(data.toString()).to.equal('test text file, awesome!');
done();
@@ -134,8 +134,8 @@ describe('FtpServer', function () {
});
});
it('RETR test.txt', done => {
client.get('test.txt', (err, stream) => {
it('RETR tést.txt', done => {
client.get('tést.txt', (err, stream) => {
expect(err).to.not.exist;
let text = '';
stream.on('data', data => {
@@ -148,10 +148,10 @@ describe('FtpServer', function () {
});
});
it('RNFR test.txt, RNTO awesome.txt', done => {
client.rename('test.txt', 'awesome.txt', err => {
it('RNFR tést.txt, RNTO awesome.txt', done => {
client.rename('tést.txt', 'awesome.txt', err => {
expect(err).to.not.exist;
expect(fs.existsSync('./test/test.txt')).to.equal(false);
expect(fs.existsSync('./test/tést.txt')).to.equal(false);
expect(fs.existsSync('./test/awesome.txt')).to.equal(true);
fs.readFile('./test/awesome.txt', (fserr, data) => {
expect(fserr).to.not.exist;
@@ -196,19 +196,19 @@ describe('FtpServer', function () {
});
});
it('MKD tmp', done => {
if (fs.existsSync('./test/tmp')) {
fs.rmdirSync('./test/tmp');
it('MKD témp', done => {
if (fs.existsSync('./test/témp')) {
fs.rmdirSync('./test/témp');
}
client.mkdir('tmp', err => {
client.mkdir('témp', err => {
expect(err).to.not.exist;
expect(fs.existsSync('./test/tmp')).to.equal(true);
expect(fs.existsSync('./test/témp')).to.equal(true);
done();
});
});
it('CWD tmp', done => {
client.cwd('tmp', (err, data) => {
it('CWD témp', done => {
client.cwd('témp', (err, data) => {
expect(err).to.not.exist;
expect(data).to.be.a('string');
done();
@@ -222,10 +222,10 @@ describe('FtpServer', function () {
});
});
it('RMD tmp', done => {
client.rmdir('tmp', err => {
it('RMD témp', done => {
client.rmdir('témp', err => {
expect(err).to.not.exist;
expect(fs.existsSync('./test/tmp')).to.equal(false);
expect(fs.existsSync('./test/témp')).to.equal(false);
done();
});
});