mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
273 lines
9.9 KiB
JavaScript
273 lines
9.9 KiB
JavaScript
|
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
||
|
|
||
|
import { withMockedCommunications, withNetworkClient } from './helpers';
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
* @param additionalArgs - Additional arguments.
|
||
|
* @param additionalArgs.numberOfParameters - The number of parameters supported
|
||
|
* by the method under test.
|
||
|
* @param additionalArgs.providerType - The type of provider being tested;
|
||
|
* either `infura` or `custom` (default: "infura").
|
||
|
*/
|
||
|
export function testsForRpcMethodsThatCheckForBlockHashInResponse(
|
||
|
method,
|
||
|
{ numberOfParameters, providerType },
|
||
|
) {
|
||
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||
|
throw new Error(
|
||
|
`providerType must be either "infura" or "custom", was "${providerType}" instead`,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
it('does not hit the RPC endpoint more than once for identical requests and it has a valid blockHash', async () => {
|
||
|
const requests = [{ method }, { method }];
|
||
|
const mockResult = { blockHash: '0x1' };
|
||
|
|
||
|
await withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResult },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ makeRpcCallsInSeries }) => makeRpcCallsInSeries(requests),
|
||
|
);
|
||
|
|
||
|
expect(results).toStrictEqual([mockResult, mockResult]);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('hits the RPC endpoint 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 withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockNextBlockTrackerRequest({ blockNumber: '0x2' });
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
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('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 withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ 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 withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ 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 withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ makeRpcCallsInSeries }) => makeRpcCallsInSeries(requests),
|
||
|
);
|
||
|
|
||
|
expect(results).toStrictEqual(mockResults);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
for (const emptyValue of [null, undefined, '\u003cnil\u003e']) {
|
||
|
it(`does not retry an empty response of "${emptyValue}"`, async () => {
|
||
|
const request = { method };
|
||
|
const mockResult = emptyValue;
|
||
|
|
||
|
await withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request,
|
||
|
response: { result: mockResult },
|
||
|
});
|
||
|
|
||
|
const result = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ makeRpcCall }) => makeRpcCall(request),
|
||
|
);
|
||
|
|
||
|
expect(result).toStrictEqual(mockResult);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it(`does not reuse the result of a previous request if it was "${emptyValue}"`, async () => {
|
||
|
const requests = [{ method }, { method }];
|
||
|
const mockResults = [emptyValue, { blockHash: '0x100' }];
|
||
|
|
||
|
await withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ makeRpcCallsInSeries }) => makeRpcCallsInSeries(requests),
|
||
|
);
|
||
|
|
||
|
expect(results).toStrictEqual(mockResults);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
for (const paramIndex of [...Array(numberOfParameters).keys()]) {
|
||
|
it(`does not reuse the result of a previous request with a valid blockHash if parameter at index "${paramIndex}" differs`, async () => {
|
||
|
const firstMockParams = [
|
||
|
...new Array(numberOfParameters).fill('some value'),
|
||
|
];
|
||
|
const secondMockParams = firstMockParams.slice();
|
||
|
secondMockParams[paramIndex] = 'another value';
|
||
|
const requests = [
|
||
|
{
|
||
|
method,
|
||
|
params: firstMockParams,
|
||
|
},
|
||
|
{ method, params: secondMockParams },
|
||
|
];
|
||
|
const mockResults = [{ blockHash: '0x100' }, { blockHash: '0x200' }];
|
||
|
|
||
|
await withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||
|
request: requests[0],
|
||
|
response: { result: mockResults[0] },
|
||
|
});
|
||
|
comms.mockRpcCall({
|
||
|
request: requests[1],
|
||
|
response: { result: mockResults[1] },
|
||
|
});
|
||
|
|
||
|
const results = await withNetworkClient(
|
||
|
{ providerType },
|
||
|
({ makeRpcCallsInSeries }) => makeRpcCallsInSeries(requests),
|
||
|
);
|
||
|
|
||
|
expect(results).toStrictEqual([mockResults[0], mockResults[1]]);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|