A Metamask fork with Infura removed and default networks editable
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
ciphermask/app/scripts/controllers/network/provider-api-tests/shared-tests.js

708 lines
26 KiB

/* eslint-disable jest/require-top-level-describe, jest/no-export, jest/no-identical-title */
import { fill } from 'lodash';
import {
withMockedInfuraCommunications,
withInfuraClient,
buildMockParamsWithoutBlockParamAt,
buildMockParamsWithBlockParamAt,
buildRequestWithReplacedBlockParam,
} from './helpers';
export function testsForRpcMethodNotHandledByMiddleware(
method,
{ numberOfParameters },
) {
it('attempts to pass the request off to Infura', async () => {
const request = {
method,
params: fill(Array(numberOfParameters), 'some value'),
};
const expectedResult = 'the result';
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request,
response: { result: expectedResult },
});
const actualResult = await withInfuraClient(({ makeRpcCall }) =>
makeRpcCall(request),
);
expect(actualResult).toStrictEqual(expectedResult);
});
});
}
/**
* Defines tests which exercise the behavior exhibited by an RPC method which is
* assumed to not take a block parameter. Even if it does, the value of this
* parameter will not be used in determining how to cache the method.
*
* @param method - The name of the RPC method under test.
*/
export function testsForRpcMethodAssumingNoBlockParam(method) {
it('does not hit Infura more than once for identical requests', async () => {
const requests = [{ method }, { method }];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
it('hits Infura and does not reuse the result of a previous request if the latest block number was updated since', async () => {
const requests = [{ method }, { method }];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// Note that we have to mock these requests in a specific order. The
// first block tracker request occurs because of the first RPC request.
// The second block tracker request, however, does not occur because of
// the second RPC request, but rather because we call `clock.runAll()`
// below.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x1' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockNextBlockTrackerRequest({ blockNumber: '0x2' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(async (client) => {
const firstResult = await client.makeRpcCall(requests[0]);
// Proceed to the next iteration of the block tracker so that a new
// block is fetched and the current block is updated.
client.clock.runAll();
const secondResult = await client.makeRpcCall(requests[1]);
return [firstResult, secondResult];
});
expect(results).toStrictEqual(mockResults);
});
});
it.each([null, undefined, '\u003cnil\u003e'])(
'does not reuse the result of a previous request if it was `%s`',
async (emptyValue) => {
const requests = [{ method }, { method }];
const mockResults = [emptyValue, 'some result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
},
);
}
/**
* Defines tests which exercise the behavior exhibited by an RPC method that
* use `blockHash` in the response data to determine whether the response is
* cacheable.
*
* @param method - The name of the RPC method under test.
*/
export function testsForRpcMethodsThatCheckForBlockHashInResponse(method) {
it('does not hit Infura more than once for identical requests and it has a valid blockHash', async () => {
const requests = [{ method }, { method }];
const mockResults = [{ blockHash: '0x100' }, { blockHash: '0x200' }];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
it('hits Infura and does not reuse the result of a previous request if the latest block number was updated since', async () => {
const requests = [{ method }, { method }];
const mockResults = [{ blockHash: '0x100' }, { blockHash: '0x200' }];
await withMockedInfuraCommunications(async (comms) => {
// Note that we have to mock these requests in a specific order. The
// first block tracker request occurs because of the first RPC
// request. The second block tracker request, however, does not occur
// because of the second RPC request, but rather because we call
// `clock.runAll()` below.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x1' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockNextBlockTrackerRequest({ blockNumber: '0x2' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(async (client) => {
const firstResult = await client.makeRpcCall(requests[0]);
// Proceed to the next iteration of the block tracker so that a new
// block is fetched and the current block is updated.
client.clock.runAll();
const secondResult = await client.makeRpcCall(requests[1]);
return [firstResult, secondResult];
});
expect(results).toStrictEqual(mockResults);
});
});
it.each([null, undefined, '\u003cnil\u003e'])(
'does not reuse the result of a previous request if it was `%s`',
async (emptyValue) => {
const requests = [{ method }, { method }];
const mockResults = [emptyValue, { blockHash: '0x100' }];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
},
);
it('does not reuse the result of a previous request if result.blockHash was null', async () => {
const requests = [{ method }, { method }];
const mockResults = [
{ blockHash: null, extra: 'some value' },
{ blockHash: '0x100', extra: 'some other value' },
];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
});
it('does not reuse the result of a previous request if result.blockHash was undefined', async () => {
const requests = [{ method }, { method }];
const mockResults = [
{ extra: 'some value' },
{ blockHash: '0x100', extra: 'some other value' },
];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
});
it('does not reuse the result of a previous request if result.blockHash was "0x0000000000000000000000000000000000000000000000000000000000000000"', async () => {
const requests = [{ method }, { method }];
const mockResults = [
{
blockHash:
'0x0000000000000000000000000000000000000000000000000000000000000000',
extra: 'some value',
},
{ blockHash: '0x100', extra: 'some other value' },
];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
});
}
/**
* Defines tests which exercise the behavior exhibited by an RPC method that
* takes a block parameter. The value of this parameter can be either a block
* number or a block tag ("latest", "earliest", or "pending") and affects how
* the method is cached.
*/
/* eslint-disable-next-line jest/no-export */
export function testsForRpcMethodSupportingBlockParam(
method,
{ blockParamIndex },
) {
describe.each([
['given no block tag', 'none'],
['given a block tag of "latest"', 'latest', 'latest'],
])('%s', (_desc, blockParamType, blockParam) => {
const params =
blockParamType === 'none'
? buildMockParamsWithoutBlockParamAt(blockParamIndex)
: buildMockParamsWithBlockParamAt(blockParamIndex, blockParam);
it('does not hit Infura more than once for identical requests', async () => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the block-cache
// middleware will request the latest block number through the block
// tracker to determine the cache key. Later, the block-ref
// middleware will request the latest block number again to resolve
// the value of "latest", but the block number is cached once made,
// so we only need to mock the request once.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x100' });
// The block-ref middleware will make the request as specified
// except that the block param is replaced with the latest block
// number.
comms.mockSuccessfulInfuraRpcCall({
request: buildRequestWithReplacedBlockParam(
requests[0],
blockParamIndex,
'0x100',
),
response: { result: mockResults[0] },
});
// Note that the block-ref middleware will still allow the original
// request to go through.
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
it('hits Infura and does not reuse the result of a previous request if the latest block number was updated since', async () => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// Note that we have to mock these requests in a specific order.
// The first block tracker request occurs because of the first RPC
// request. The second block tracker request, however, does not
// occur because of the second RPC request, but rather because we
// call `clock.runAll()` below.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x100' });
// The block-ref middleware will make the request as specified
// except that the block param is replaced with the latest block
// number.
comms.mockSuccessfulInfuraRpcCall({
request: buildRequestWithReplacedBlockParam(
requests[0],
blockParamIndex,
'0x100',
),
response: { result: mockResults[0] },
});
// Note that the block-ref middleware will still allow the original
// request to go through.
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockNextBlockTrackerRequest({ blockNumber: '0x200' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
// The previous two requests will happen again, with a different block
// number, in the same order.
comms.mockSuccessfulInfuraRpcCall({
request: buildRequestWithReplacedBlockParam(
requests[0],
blockParamIndex,
'0x200',
),
response: { result: mockResults[1] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(async (client) => {
const firstResult = await client.makeRpcCall(requests[0]);
// Proceed to the next iteration of the block tracker so that a
// new block is fetched and the current block is updated.
client.clock.runAll();
const secondResult = await client.makeRpcCall(requests[1]);
return [firstResult, secondResult];
});
expect(results).toStrictEqual(mockResults);
});
});
it.each([null, undefined, '\u003cnil\u003e'])(
'does not reuse the result of a previous request if it was `%s`',
async (emptyValue) => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = [emptyValue, 'some result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the
// block-cache middleware will request the latest block number
// through the block tracker to determine the cache key. Later,
// the block-ref middleware will request the latest block number
// again to resolve the value of "latest", but the block number is
// cached once made, so we only need to mock the request once.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x100' });
// The block-ref middleware will make the request as specified
// except that the block param is replaced with the latest block
// number.
comms.mockSuccessfulInfuraRpcCall({
request: buildRequestWithReplacedBlockParam(
requests[0],
blockParamIndex,
'0x100',
),
response: { result: mockResults[0] },
});
// Note that the block-ref middleware will still allow the original
// request to go through.
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
// The previous two requests will happen again, in the same order.
comms.mockSuccessfulInfuraRpcCall({
request: buildRequestWithReplacedBlockParam(
requests[0],
blockParamIndex,
'0x100',
),
response: { result: mockResults[1] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
},
);
});
describe.each([
['given a block tag of "earliest"', 'earliest', 'earliest'],
['given a block number', 'block number', '0x100'],
])('%s', (_desc, blockParamType, blockParam) => {
const params = buildMockParamsWithBlockParamAt(blockParamIndex, blockParam);
it('does not hit Infura more than once for identical requests', async () => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the block-cache
// middleware will request the latest block number through the block
// tracker to determine the cache key. This block number doesn't
// matter.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
it('reuses the result of a previous request even if the latest block number was updated since', async () => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// Note that we have to mock these requests in a specific order. The
// first block tracker request occurs because of the first RPC
// request. The second block tracker request, however, does not
// occur because of the second RPC request, but rather because we
// call `clock.runAll()` below.
comms.mockNextBlockTrackerRequest({ blockNumber: '0x1' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockNextBlockTrackerRequest({ blockNumber: '0x2' });
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(async (client) => {
const firstResult = await client.makeRpcCall(requests[0]);
// Proceed to the next iteration of the block tracker so that a
// new block is fetched and the current block is updated.
client.clock.runAll();
const secondResult = await client.makeRpcCall(requests[1]);
return [firstResult, secondResult];
});
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
it.each([null, undefined, '\u003cnil\u003e'])(
'does not reuse the result of a previous request if it was `%s`',
async (emptyValue) => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = [emptyValue, 'some result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
},
);
if (blockParamType === 'earliest') {
it('treats "0x00" as a synonym for "earliest"', async () => {
const requests = [
{
method,
params: buildMockParamsWithBlockParamAt(
blockParamIndex,
blockParam,
),
},
{
method,
params: buildMockParamsWithBlockParamAt(blockParamIndex, '0x00'),
},
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest
// block number is retrieved through the block tracker first. It
// doesn't matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual([mockResults[0], mockResults[0]]);
});
});
}
if (blockParamType === 'block number') {
it('does not reuse the result of a previous request if it was made with different arguments than this one', async () => {
await withMockedInfuraCommunications(async (comms) => {
const requests = [
{
method,
params: buildMockParamsWithBlockParamAt(blockParamIndex, '0x100'),
},
{
method,
params: buildMockParamsWithBlockParamAt(blockParamIndex, '0x200'),
},
];
// The first time a block-cacheable request is made, the latest block
// number is retrieved through the block tracker first. It doesn't
// matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: 'first result' },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: 'second result' },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(['first result', 'second result']);
});
});
}
});
describe('given a block tag of "pending"', () => {
const params = buildMockParamsWithBlockParamAt(blockParamIndex, 'pending');
it('hits Infura on all calls and does not cache anything', async () => {
const requests = [
{ method, params },
{ method, params },
];
const mockResults = ['first result', 'second result'];
await withMockedInfuraCommunications(async (comms) => {
// The first time a block-cacheable request is made, the latest
// block number is retrieved through the block tracker first. It
// doesn't matter what this is — it's just used as a cache key.
comms.mockNextBlockTrackerRequest();
comms.mockSuccessfulInfuraRpcCall({
request: requests[0],
response: { result: mockResults[0] },
});
comms.mockSuccessfulInfuraRpcCall({
request: requests[1],
response: { result: mockResults[1] },
});
const results = await withInfuraClient(({ makeRpcCallsInSeries }) =>
makeRpcCallsInSeries(requests),
);
expect(results).toStrictEqual(mockResults);
});
});
});
}