Initial commit
This commit is contained in:
316
server/node_modules/ssh2/test/common.js
generated
vendored
Normal file
316
server/node_modules/ssh2/test/common.js
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { readFileSync } = require('fs');
|
||||
const { join } = require('path');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const Client = require('../lib/client.js');
|
||||
const Server = require('../lib/server.js');
|
||||
const { parseKey } = require('../lib/protocol/keyParser.js');
|
||||
|
||||
const mustCallChecks = [];
|
||||
|
||||
const DEFAULT_TEST_TIMEOUT = 30 * 1000;
|
||||
|
||||
function noop() {}
|
||||
|
||||
function runCallChecks(exitCode) {
|
||||
if (exitCode !== 0) return;
|
||||
|
||||
const failed = mustCallChecks.filter((context) => {
|
||||
if ('minimum' in context) {
|
||||
context.messageSegment = `at least ${context.minimum}`;
|
||||
return context.actual < context.minimum;
|
||||
}
|
||||
context.messageSegment = `exactly ${context.exact}`;
|
||||
return context.actual !== context.exact;
|
||||
});
|
||||
|
||||
failed.forEach((context) => {
|
||||
console.error('Mismatched %s function calls. Expected %s, actual %d.',
|
||||
context.name,
|
||||
context.messageSegment,
|
||||
context.actual);
|
||||
console.error(context.stack.split('\n').slice(2).join('\n'));
|
||||
});
|
||||
|
||||
if (failed.length)
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function mustCall(fn, exact) {
|
||||
return _mustCallInner(fn, exact, 'exact');
|
||||
}
|
||||
|
||||
function mustCallAtLeast(fn, minimum) {
|
||||
return _mustCallInner(fn, minimum, 'minimum');
|
||||
}
|
||||
|
||||
function _mustCallInner(fn, criteria = 1, field) {
|
||||
if (process._exiting)
|
||||
throw new Error('Cannot use common.mustCall*() in process exit handler');
|
||||
|
||||
if (typeof fn === 'number') {
|
||||
criteria = fn;
|
||||
fn = noop;
|
||||
} else if (fn === undefined) {
|
||||
fn = noop;
|
||||
}
|
||||
|
||||
if (typeof criteria !== 'number')
|
||||
throw new TypeError(`Invalid ${field} value: ${criteria}`);
|
||||
|
||||
const context = {
|
||||
[field]: criteria,
|
||||
actual: 0,
|
||||
stack: inspect(new Error()),
|
||||
name: fn.name || '<anonymous>'
|
||||
};
|
||||
|
||||
// Add the exit listener only once to avoid listener leak warnings
|
||||
if (mustCallChecks.length === 0)
|
||||
process.on('exit', runCallChecks);
|
||||
|
||||
mustCallChecks.push(context);
|
||||
|
||||
function wrapped(...args) {
|
||||
++context.actual;
|
||||
return fn.call(this, ...args);
|
||||
}
|
||||
// TODO: remove origFn?
|
||||
wrapped.origFn = fn;
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
function getCallSite(top) {
|
||||
const originalStackFormatter = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = (err, stack) =>
|
||||
`${stack[0].getFileName()}:${stack[0].getLineNumber()}`;
|
||||
const err = new Error();
|
||||
Error.captureStackTrace(err, top);
|
||||
// With the V8 Error API, the stack is not formatted until it is accessed
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
err.stack;
|
||||
Error.prepareStackTrace = originalStackFormatter;
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
function mustNotCall(msg) {
|
||||
const callSite = getCallSite(mustNotCall);
|
||||
return function mustNotCall(...args) {
|
||||
args = args.map(inspect).join(', ');
|
||||
const argsInfo = (args.length > 0
|
||||
? `\ncalled with arguments: ${args}`
|
||||
: '');
|
||||
assert.fail(
|
||||
`${msg || 'function should not have been called'} at ${callSite}`
|
||||
+ argsInfo);
|
||||
};
|
||||
}
|
||||
|
||||
function setup(title, configs) {
|
||||
const {
|
||||
client: clientCfg_,
|
||||
server: serverCfg_,
|
||||
allReady: allReady_,
|
||||
timeout: timeout_,
|
||||
debug,
|
||||
noForceClientReady,
|
||||
noForceServerReady,
|
||||
noClientError,
|
||||
noServerError,
|
||||
} = configs;
|
||||
|
||||
// Make shallow copies of client/server configs to avoid mutating them when
|
||||
// multiple tests share the same config object reference
|
||||
let clientCfg;
|
||||
if (clientCfg_)
|
||||
clientCfg = { ...clientCfg_ };
|
||||
let serverCfg;
|
||||
if (serverCfg_)
|
||||
serverCfg = { ...serverCfg_ };
|
||||
|
||||
let clientClose = false;
|
||||
let clientReady = false;
|
||||
let serverClose = false;
|
||||
let serverReady = false;
|
||||
const msg = (text) => {
|
||||
return `${title}: ${text}`;
|
||||
};
|
||||
|
||||
const timeout = (typeof timeout_ === 'number'
|
||||
? timeout_
|
||||
: DEFAULT_TEST_TIMEOUT);
|
||||
|
||||
const allReady = (typeof allReady_ === 'function' ? allReady_ : undefined);
|
||||
|
||||
if (debug) {
|
||||
if (clientCfg) {
|
||||
clientCfg.debug = (...args) => {
|
||||
console.log(`[${title}][CLIENT]`, ...args);
|
||||
};
|
||||
}
|
||||
if (serverCfg) {
|
||||
serverCfg.debug = (...args) => {
|
||||
console.log(`[${title}][SERVER]`, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let timer;
|
||||
let client;
|
||||
let clientReadyFn;
|
||||
let server;
|
||||
let serverReadyFn;
|
||||
if (clientCfg) {
|
||||
client = new Client();
|
||||
if (!noClientError)
|
||||
client.on('error', onError);
|
||||
clientReadyFn = (noForceClientReady ? onReady : mustCall(onReady));
|
||||
client.on('ready', clientReadyFn)
|
||||
.on('close', mustCall(onClose));
|
||||
} else {
|
||||
clientReady = clientClose = true;
|
||||
}
|
||||
|
||||
if (serverCfg) {
|
||||
server = new Server(serverCfg);
|
||||
if (!noServerError)
|
||||
server.on('error', onError);
|
||||
serverReadyFn = (noForceServerReady ? onReady : mustCall(onReady));
|
||||
server.on('connection', mustCall((conn) => {
|
||||
if (!noServerError)
|
||||
conn.on('error', onError);
|
||||
conn.on('ready', serverReadyFn);
|
||||
server.close();
|
||||
})).on('close', mustCall(onClose));
|
||||
} else {
|
||||
serverReady = serverClose = true;
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
const which = (this === client ? 'client' : 'server');
|
||||
assert(false, msg(`Unexpected ${which} error: ${err.stack}\n`));
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
if (this === client) {
|
||||
assert(!clientReady,
|
||||
msg('Received multiple ready events for client'));
|
||||
clientReady = true;
|
||||
} else {
|
||||
assert(!serverReady,
|
||||
msg('Received multiple ready events for server'));
|
||||
serverReady = true;
|
||||
}
|
||||
clientReady && serverReady && allReady && allReady();
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
if (this === client) {
|
||||
assert(!clientClose,
|
||||
msg('Received multiple close events for client'));
|
||||
clientClose = true;
|
||||
} else {
|
||||
assert(!serverClose,
|
||||
msg('Received multiple close events for server'));
|
||||
serverClose = true;
|
||||
}
|
||||
if (clientClose && serverClose)
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
process.nextTick(mustCall(() => {
|
||||
function connectClient() {
|
||||
if (clientCfg.sock) {
|
||||
clientCfg.sock.connect(server.address().port, 'localhost');
|
||||
} else {
|
||||
clientCfg.host = 'localhost';
|
||||
clientCfg.port = server.address().port;
|
||||
}
|
||||
try {
|
||||
client.connect(clientCfg);
|
||||
} catch (ex) {
|
||||
ex.message = msg(ex.message);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (server) {
|
||||
server.listen(0, 'localhost', mustCall(() => {
|
||||
if (timeout >= 0) {
|
||||
timer = setTimeout(() => {
|
||||
assert(false, msg('Test timed out'));
|
||||
}, timeout);
|
||||
}
|
||||
if (client)
|
||||
connectClient();
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
return { client, server };
|
||||
}
|
||||
|
||||
const FIXTURES_DIR = join(__dirname, 'fixtures');
|
||||
const fixture = (() => {
|
||||
const cache = new Map();
|
||||
return (file) => {
|
||||
const existing = cache.get(file);
|
||||
if (existing !== undefined)
|
||||
return existing;
|
||||
|
||||
const result = readFileSync(join(FIXTURES_DIR, file));
|
||||
cache.set(file, result);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
const fixtureKey = (() => {
|
||||
const cache = new Map();
|
||||
return (file, passphrase, bypass) => {
|
||||
if (typeof passphrase === 'boolean') {
|
||||
bypass = passphrase;
|
||||
passphrase = undefined;
|
||||
}
|
||||
if (typeof bypass !== 'boolean' || !bypass) {
|
||||
const existing = cache.get(file);
|
||||
if (existing !== undefined)
|
||||
return existing;
|
||||
}
|
||||
const fullPath = join(FIXTURES_DIR, file);
|
||||
const raw = fixture(file);
|
||||
let key = parseKey(raw, passphrase);
|
||||
if (Array.isArray(key))
|
||||
key = key[0];
|
||||
const result = { key, raw, fullPath };
|
||||
cache.set(file, result);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
|
||||
function setupSimple(debug, title) {
|
||||
const { client, server } = setup(title, {
|
||||
client: { username: 'Password User', password: '12345' },
|
||||
server: { hostKeys: [ fixtureKey('ssh_host_rsa_key').raw ] },
|
||||
debug,
|
||||
});
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
ctx.accept();
|
||||
}));
|
||||
}));
|
||||
return { client, server };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fixture,
|
||||
fixtureKey,
|
||||
FIXTURES_DIR,
|
||||
mustCall,
|
||||
mustCallAtLeast,
|
||||
mustNotCall,
|
||||
setup,
|
||||
setupSimple,
|
||||
};
|
||||
Reference in New Issue
Block a user