asset-swapper: Mockable axios for QuoteRequestor (#2549)
* Mockable axios for QuoteRequestor * Move RFQT Mocker to src * move MockedRfqtFirmQuoteResponse into types file * fix import
This commit is contained in:
parent
0cb5e4553b
commit
84adbcb683
@ -55,6 +55,7 @@
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"axios": "^0.19.2",
|
||||
"axios-mock-adapter": "^1.18.1",
|
||||
"heartbeats": "^5.0.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
|
@ -44,6 +44,7 @@ export {
|
||||
MarketBuySwapQuote,
|
||||
MarketOperation,
|
||||
MarketSellSwapQuote,
|
||||
MockedRfqtFirmQuoteResponse,
|
||||
RfqtFirmQuoteRequestOpts,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumerBase,
|
||||
@ -67,3 +68,4 @@ export {
|
||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||
export { QuoteRequestor } from './utils/quote_requestor';
|
||||
export { rfqtMocker } from './utils/rfqt_mocker';
|
||||
|
@ -277,3 +277,16 @@ export enum OrderPrunerPermittedFeeTypes {
|
||||
export interface RfqtFirmQuoteRequestOpts {
|
||||
makerEndpointMaxResponseTimeMs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a mocked RFQT maker responses.
|
||||
*/
|
||||
export interface MockedRfqtFirmQuoteResponse {
|
||||
endpoint: string;
|
||||
requestApiKey: string;
|
||||
requestParams: {
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
responseData: any;
|
||||
responseCode: number;
|
||||
}
|
||||
|
@ -68,10 +68,9 @@ export class QuoteRequestor {
|
||||
});
|
||||
} catch (err) {
|
||||
logUtils.warn(
|
||||
`Failed to get RFQ-T quote from market maker endpoint ${rfqtMakerEndpoint} for API key ${takerApiKey} for taker address ${takerAddress}: ${JSON.stringify(
|
||||
err,
|
||||
)}`,
|
||||
`Failed to get RFQ-T quote from market maker endpoint ${rfqtMakerEndpoint} for API key ${takerApiKey} for taker address ${takerAddress}`,
|
||||
);
|
||||
logUtils.warn(err);
|
||||
return undefined;
|
||||
}
|
||||
}),
|
||||
|
37
packages/asset-swapper/src/utils/rfqt_mocker.ts
Normal file
37
packages/asset-swapper/src/utils/rfqt_mocker.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import axios from 'axios';
|
||||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import { MockedRfqtFirmQuoteResponse } from '../types';
|
||||
|
||||
/**
|
||||
* A helper utility for testing which mocks out
|
||||
* requests to RFQ-t providers
|
||||
*/
|
||||
export const rfqtMocker = {
|
||||
/**
|
||||
* Stubs out responses from RFQ-T providers by mocking out
|
||||
* HTTP calls via axios. Always restores the mock adapter
|
||||
* after executing the `performFn`.
|
||||
*/
|
||||
withMockedRfqtFirmQuotes: async (
|
||||
mockedResponses: MockedRfqtFirmQuoteResponse[],
|
||||
performFn: () => Promise<void>,
|
||||
) => {
|
||||
const mockedAxios = new AxiosMockAdapter(axios);
|
||||
try {
|
||||
// Mock out RFQT responses
|
||||
for (const mockedResponse of mockedResponses) {
|
||||
const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse;
|
||||
const requestHeaders = { Accept: 'application/json, text/plain, */*', '0x-api-key': requestApiKey };
|
||||
mockedAxios
|
||||
.onGet(`${endpoint}/quote`, { params: requestParams }, requestHeaders)
|
||||
.replyOnce(responseCode, responseData);
|
||||
}
|
||||
|
||||
await performFn();
|
||||
} finally {
|
||||
// Ensure we always restore axios afterwards
|
||||
mockedAxios.restore();
|
||||
}
|
||||
},
|
||||
};
|
79
packages/asset-swapper/test/quote_requestor_test.ts
Normal file
79
packages/asset-swapper/test/quote_requestor_test.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { tokenUtils } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { StatusCodes } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { MarketOperation, MockedRfqtFirmQuoteResponse } from '../src/types';
|
||||
import { QuoteRequestor } from '../src/utils/quote_requestor';
|
||||
import { rfqtMocker } from '../src/utils/rfqt_mocker';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { testOrderFactory } from './utils/test_order_factory';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('QuoteRequestor', async () => {
|
||||
const [makerToken, takerToken] = tokenUtils.getDummyERC20TokenAddresses();
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerToken);
|
||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerToken);
|
||||
|
||||
describe('requestRfqtFirmQuotesAsync', async () => {
|
||||
it('should return successful RFQT requests', async () => {
|
||||
const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a';
|
||||
const takerApiKey = 'my-ko0l-api-key';
|
||||
|
||||
// Set up RFQT responses
|
||||
// tslint:disable-next-line:array-type
|
||||
const mockedRequests: MockedRfqtFirmQuoteResponse[] = [];
|
||||
const expectedParams = {
|
||||
sellToken: takerToken,
|
||||
buyToken: makerToken,
|
||||
sellAmount: '10000',
|
||||
buyAmount: undefined,
|
||||
takerAddress,
|
||||
};
|
||||
// Successful response
|
||||
const mockedOrder1 = testOrderFactory.generateTestSignedOrder({});
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://1337.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: mockedOrder1,
|
||||
responseCode: StatusCodes.Success,
|
||||
});
|
||||
// Test out a bad response code, ensure it doesnt cause throw
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://420.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: { error: 'bad request' },
|
||||
responseCode: StatusCodes.InternalError,
|
||||
});
|
||||
// Another Successful response
|
||||
const mockedOrder3 = testOrderFactory.generateTestSignedOrder({});
|
||||
mockedRequests.push({
|
||||
endpoint: 'https://37.0.0.1',
|
||||
requestApiKey: takerApiKey,
|
||||
requestParams: expectedParams,
|
||||
responseData: mockedOrder3,
|
||||
responseCode: StatusCodes.Success,
|
||||
});
|
||||
|
||||
return rfqtMocker.withMockedRfqtFirmQuotes(mockedRequests, async () => {
|
||||
const qr = new QuoteRequestor(['https://1337.0.0.1', 'https://420.0.0.1', 'https://37.0.0.1']);
|
||||
const resp = await qr.requestRfqtFirmQuotesAsync(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
new BigNumber(10000),
|
||||
MarketOperation.Sell,
|
||||
takerApiKey,
|
||||
takerAddress,
|
||||
);
|
||||
expect(resp.sort()).to.eql([mockedOrder1, mockedOrder3].sort());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
27
yarn.lock
27
yarn.lock
@ -3069,6 +3069,13 @@ aws4@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
||||
|
||||
axios-mock-adapter@^1.18.1:
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.18.1.tgz#a2ba2638ef513d954793f96bde3e26bd4a1b7940"
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
is-buffer "^2.0.3"
|
||||
|
||||
axios@^0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
|
||||
@ -3076,6 +3083,12 @@ axios@^0.18.0:
|
||||
follow-redirects "^1.3.0"
|
||||
is-buffer "^1.1.5"
|
||||
|
||||
axios@^0.19.2:
|
||||
version "0.19.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
|
||||
dependencies:
|
||||
follow-redirects "1.5.10"
|
||||
|
||||
babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||
@ -7353,6 +7366,10 @@ fast-deep-equal@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
|
||||
fast-diff@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
@ -7659,6 +7676,12 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.0.4"
|
||||
|
||||
follow-redirects@1.5.10:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
|
||||
dependencies:
|
||||
debug "=3.1.0"
|
||||
|
||||
follow-redirects@^1.3.0:
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.8.tgz#1dbfe13e45ad969f813e86c00e5296f525c885a1"
|
||||
@ -9133,6 +9156,10 @@ is-buffer@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
|
||||
is-buffer@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
|
||||
|
||||
is-buffer@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
|
||||
|
Loading…
x
Reference in New Issue
Block a user