metaapi.cloud-sdk
Version:
SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)
97 lines (96 loc) • 13.8 kB
JavaScript
;
import * as testHelpers from './testHelpers';
import { AssertionError } from 'assert';
import util from 'util';
import 'should';
/**
* Asserts that a stub or apy called specified number of times with args matched specified ones
* @param {Function} spy Sinon stub or spy
* @param {Number} calls Expected number of calls
* @param {Array} args Expected call arguments
* @throws {AssertionError} If assertion failed
*/ export function callCountWithMatch(spy, calls, ...args) {
if (!spy.isSinonProxy) {
throw new AssertionError({
message: 'Given function is not a sinon spy'
});
}
let matchedCalls = testHelpers.getCallsWithMatch(spy, ...args);
if (matchedCalls.length !== calls) {
throw Object.assign(new AssertionError({
message: `Spy expected to be called ${calls} times with specified arguments but was called ` + `${matchedCalls.length} times with them of ${spy.callCount} total calls`
}), {
callArgs: util.inspect(spy.args, {
depth: 3
})
});
}
}
/**
* Asserts that a stub or apy called specified number of times with args that deep equal to specified ones
* @param {Function} spy Sinon stub or spy
* @param {Number} calls Expected number of calls
* @param {Array} args Expected call arguments
* @throws {AssertionError} If assertion failed
*/ export function callCountWithExactly(spy, calls, ...args) {
if (!spy.isSinonProxy) {
throw new AssertionError({
message: 'Given function is not a sinon spy'
});
}
let matchedCalls = testHelpers.getCallsWithExactly(spy, ...args);
matchedCalls.length.should.equal(calls, `Spy expected to be called ${calls} times with specified arguments ` + `but was called ${matchedCalls.length} times with them of ${spy.callCount} total calls`);
}
/**
* Asserts that array matches to expected one with and the lengths are equal
* @param {Array} actualArray Actual array to match
* @param {Array} expectedArray Expected array to match to
* @throws {AssertionError} If assertion failed
*/ export function arrayMatchWithEqualLength(actualArray, expectedArray) {
actualArray.should.match(expectedArray);
actualArray.length.should.equal(expectedArray.length);
}
/**
* Sinon's callOrder seems to have some bug giving incorrect assertion, so this is a manual implementation
* @param spyCalls spy calls
* @throws assertion error
*/ export function callOrder(spyCalls) {
let callIds = spyCalls.map((call, index)=>{
const label = call.label || call.spy.name || 'default';
let sinonCall = call.spy.getCall(call.call);
if (!sinonCall) {
throw new AssertionError({
message: `Call ${index} (${label}) does not exist`
});
}
return {
id: sinonCall.callId,
label
};
});
let previousCallId;
for(let index = 0; index < spyCalls.length; ++index){
if (previousCallId !== undefined && callIds[index].id < previousCallId.id) {
throw new AssertionError({
message: `Wrong call order detected in call ids ${callIds.map((id)=>`\n${id.id} (${id.label})`)}`,
expected: `Call id larger than the previous call ${previousCallId}`,
actual: callIds[index]
});
}
if (spyCalls[index].matchArgs) {
try {
spyCalls[index].spy.getCall(spyCalls[index].call).args.should.match(spyCalls[index].matchArgs);
} catch (err) {
throw Object.assign(new AssertionError({
message: `Call ${index} (${callIds[index].label}) args do not match`
}), {
index,
label: callIds[index].label,
cause: err
});
}
}
previousCallId = callIds[index];
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCAqIGFzIHRlc3RIZWxwZXJzIGZyb20gJy4vdGVzdEhlbHBlcnMnO1xuaW1wb3J0IHtBc3NlcnRpb25FcnJvcn0gZnJvbSAnYXNzZXJ0JztcbmltcG9ydCB7RGVlcFBhcnRpYWx9IGZyb20gJy4uLy4uL3R5cGVzL3V0aWwnO1xuaW1wb3J0IHR5cGUgc2lub24gZnJvbSAnc2lub24nO1xuaW1wb3J0IHV0aWwgZnJvbSAndXRpbCc7XG5pbXBvcnQgJ3Nob3VsZCc7XG5cbi8qKlxuICogQXNzZXJ0cyB0aGF0IGEgc3R1YiBvciBhcHkgY2FsbGVkIHNwZWNpZmllZCBudW1iZXIgb2YgdGltZXMgd2l0aCBhcmdzIG1hdGNoZWQgc3BlY2lmaWVkIG9uZXNcbiAqIEBwYXJhbSB7RnVuY3Rpb259IHNweSBTaW5vbiBzdHViIG9yIHNweVxuICogQHBhcmFtIHtOdW1iZXJ9IGNhbGxzIEV4cGVjdGVkIG51bWJlciBvZiBjYWxsc1xuICogQHBhcmFtIHtBcnJheX0gYXJncyBFeHBlY3RlZCBjYWxsIGFyZ3VtZW50c1xuICogQHRocm93cyB7QXNzZXJ0aW9uRXJyb3J9IElmIGFzc2VydGlvbiBmYWlsZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGxDb3VudFdpdGhNYXRjaDxTcHkgZXh0ZW5kcyBzaW5vbi5TaW5vblNweT4oXG4gIHNweTogU3B5LCBjYWxsczogbnVtYmVyLCAuLi5hcmdzOiBEZWVwUGFydGlhbDxQYXJhbWV0ZXJzPFNweT4+XG4pIHtcbiAgaWYgKCEoc3B5IGFzIGFueSkuaXNTaW5vblByb3h5KSB7XG4gICAgdGhyb3cgbmV3IEFzc2VydGlvbkVycm9yKHttZXNzYWdlOiAnR2l2ZW4gZnVuY3Rpb24gaXMgbm90IGEgc2lub24gc3B5J30pO1xuICB9XG4gIGxldCBtYXRjaGVkQ2FsbHMgPSB0ZXN0SGVscGVycy5nZXRDYWxsc1dpdGhNYXRjaChzcHksIC4uLmFyZ3MpO1xuICBpZiAobWF0Y2hlZENhbGxzLmxlbmd0aCAhPT0gY2FsbHMpIHtcbiAgICB0aHJvdyBPYmplY3QuYXNzaWduKFxuICAgICAgbmV3IEFzc2VydGlvbkVycm9yKHttZXNzYWdlOiBgU3B5IGV4cGVjdGVkIHRvIGJlIGNhbGxlZCAke2NhbGxzfSB0aW1lcyB3aXRoIHNwZWNpZmllZCBhcmd1bWVudHMgYnV0IHdhcyBjYWxsZWQgYCArXG4gICAgICAgIGAke21hdGNoZWRDYWxscy5sZW5ndGh9IHRpbWVzIHdpdGggdGhlbSBvZiAke3NweS5jYWxsQ291bnR9IHRvdGFsIGNhbGxzYH0pLFxuICAgICAge2NhbGxBcmdzOiB1dGlsLmluc3BlY3Qoc3B5LmFyZ3MsIHtkZXB0aDogM30pfVxuICAgICk7XG4gIH1cbn1cblxuLyoqXG4gKiBBc3NlcnRzIHRoYXQgYSBzdHViIG9yIGFweSBjYWxsZWQgc3BlY2lmaWVkIG51bWJlciBvZiB0aW1lcyB3aXRoIGFyZ3MgdGhhdCBkZWVwIGVxdWFsIHRvIHNwZWNpZmllZCBvbmVzXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBzcHkgU2lub24gc3R1YiBvciBzcHlcbiAqIEBwYXJhbSB7TnVtYmVyfSBjYWxscyBFeHBlY3RlZCBudW1iZXIgb2YgY2FsbHNcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgRXhwZWN0ZWQgY2FsbCBhcmd1bWVudHNcbiAqIEB0aHJvd3Mge0Fzc2VydGlvbkVycm9yfSBJZiBhc3NlcnRpb24gZmFpbGVkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjYWxsQ291bnRXaXRoRXhhY3RseTxTcHkgZXh0ZW5kcyBzaW5vbi5TaW5vblNweT4oc3B5OiBTcHksIGNhbGxzOiBudW1iZXIsIC4uLmFyZ3M6IFBhcmFtZXRlcnM8U3B5Pikge1xuICBpZiAoIShzcHkgYXMgYW55KS5pc1Npbm9uUHJveHkpIHtcbiAgICB0aHJvdyBuZXcgQXNzZXJ0aW9uRXJyb3Ioe21lc3NhZ2U6ICdHaXZlbiBmdW5jdGlvbiBpcyBub3QgYSBzaW5vbiBzcHknfSk7XG4gIH1cbiAgbGV0IG1hdGNoZWRDYWxscyA9IHRlc3RIZWxwZXJzLmdldENhbGxzV2l0aEV4YWN0bHkoc3B5LCAuLi5hcmdzIGFzIERlZXBQYXJ0aWFsPFBhcmFtZXRlcnM8U3B5Pj4pO1xuICBtYXRjaGVkQ2FsbHMubGVuZ3RoLnNob3VsZC5lcXVhbChjYWxscywgYFNweSBleHBlY3RlZCB0byBiZSBjYWxsZWQgJHtjYWxsc30gdGltZXMgd2l0aCBzcGVjaWZpZWQgYXJndW1lbnRzIGAgK1xuICAgIGBidXQgd2FzIGNhbGxlZCAke21hdGNoZWRDYWxscy5sZW5ndGh9IHRpbWVzIHdpdGggdGhlbSBvZiAke3NweS5jYWxsQ291bnR9IHRvdGFsIGNhbGxzYCk7XG59XG5cbi8qKlxuICogQXNzZXJ0cyB0aGF0IGFycmF5IG1hdGNoZXMgdG8gZXhwZWN0ZWQgb25lIHdpdGggYW5kIHRoZSBsZW5ndGhzIGFyZSBlcXVhbCBcbiAqIEBwYXJhbSB7QXJyYXl9IGFjdHVhbEFycmF5IEFjdHVhbCBhcnJheSB0byBtYXRjaFxuICogQHBhcmFtIHtBcnJheX0gZXhwZWN0ZWRBcnJheSBFeHBlY3RlZCBhcnJheSB0byBtYXRjaCB0b1xuICogQHRocm93cyB7QXNzZXJ0aW9uRXJyb3J9IElmIGFzc2VydGlvbiBmYWlsZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFycmF5TWF0Y2hXaXRoRXF1YWxMZW5ndGgoYWN0dWFsQXJyYXksIGV4cGVjdGVkQXJyYXkpIHtcbiAgYWN0dWFsQXJyYXkuc2hvdWxkLm1hdGNoKGV4cGVjdGVkQXJyYXkpO1xuICBhY3R1YWxBcnJheS5sZW5ndGguc2hvdWxkLmVxdWFsKGV4cGVjdGVkQXJyYXkubGVuZ3RoKTtcbn1cblxuLyoqIFNweSBjYWxsIG9wdGlvbnMgKi9cbmV4cG9ydCB0eXBlIFNweUNhbGw8VEFyZ3MgZXh0ZW5kcyByZWFkb25seSBhbnlbXT4gPSB7XG4gIC8qKiBTaW5vbiBzcHkgKi9cbiAgc3B5OiBzaW5vbi5TaW5vblNweTxUQXJncz4sXG4gIC8qKiBDYWxsIGluZGV4ICovXG4gIGNhbGw6IG51bWJlcixcbiAgLyoqIElmIHNwZWNpZmllZCwgYXNzZXJ0cyBhcmdzIG1hdGNoICovXG4gIG1hdGNoQXJncz86IERlZXBQYXJ0aWFsPFRBcmdzPixcbiAgLyoqIExvZ2dpbmcgbGFiZWwuIERlZmF1bHRzIHRvIGBzcHlgIGZ1bmN0aW9uIG5hbWUsIHdoaWNoIGRlZmF1bHRzIHRvIGBkZWZhdWx0YCAqL1xuICBsYWJlbD86IHN0cmluZ1xufTtcblxuLyoqXG4gKiBTaW5vbidzIGNhbGxPcmRlciBzZWVtcyB0byBoYXZlIHNvbWUgYnVnIGdpdmluZyBpbmNvcnJlY3QgYXNzZXJ0aW9uLCBzbyB0aGlzIGlzIGEgbWFudWFsIGltcGxlbWVudGF0aW9uXG4gKiBAcGFyYW0gc3B5Q2FsbHMgc3B5IGNhbGxzXG4gKiBAdGhyb3dzIGFzc2VydGlvbiBlcnJvclxuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsbE9yZGVyKHNweUNhbGxzOiBTcHlDYWxsPGFueT5bXSkge1xuICB0eXBlIENhbGxJZCA9IHtpZDogbnVtYmVyLCBsYWJlbDogc3RyaW5nfTtcbiAgbGV0IGNhbGxJZHM6IENhbGxJZFtdID0gc3B5Q2FsbHMubWFwKChjYWxsLCBpbmRleCkgPT4ge1xuICAgIGNvbnN0IGxhYmVsID0gY2FsbC5sYWJlbCB8fCBjYWxsLnNweS5uYW1lIHx8ICdkZWZhdWx0JztcbiAgICBsZXQgc2lub25DYWxsID0gY2FsbC5zcHkuZ2V0Q2FsbChjYWxsLmNhbGwpO1xuICAgIGlmICghc2lub25DYWxsKSB7XG4gICAgICB0aHJvdyBuZXcgQXNzZXJ0aW9uRXJyb3Ioe21lc3NhZ2U6IGBDYWxsICR7aW5kZXh9ICgke2xhYmVsfSkgZG9lcyBub3QgZXhpc3RgfSk7XG4gICAgfVxuICAgIHJldHVybiB7aWQ6IChzaW5vbkNhbGwgYXMgYW55KS5jYWxsSWQsIGxhYmVsfTtcbiAgfSk7XG4gIGxldCBwcmV2aW91c0NhbGxJZDogQ2FsbElkO1xuICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgc3B5Q2FsbHMubGVuZ3RoOyArK2luZGV4KSB7XG4gICAgaWYgKHByZXZpb3VzQ2FsbElkICE9PSB1bmRlZmluZWQgJiYgY2FsbElkc1tpbmRleF0uaWQgPCBwcmV2aW91c0NhbGxJZC5pZCkge1xuICAgICAgdGhyb3cgbmV3IEFzc2VydGlvbkVycm9yKHtcbiAgICAgICAgbWVzc2FnZTogYFdyb25nIGNhbGwgb3JkZXIgZGV0ZWN0ZWQgaW4gY2FsbCBpZHMgJHtjYWxsSWRzLm1hcChpZCA9PiBgXFxuJHtpZC5pZH0gKCR7aWQubGFiZWx9KWApfWAsXG4gICAgICAgIGV4cGVjdGVkOiBgQ2FsbCBpZCBsYXJnZXIgdGhhbiB0aGUgcHJldmlvdXMgY2FsbCAke3ByZXZpb3VzQ2FsbElkfWAsXG4gICAgICAgIGFjdHVhbDogY2FsbElkc1tpbmRleF1cbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAoc3B5Q2FsbHNbaW5kZXhdLm1hdGNoQXJncykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgc3B5Q2FsbHNbaW5kZXhdLnNweS5nZXRDYWxsKHNweUNhbGxzW2luZGV4XS5jYWxsKS5hcmdzLnNob3VsZC5tYXRjaChzcHlDYWxsc1tpbmRleF0ubWF0Y2hBcmdzKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICB0aHJvdyBPYmplY3QuYXNzaWduKFxuICAgICAgICAgIG5ldyBBc3NlcnRpb25FcnJvcih7bWVzc2FnZTogYENhbGwgJHtpbmRleH0gKCR7Y2FsbElkc1tpbmRleF0ubGFiZWx9KSBhcmdzIGRvIG5vdCBtYXRjaGB9KSxcbiAgICAgICAgICB7aW5kZXgsIGxhYmVsOiBjYWxsSWRzW2luZGV4XS5sYWJlbCwgY2F1c2U6IGVycn1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcHJldmlvdXNDYWxsSWQgPSBjYWxsSWRzW2luZGV4XTtcbiAgfVxufVxuIl0sIm5hbWVzIjpbInRlc3RIZWxwZXJzIiwiQXNzZXJ0aW9uRXJyb3IiLCJ1dGlsIiwiY2FsbENvdW50V2l0aE1hdGNoIiwic3B5IiwiY2FsbHMiLCJhcmdzIiwiaXNTaW5vblByb3h5IiwibWVzc2FnZSIsIm1hdGNoZWRDYWxscyIsImdldENhbGxzV2l0aE1hdGNoIiwibGVuZ3RoIiwiT2JqZWN0IiwiYXNzaWduIiwiY2FsbENvdW50IiwiY2FsbEFyZ3MiLCJpbnNwZWN0IiwiZGVwdGgiLCJjYWxsQ291bnRXaXRoRXhhY3RseSIsImdldENhbGxzV2l0aEV4YWN0bHkiLCJzaG91bGQiLCJlcXVhbCIsImFycmF5TWF0Y2hXaXRoRXF1YWxMZW5ndGgiLCJhY3R1YWxBcnJheSIsImV4cGVjdGVkQXJyYXkiLCJtYXRjaCIsImNhbGxPcmRlciIsInNweUNhbGxzIiwiY2FsbElkcyIsIm1hcCIsImNhbGwiLCJpbmRleCIsImxhYmVsIiwibmFtZSIsInNpbm9uQ2FsbCIsImdldENhbGwiLCJpZCIsImNhbGxJZCIsInByZXZpb3VzQ2FsbElkIiwidW5kZWZpbmVkIiwiZXhwZWN0ZWQiLCJhY3R1YWwiLCJtYXRjaEFyZ3MiLCJlcnIiLCJjYXVzZSJdLCJtYXBwaW5ncyI6IkFBQUE7QUFFQSxZQUFZQSxpQkFBaUIsZ0JBQWdCO0FBQzdDLFNBQVFDLGNBQWMsUUFBTyxTQUFTO0FBR3RDLE9BQU9DLFVBQVUsT0FBTztBQUN4QixPQUFPLFNBQVM7QUFFaEI7Ozs7OztDQU1DLEdBQ0QsT0FBTyxTQUFTQyxtQkFDZEMsR0FBUSxFQUFFQyxLQUFhLEVBQUUsR0FBR0MsSUFBa0M7SUFFOUQsSUFBSSxDQUFDLEFBQUNGLElBQVlHLFlBQVksRUFBRTtRQUM5QixNQUFNLElBQUlOLGVBQWU7WUFBQ08sU0FBUztRQUFtQztJQUN4RTtJQUNBLElBQUlDLGVBQWVULFlBQVlVLGlCQUFpQixDQUFDTixRQUFRRTtJQUN6RCxJQUFJRyxhQUFhRSxNQUFNLEtBQUtOLE9BQU87UUFDakMsTUFBTU8sT0FBT0MsTUFBTSxDQUNqQixJQUFJWixlQUFlO1lBQUNPLFNBQVMsQ0FBQywwQkFBMEIsRUFBRUgsTUFBTSwrQ0FBK0MsQ0FBQyxHQUM5RyxDQUFDLEVBQUVJLGFBQWFFLE1BQU0sQ0FB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