* Rename existing wrapper, to match contract name * base contract: make member var public * json_schemas.py: stop storing copies of schemas! * .gitignore generated erc20_token.py wrapper * json schemas: allow uppercase digits in address * existing exchange wrapper: re-order methods to match method order in Solidity contract, to reduce noise in upcoming diffs of newly generated code vs. old manually-written code. * existing exchange wrapper: rename method params To match contract method param names * existing exchange wrapper: remove redundant member * existing exchange wrapper: make signatures bytes Not strings. * abi-gen/test-cli: show context on diff failure * abi-gen-templates/Py: fix broken event interface Previous changes had removed the `token_address` parameter from all generated methods, but this instance was missed because there weren't tests/examples using events for the first contract for which wrappers were generated (ERC20Token). * abi-gen: remove unused method parameters * abi-gen: convert Py method params to snake case * abi-gen: rewrite Python tuple handling * python-generated-wrappers: include Exchange * abi-gen-templates/Py: easy linter fixes * abi-gen-templates/Py: satisfy docstring linters * abi-gen-templates/Py: normalize bytes before use * contract_wrappers.py: replace Exchange w/generated * contract_wrappers.py: rm manually written Exchange * contract_wrappers.py/doctest: rename variables * abi-gen: fix misspelling in docstring Co-Authored-By: Fabio B <me@fabioberger.com> * Py docs: error on warning, and test build in CI * abi-gen: doc Py bytes params as requiring UTF-8 * abi-gen: git mv diff.sh test-cli/ * abi-gen: put Py wrapper in module folder, not file This leaves space for user-defined additions to the same module, such as for custom types, as shown herein. * abi-gen: customizable param validation for Python * contract_wrappers.py: JSON schema Order validation * CircleCI Build Artifacts For abi-gen command-line test output, for generated Python contract wrappers as output by abi-gen, for generated Python contract wrappers as reformatted and included in the Python package area, and for the "build" output folder in each Python package, which includes the generated documentation. * CHANGELOG updates for all components * abi-gen: grammar in comments Co-Authored-By: Fabio B <me@fabioberger.com> * abi-gen: CHANGELOG spelling correction Co-Authored-By: Fabio B <me@fabioberger.com> * order_utils.py: reverse (chronological) CHANGELOG * abi-gen-templates: reset CHANGELOG patch version * CHANGELOGs: use multiple entries where appropriate * abi-gen: enable devdoc solc output in test-cli * abi-gen-templates/Py: consolidate return type * abi-gen/test-cli: non-pure fixture contract method Added a method to the "dummy" test fixture contract that isn't pure. All of the other prior method cases were pure. * abi-gen/Py: fix const methods missing return type * abi-gen/Py: fix wrong return types on some methods Specifically, wrapper methods wrapping contract methods that modify contract state and return no return value. There was no test case for this. Now there is. * contract_wrappers.py: rm generated code in `clean` * Parallelize Py monorepo scripts (test, lint, etc)
867 lines
38 KiB
TypeScript
867 lines
38 KiB
TypeScript
import { BigNumber } from '@0x/utils';
|
|
import * as chai from 'chai';
|
|
import * as dirtyChai from 'dirty-chai';
|
|
import forEach = require('lodash.foreach');
|
|
import 'mocha';
|
|
|
|
import { schemas, SchemaValidator } from '../src/index';
|
|
|
|
chai.config.includeStack = true;
|
|
chai.use(dirtyChai);
|
|
const expect = chai.expect;
|
|
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
const {
|
|
numberSchema,
|
|
addressSchema,
|
|
hexSchema,
|
|
orderCancellationRequestsSchema,
|
|
orderFillOrKillRequestsSchema,
|
|
orderFillRequestsSchema,
|
|
orderHashSchema,
|
|
orderSchema,
|
|
signedOrderSchema,
|
|
signedOrdersSchema,
|
|
blockParamSchema,
|
|
blockRangeSchema,
|
|
tokenSchema,
|
|
jsNumber,
|
|
txDataSchema,
|
|
paginatedCollectionSchema,
|
|
relayerApiErrorResponseSchema,
|
|
relayerApiOrderbookResponseSchema,
|
|
relayerApiAssetDataPairsResponseSchema,
|
|
relayerApiOrderConfigPayloadSchema,
|
|
relayerApiOrderConfigResponseSchema,
|
|
relayerApiOrdersChannelSubscribeSchema,
|
|
relayerApiOrdersChannelUpdateSchema,
|
|
relayerApiOrdersResponseSchema,
|
|
relayerApiOrderSchema,
|
|
wholeNumberSchema,
|
|
} = schemas;
|
|
|
|
describe('Schema', () => {
|
|
const validator = new SchemaValidator();
|
|
const validateAgainstSchema = (testCases: any[], schema: any, shouldFail = false) => {
|
|
forEach(testCases, (testCase: any) => {
|
|
const validationResult = validator.validate(testCase, schema);
|
|
const hasErrors = validationResult.errors.length !== 0;
|
|
if (shouldFail) {
|
|
if (!hasErrors) {
|
|
throw new Error(
|
|
`Expected testCase: ${JSON.stringify(testCase, null, '\t')} to fail and it didn't.`,
|
|
);
|
|
}
|
|
} else {
|
|
if (hasErrors) {
|
|
throw new Error(JSON.stringify(validationResult.errors, null, '\t'));
|
|
}
|
|
}
|
|
});
|
|
};
|
|
const paginatedResponse = {
|
|
total: 100,
|
|
perPage: 10,
|
|
page: 3,
|
|
};
|
|
describe('#numberSchema', () => {
|
|
it('should validate valid numbers', () => {
|
|
const testCases = ['42', '0', '1.3', '0.2', '00.00'];
|
|
validateAgainstSchema(testCases, numberSchema);
|
|
});
|
|
it('should fail for invalid numbers', () => {
|
|
const testCases = ['.3', '1.', 'abacaba', 'и', '1..0'];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, numberSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#wholeNumberSchema', () => {
|
|
it('should validate valid numbers', () => {
|
|
const testCases = ['5', '42', '0'];
|
|
validateAgainstSchema(testCases, wholeNumberSchema);
|
|
});
|
|
it('should fail for invalid numbers', () => {
|
|
const testCases = ['1.3', '0.2', '00.00', '.3', '1.', 'abacaba', 'и', '1..0'];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, wholeNumberSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#addressSchema', () => {
|
|
it('should validate valid addresses', () => {
|
|
const testCases = ['0x8b0292b11a196601ed2ce54b665cafeca0347d42', NULL_ADDRESS];
|
|
validateAgainstSchema(testCases, addressSchema);
|
|
});
|
|
it('should fail for invalid addresses', () => {
|
|
const testCases = ['0x', '0', '0x00', '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42'];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, addressSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#hexSchema', () => {
|
|
it('should validate valid hex string', () => {
|
|
const testCases = ['0x8b0292b11a196601ed2ce54b665cafeca0347d42', NULL_ADDRESS];
|
|
validateAgainstSchema(testCases, hexSchema);
|
|
});
|
|
it('should fail for invalid hex string', () => {
|
|
const testCases = ['0', '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42'];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, hexSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#orderHashSchema', () => {
|
|
it('should validate valid order hash', () => {
|
|
const testCases = [
|
|
'0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
|
|
'0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
|
];
|
|
validateAgainstSchema(testCases, orderHashSchema);
|
|
});
|
|
it('should fail for invalid order hash', () => {
|
|
const testCases = [
|
|
{},
|
|
'0x',
|
|
'0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
|
|
'61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, orderHashSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#blockParamSchema', () => {
|
|
it('should validate valid block param', () => {
|
|
const blockNumber = 42;
|
|
const testCases = [blockNumber, 'latest', 'pending', 'earliest'];
|
|
validateAgainstSchema(testCases, blockParamSchema);
|
|
});
|
|
it('should fail for invalid block param', () => {
|
|
const testCases = [{}, '42', 'pemding'];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, blockParamSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#blockRangeSchema', () => {
|
|
it('should validate valid subscription opts', () => {
|
|
const testCases = [{ fromBlock: 42, toBlock: 'latest' }, { fromBlock: 42 }, {}];
|
|
validateAgainstSchema(testCases, blockRangeSchema);
|
|
});
|
|
it('should fail for invalid subscription opts', () => {
|
|
const testCases = [{ fromBlock: '42' }];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, blockRangeSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#tokenSchema', () => {
|
|
const token = {
|
|
name: 'Zero Ex',
|
|
symbol: 'ZRX',
|
|
decimals: 100500,
|
|
address: '0x8b0292b11a196601ed2ce54b665cafeca0347d42',
|
|
url: 'https://0xproject.com',
|
|
};
|
|
it('should validate valid token', () => {
|
|
const testCases = [token];
|
|
validateAgainstSchema(testCases, tokenSchema);
|
|
});
|
|
it('should fail for invalid token', () => {
|
|
const num = 4;
|
|
const testCases = [
|
|
{
|
|
...token,
|
|
address: null,
|
|
},
|
|
{
|
|
...token,
|
|
decimals: undefined,
|
|
},
|
|
[],
|
|
num,
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, tokenSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#paginatedCollectionSchema', () => {
|
|
it('should validate valid paginated collections', () => {
|
|
const testCases = [paginatedResponse];
|
|
validateAgainstSchema(testCases, paginatedCollectionSchema);
|
|
});
|
|
it('should fail for invalid paginated collections', () => {
|
|
const paginatedCollectionNoTotal = {
|
|
page: 10,
|
|
perPage: 2,
|
|
};
|
|
const paginatedCollectionNoPerPage = {
|
|
page: 10,
|
|
total: 100,
|
|
};
|
|
const paginatedCollectionNoPage = {
|
|
total: 10,
|
|
perPage: 20,
|
|
};
|
|
const testCases = [{}, paginatedCollectionNoPage, paginatedCollectionNoPerPage, paginatedCollectionNoTotal];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, paginatedCollectionSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('order including schemas', () => {
|
|
const order = {
|
|
makerAddress: NULL_ADDRESS,
|
|
takerAddress: NULL_ADDRESS,
|
|
senderAddress: NULL_ADDRESS,
|
|
makerFee: '1',
|
|
takerFee: '2',
|
|
makerAssetAmount: '1',
|
|
takerAssetAmount: '2',
|
|
makerAssetData: NULL_ADDRESS,
|
|
takerAssetData: NULL_ADDRESS,
|
|
salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
|
|
feeRecipientAddress: NULL_ADDRESS,
|
|
exchangeAddress: NULL_ADDRESS,
|
|
expirationTimeSeconds: '42',
|
|
};
|
|
const relayerApiOrder = {
|
|
order,
|
|
metaData: {
|
|
someMetaData: 5,
|
|
},
|
|
};
|
|
const relayerApiOrdersResponse = {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
};
|
|
describe('#orderSchema', () => {
|
|
it('should validate valid order', () => {
|
|
const testCases = [order];
|
|
validateAgainstSchema(testCases, orderSchema);
|
|
});
|
|
it('should fail for invalid order', () => {
|
|
const testCases = [
|
|
{
|
|
...order,
|
|
salt: undefined,
|
|
},
|
|
{
|
|
...order,
|
|
salt: 'salt',
|
|
},
|
|
'order',
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, orderSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('signed order including schemas', () => {
|
|
const signedOrder = {
|
|
...order,
|
|
signature:
|
|
'0x031b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
|
};
|
|
describe('#signedOrdersSchema', () => {
|
|
it('should validate valid signed orders', () => {
|
|
const testCases = [[signedOrder], []];
|
|
validateAgainstSchema(testCases, signedOrdersSchema);
|
|
});
|
|
it('should fail for invalid signed orders', () => {
|
|
const testCases = [[signedOrder, 1]];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, signedOrdersSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#signedOrderSchema', () => {
|
|
it('should validate valid signed order', () => {
|
|
const testCases = [signedOrder];
|
|
validateAgainstSchema(testCases, signedOrderSchema);
|
|
});
|
|
it('should fail for invalid signed order', () => {
|
|
const testCases = [
|
|
{
|
|
...signedOrder,
|
|
signature: undefined,
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, signedOrderSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#orderFillOrKillRequestsSchema', () => {
|
|
const orderFillOrKillRequests = [
|
|
{
|
|
signedOrder,
|
|
fillTakerAmount: '5',
|
|
},
|
|
];
|
|
it('should validate valid order fill or kill requests', () => {
|
|
const testCases = [orderFillOrKillRequests];
|
|
validateAgainstSchema(testCases, orderFillOrKillRequestsSchema);
|
|
});
|
|
it('should fail for invalid order fill or kill requests', () => {
|
|
const testCases = [
|
|
[
|
|
{
|
|
...orderFillOrKillRequests[0],
|
|
fillTakerAmount: undefined,
|
|
},
|
|
],
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, orderFillOrKillRequestsSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#orderCancellationRequestsSchema', () => {
|
|
const orderCancellationRequests = [
|
|
{
|
|
order,
|
|
takerTokenCancelAmount: '5',
|
|
},
|
|
];
|
|
it('should validate valid order cancellation requests', () => {
|
|
const testCases = [orderCancellationRequests];
|
|
validateAgainstSchema(testCases, orderCancellationRequestsSchema);
|
|
});
|
|
it('should fail for invalid order cancellation requests', () => {
|
|
const testCases = [
|
|
[
|
|
{
|
|
...orderCancellationRequests[0],
|
|
takerTokenCancelAmount: undefined,
|
|
},
|
|
],
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, orderCancellationRequestsSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#orderFillRequestsSchema', () => {
|
|
const orderFillRequests = [
|
|
{
|
|
signedOrder,
|
|
takerTokenFillAmount: '5',
|
|
},
|
|
];
|
|
it('should validate valid order fill requests', () => {
|
|
const testCases = [orderFillRequests];
|
|
validateAgainstSchema(testCases, orderFillRequestsSchema);
|
|
});
|
|
it('should fail for invalid order fill requests', () => {
|
|
const testCases = [
|
|
[
|
|
{
|
|
...orderFillRequests[0],
|
|
takerTokenFillAmount: undefined,
|
|
},
|
|
],
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, orderFillRequestsSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('standard relayer api schemas', () => {
|
|
describe('#relayerApiOrderSchema', () => {
|
|
it('should validate valid relayer api order', () => {
|
|
const testCases = [relayerApiOrder];
|
|
validateAgainstSchema(testCases, relayerApiOrderSchema);
|
|
});
|
|
it('should fail for invalid relayer api orders', () => {
|
|
const testCases = [{}, order, { order }, { order, metaData: 5 }];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrderSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiErrorResponseSchema', () => {
|
|
it('should validate valid errorResponse', () => {
|
|
const testCases = [
|
|
{
|
|
code: 102,
|
|
reason: 'Order submission disabled',
|
|
},
|
|
{
|
|
code: 101,
|
|
reason: 'Validation failed',
|
|
validationErrors: [
|
|
{
|
|
field: 'maker',
|
|
code: 1002,
|
|
reason: 'Invalid address',
|
|
},
|
|
],
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiErrorResponseSchema);
|
|
});
|
|
it('should fail for invalid error responses', () => {
|
|
const testCases = [
|
|
{},
|
|
{
|
|
code: 102,
|
|
},
|
|
{
|
|
code: '102',
|
|
reason: 'Order submission disabled',
|
|
},
|
|
{
|
|
reason: 'Order submission disabled',
|
|
},
|
|
{
|
|
code: 101,
|
|
reason: 'Validation failed',
|
|
validationErrors: [
|
|
{
|
|
field: 'maker',
|
|
reason: 'Invalid address',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: 101,
|
|
reason: 'Validation failed',
|
|
validationErrors: [
|
|
{
|
|
field: 'maker',
|
|
code: '1002',
|
|
reason: 'Invalid address',
|
|
},
|
|
],
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiErrorResponseSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrderConfigPayloadSchema', () => {
|
|
it('should validate valid fees payloads', () => {
|
|
const testCases = [
|
|
{
|
|
exchangeAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
makerAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
takerAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
makerAssetData: NULL_ADDRESS,
|
|
takerAssetData: NULL_ADDRESS,
|
|
makerAssetAmount: '10000000000000000000',
|
|
takerAssetAmount: '30000000000000000000',
|
|
expirationTimeSeconds: '42',
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrderConfigPayloadSchema);
|
|
});
|
|
it('should fail for invalid fees payloads', () => {
|
|
const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
|
|
const testCases = [
|
|
{},
|
|
{
|
|
makerAssetAmount: '10000000000000000000',
|
|
takerAssetAmount: '30000000000000000000',
|
|
makerAssetData: NULL_ADDRESS,
|
|
takerAssetData: NULL_ADDRESS,
|
|
},
|
|
{
|
|
takerAddress: checksummedAddress,
|
|
makerAssetAmount: '10000000000000000000',
|
|
takerAssetAmount: '30000000000000000000',
|
|
},
|
|
{
|
|
makerAssetAmount: 10000000000000000000,
|
|
takerAssetAmount: 30000000000000000000,
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrderConfigPayloadSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrderConfigResponseSchema', () => {
|
|
it('should validate valid fees responses', () => {
|
|
const testCases = [
|
|
{
|
|
makerFee: '10000000000000000',
|
|
takerFee: '30000000000000000',
|
|
feeRecipientAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
senderAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrderConfigResponseSchema);
|
|
});
|
|
it('should fail for invalid fees responses', () => {
|
|
const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
|
|
const testCases = [
|
|
{},
|
|
{
|
|
makerFee: 10000000000000000,
|
|
takerFee: 30000000000000000,
|
|
},
|
|
{
|
|
feeRecipient: checksummedAddress,
|
|
takerToSpecify: checksummedAddress,
|
|
makerFee: '10000000000000000',
|
|
takerFee: '30000000000000000',
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrderConfigResponseSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerAssetDataPairsResponseSchema', () => {
|
|
it('should validate valid assetPairs response', () => {
|
|
const testCases = [
|
|
{
|
|
...paginatedResponse,
|
|
records: [],
|
|
},
|
|
{
|
|
...paginatedResponse,
|
|
records: [
|
|
{
|
|
assetDataA: {
|
|
assetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
minAmount: '0',
|
|
maxAmount: '10000000000000000000',
|
|
precision: 5,
|
|
},
|
|
assetDataB: {
|
|
assetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
|
minAmount: '0',
|
|
maxAmount: '50000000000000000000',
|
|
precision: 5,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
...paginatedResponse,
|
|
records: [
|
|
{
|
|
assetDataA: {
|
|
assetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
},
|
|
assetDataB: {
|
|
assetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiAssetDataPairsResponseSchema);
|
|
});
|
|
it('should fail for invalid assetPairs responses', () => {
|
|
const testCases = [
|
|
{
|
|
...paginatedResponse,
|
|
records: [
|
|
{
|
|
assetDataA: {
|
|
assetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
minAmount: '0',
|
|
maxAmount: '10000000000000000000',
|
|
precision: 5,
|
|
},
|
|
assetDataC: {
|
|
assetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
|
minAmount: '0',
|
|
maxAmount: '50000000000000000000',
|
|
precision: 5,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
records: [
|
|
{
|
|
assetDataA: {
|
|
assetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
|
minAmount: '0',
|
|
maxAmount: '10000000000000000000',
|
|
precision: 5,
|
|
},
|
|
assetDataB: {
|
|
assetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
|
minAmount: '0',
|
|
maxAmount: '50000000000000000000',
|
|
precision: 5,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
...paginatedResponse,
|
|
records: [
|
|
{
|
|
assetDataA: {
|
|
minAmount: '0',
|
|
maxAmount: '10000000000000000000',
|
|
precision: 5,
|
|
},
|
|
assetDataB: {
|
|
minAmount: '0',
|
|
maxAmount: '50000000000000000000',
|
|
precision: 5,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiAssetDataPairsResponseSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrdersResponseSchema', () => {
|
|
it('should validate valid orders responses', () => {
|
|
const testCases = [
|
|
relayerApiOrdersResponse,
|
|
{
|
|
...paginatedResponse,
|
|
records: [],
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrdersResponseSchema);
|
|
});
|
|
it('should fail for invalid orders responses', () => {
|
|
const testCases = [
|
|
{
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
{
|
|
...paginatedResponse,
|
|
},
|
|
{
|
|
...paginatedResponse,
|
|
records: [{}, relayerApiOrder],
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrdersResponseSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrderbookResponseSchema', () => {
|
|
it('should validate valid order book responses', () => {
|
|
const testCases = [
|
|
{
|
|
bids: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder],
|
|
},
|
|
asks: {
|
|
...paginatedResponse,
|
|
records: [],
|
|
},
|
|
},
|
|
{
|
|
bids: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
asks: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
},
|
|
{
|
|
bids: {
|
|
...paginatedResponse,
|
|
records: [],
|
|
},
|
|
asks: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrderbookResponseSchema);
|
|
});
|
|
it('should fail for invalid order fill requests', () => {
|
|
const testCases = [
|
|
{},
|
|
{
|
|
bids: {
|
|
records: [relayerApiOrder],
|
|
},
|
|
asks: {
|
|
...paginatedResponse,
|
|
records: [],
|
|
},
|
|
},
|
|
{
|
|
bids: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
asks: {},
|
|
},
|
|
{
|
|
bids: {
|
|
...paginatedResponse,
|
|
},
|
|
asks: {
|
|
...paginatedResponse,
|
|
records: [relayerApiOrder, relayerApiOrder],
|
|
},
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrdersResponseSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrdersChannelSubscribeSchema', () => {
|
|
it('should validate valid orders channel websocket subscribe message', () => {
|
|
const testCases = [
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
},
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: {
|
|
makerAssetProxyId: '0x02571792',
|
|
takerAssetProxyId: '0xf47261b0',
|
|
},
|
|
},
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: {},
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrdersChannelSubscribeSchema);
|
|
});
|
|
it('should fail for invalid orders channel websocket subscribe message', () => {
|
|
const bogusAddress = '0xz2b31daCf30a9C50ca473337c01d8A201ae33e32';
|
|
const testCases = [
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
},
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: {
|
|
makerAssetProxyId: '0x02571792',
|
|
takerAssetProxyId: '0xf47261b0',
|
|
makerAssetAddress: bogusAddress,
|
|
},
|
|
},
|
|
{
|
|
type: 'subscribe',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: {
|
|
makerAssetProxyId: 'invalidId',
|
|
},
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrdersChannelSubscribeSchema, shouldFail);
|
|
});
|
|
});
|
|
describe('#relayerApiOrdersChannelUpdateSchema', () => {
|
|
it('should validate valid orders channel websocket update message', () => {
|
|
const testCases = [
|
|
{
|
|
type: 'update',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: [relayerApiOrder],
|
|
},
|
|
{
|
|
type: 'update',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: [],
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, relayerApiOrdersChannelUpdateSchema);
|
|
});
|
|
it('should fail for invalid orders channel websocket update message', () => {
|
|
const testCases = [
|
|
{
|
|
type: 'foo',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
},
|
|
{
|
|
type: 'update',
|
|
channel: 'bar',
|
|
requestId: 2,
|
|
payload: [relayerApiOrder],
|
|
},
|
|
{
|
|
type: 'update',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: {},
|
|
},
|
|
{
|
|
type: 'update',
|
|
channel: 'orders',
|
|
requestId: 'randomId',
|
|
payload: relayerApiErrorResponseSchema,
|
|
},
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, relayerApiOrdersChannelUpdateSchema, shouldFail);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
describe('BigNumber serialization', () => {
|
|
it('should correctly serialize BigNumbers', () => {
|
|
const testCases = {
|
|
'42': '42',
|
|
'0': '0',
|
|
'1.3': '1.3',
|
|
'0.2': '0.2',
|
|
'00.00': '0',
|
|
'.3': '0.3',
|
|
};
|
|
forEach(testCases, (serialized: string, input: string) => {
|
|
expect(JSON.parse(JSON.stringify(new BigNumber(input)))).to.be.equal(serialized);
|
|
});
|
|
});
|
|
});
|
|
describe('#jsNumberSchema', () => {
|
|
it('should validate valid js number', () => {
|
|
// tslint:disable-next-line:custom-no-magic-numbers
|
|
const testCases = [1, 42];
|
|
validateAgainstSchema(testCases, jsNumber);
|
|
});
|
|
it('should fail for invalid js number', () => {
|
|
// tslint:disable-next-line:custom-no-magic-numbers
|
|
const testCases = [NaN, -1, new BigNumber(1)];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, jsNumber, shouldFail);
|
|
});
|
|
});
|
|
describe('#txDataSchema', () => {
|
|
it('should validate valid txData', () => {
|
|
const bigNumGasAmount = new BigNumber(42);
|
|
const testCases = [
|
|
{
|
|
from: NULL_ADDRESS,
|
|
},
|
|
{
|
|
from: NULL_ADDRESS,
|
|
gas: bigNumGasAmount,
|
|
},
|
|
{
|
|
from: NULL_ADDRESS,
|
|
gas: 42,
|
|
},
|
|
];
|
|
validateAgainstSchema(testCases, txDataSchema);
|
|
});
|
|
it('should fail for invalid txData', () => {
|
|
const testCases = [
|
|
{
|
|
gas: new BigNumber(42),
|
|
},
|
|
{},
|
|
[],
|
|
new BigNumber(1),
|
|
];
|
|
const shouldFail = true;
|
|
validateAgainstSchema(testCases, txDataSchema, shouldFail);
|
|
});
|
|
});
|
|
}); // tslint:disable:max-file-line-count
|