mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Test parameter change cache miss (#17346)
Tests have been added to ensure that our middleware will not return a cached response to a request with unique parameters. Each parameter supported by each method is tested independently. Closes #17003
This commit is contained in:
parent
506ee16648
commit
001ea4c951
@ -143,65 +143,104 @@ export function testsForProviderType(providerType) {
|
||||
|
||||
describe('methods that have a param to specify the block', () => {
|
||||
const supportingBlockParam = [
|
||||
{ name: 'eth_call', blockParamIndex: 1 },
|
||||
{ name: 'eth_getBalance', blockParamIndex: 1 },
|
||||
{ name: 'eth_getBlockByNumber', blockParamIndex: 0 },
|
||||
{ name: 'eth_getCode', blockParamIndex: 1 },
|
||||
{ name: 'eth_getStorageAt', blockParamIndex: 2 },
|
||||
{ name: 'eth_getTransactionCount', blockParamIndex: 1 },
|
||||
{
|
||||
name: 'eth_call',
|
||||
blockParamIndex: 1,
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
{
|
||||
name: 'eth_getBalance',
|
||||
blockParamIndex: 1,
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
{
|
||||
name: 'eth_getBlockByNumber',
|
||||
blockParamIndex: 0,
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
{ name: 'eth_getCode', blockParamIndex: 1, numberOfParameters: 2 },
|
||||
{
|
||||
name: 'eth_getStorageAt',
|
||||
blockParamIndex: 2,
|
||||
numberOfParameters: 3,
|
||||
},
|
||||
{
|
||||
name: 'eth_getTransactionCount',
|
||||
blockParamIndex: 1,
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
];
|
||||
supportingBlockParam.forEach(({ name, blockParamIndex }) => {
|
||||
describe(`method name: ${name}`, () => {
|
||||
testsForRpcMethodSupportingBlockParam(name, {
|
||||
providerType,
|
||||
blockParamIndex,
|
||||
supportingBlockParam.forEach(
|
||||
({ name, blockParamIndex, numberOfParameters }) => {
|
||||
describe(`method name: ${name}`, () => {
|
||||
testsForRpcMethodSupportingBlockParam(name, {
|
||||
providerType,
|
||||
blockParamIndex,
|
||||
numberOfParameters,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('methods that assume there is no block param', () => {
|
||||
const assumingNoBlockParam = [
|
||||
'eth_blockNumber',
|
||||
'eth_estimateGas',
|
||||
'eth_gasPrice',
|
||||
'eth_getBlockByHash',
|
||||
{ name: 'eth_blockNumber', numberOfParameters: 0 },
|
||||
{ name: 'eth_estimateGas', numberOfParameters: 2 },
|
||||
{ name: 'eth_gasPrice', numberOfParameters: 0 },
|
||||
{ name: 'eth_getBlockByHash', numberOfParameters: 2 },
|
||||
// NOTE: eth_getBlockTransactionCountByNumber does take a block param at
|
||||
// the 0th index, but this is not handled by our cache middleware
|
||||
// currently
|
||||
'eth_getBlockTransactionCountByNumber',
|
||||
{
|
||||
name: 'eth_getBlockTransactionCountByNumber',
|
||||
numberOfParameters: 1,
|
||||
},
|
||||
// NOTE: eth_getTransactionByBlockNumberAndIndex does take a block param
|
||||
// at the 0th index, but this is not handled by our cache middleware
|
||||
// currently
|
||||
'eth_getTransactionByBlockNumberAndIndex',
|
||||
'eth_getBlockTransactionCountByHash',
|
||||
'eth_getFilterLogs',
|
||||
'eth_getTransactionByBlockHashAndIndex',
|
||||
'eth_getUncleByBlockHashAndIndex',
|
||||
{
|
||||
name: 'eth_getTransactionByBlockNumberAndIndex',
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
{
|
||||
name: 'eth_getBlockTransactionCountByHash',
|
||||
numberOfParameters: 1,
|
||||
},
|
||||
{ name: 'eth_getFilterLogs', numberOfParameters: 1 },
|
||||
{
|
||||
name: 'eth_getTransactionByBlockHashAndIndex',
|
||||
numberOfParameters: 2,
|
||||
},
|
||||
{ name: 'eth_getUncleByBlockHashAndIndex', numberOfParameters: 2 },
|
||||
// NOTE: eth_getUncleByBlockNumberAndIndex does take a block param at
|
||||
// the 0th index, but this is not handled by our cache middleware
|
||||
// currently
|
||||
'eth_getUncleByBlockNumberAndIndex',
|
||||
'eth_getUncleCountByBlockHash',
|
||||
{ name: 'eth_getUncleByBlockNumberAndIndex', numberOfParameters: 2 },
|
||||
{ name: 'eth_getUncleCountByBlockHash', numberOfParameters: 1 },
|
||||
// NOTE: eth_getUncleCountByBlockNumber does take a block param at the
|
||||
// 0th index, but this is not handled by our cache middleware currently
|
||||
'eth_getUncleCountByBlockNumber',
|
||||
{ name: 'eth_getUncleCountByBlockNumber', numberOfParameters: 1 },
|
||||
];
|
||||
assumingNoBlockParam.forEach((name) =>
|
||||
assumingNoBlockParam.forEach(({ name, numberOfParameters }) =>
|
||||
describe(`method name: ${name}`, () => {
|
||||
testsForRpcMethodAssumingNoBlockParam(name, { providerType });
|
||||
testsForRpcMethodAssumingNoBlockParam(name, {
|
||||
providerType,
|
||||
numberOfParameters,
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe('methods with block hashes in their result', () => {
|
||||
const methodsWithBlockHashInResponse = [
|
||||
'eth_getTransactionByHash',
|
||||
'eth_getTransactionReceipt',
|
||||
{ name: 'eth_getTransactionByHash', numberOfParameters: 1 },
|
||||
{ name: 'eth_getTransactionReceipt', numberOfParameters: 1 },
|
||||
];
|
||||
methodsWithBlockHashInResponse.forEach((method) => {
|
||||
describe(`method name: ${method}`, () => {
|
||||
testsForRpcMethodsThatCheckForBlockHashInResponse(method, {
|
||||
methodsWithBlockHashInResponse.forEach(({ name, numberOfParameters }) => {
|
||||
describe(`method name: ${name}`, () => {
|
||||
testsForRpcMethodsThatCheckForBlockHashInResponse(name, {
|
||||
numberOfParameters,
|
||||
providerType,
|
||||
});
|
||||
});
|
||||
@ -306,12 +345,15 @@ export function testsForProviderType(providerType) {
|
||||
|
||||
describe('methods that assume there is no block param', () => {
|
||||
const assumingNoBlockParam = [
|
||||
'eth_protocolVersion',
|
||||
'web3_clientVersion',
|
||||
{ name: 'eth_protocolVersion', numberOfParameters: 0 },
|
||||
{ name: 'web3_clientVersion', numberOfParameters: 0 },
|
||||
];
|
||||
assumingNoBlockParam.forEach((name) =>
|
||||
assumingNoBlockParam.forEach(({ name, numberOfParameters }) =>
|
||||
describe(`method name: ${name}`, () => {
|
||||
testsForRpcMethodAssumingNoBlockParam(name, { providerType });
|
||||
testsForRpcMethodAssumingNoBlockParam(name, {
|
||||
providerType,
|
||||
numberOfParameters,
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
@ -416,12 +458,13 @@ export function testsForRpcMethodNotHandledByMiddleware(
|
||||
*
|
||||
* @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 testsForRpcMethodAssumingNoBlockParam(
|
||||
method,
|
||||
{ providerType },
|
||||
{ numberOfParameters, providerType },
|
||||
) {
|
||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||
throw new Error(
|
||||
@ -452,6 +495,46 @@ export function testsForRpcMethodAssumingNoBlockParam(
|
||||
});
|
||||
});
|
||||
|
||||
for (const paramIndex of [...Array(numberOfParameters).keys()]) {
|
||||
it(`does not reuse the result of a previous request 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 = ['some result', 'another result'];
|
||||
|
||||
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]]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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 = ['first result', 'second result'];
|
||||
@ -1307,12 +1390,13 @@ export function testsForRpcMethodAssumingNoBlockParam(
|
||||
*
|
||||
* @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,
|
||||
{ providerType },
|
||||
{ numberOfParameters, providerType },
|
||||
) {
|
||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||
throw new Error(
|
||||
@ -1343,6 +1427,46 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse(
|
||||
});
|
||||
});
|
||||
|
||||
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]]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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' }];
|
||||
@ -1513,13 +1637,15 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse(
|
||||
*
|
||||
* @param method - The name of the RPC method under test.
|
||||
* @param additionalArgs - Additional arguments.
|
||||
* @param additionalArgs.providerType - The type of provider being tested;
|
||||
* @param additionalArgs.blockParamIndex - The index of the block parameter.
|
||||
* @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").
|
||||
*/
|
||||
/* eslint-disable-next-line jest/no-export */
|
||||
export function testsForRpcMethodSupportingBlockParam(
|
||||
method,
|
||||
{ providerType, blockParamIndex },
|
||||
{ blockParamIndex, numberOfParameters, providerType },
|
||||
) {
|
||||
describe.each([
|
||||
['given no block tag', undefined],
|
||||
@ -1564,6 +1690,65 @@ export function testsForRpcMethodSupportingBlockParam(
|
||||
});
|
||||
});
|
||||
|
||||
for (const paramIndex of [...Array(numberOfParameters).keys()]) {
|
||||
if (paramIndex === blockParamIndex) {
|
||||
// testing changes in block param is covered under later tests
|
||||
continue;
|
||||
}
|
||||
it(`does not reuse the result of a previous request if parameter at index "${paramIndex}" differs`, async () => {
|
||||
const firstMockParams = [
|
||||
...new Array(numberOfParameters).fill('some value'),
|
||||
];
|
||||
firstMockParams[blockParamIndex] = blockParam;
|
||||
const secondMockParams = firstMockParams.slice();
|
||||
secondMockParams[paramIndex] = 'another value';
|
||||
const requests = [
|
||||
{
|
||||
method,
|
||||
params: firstMockParams,
|
||||
},
|
||||
{ method, params: secondMockParams },
|
||||
];
|
||||
const mockResults = ['first result', 'second result'];
|
||||
|
||||
await withMockedCommunications({ providerType }, 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.mockRpcCall({
|
||||
request: buildRequestWithReplacedBlockParam(
|
||||
requests[0],
|
||||
blockParamIndex,
|
||||
'0x100',
|
||||
),
|
||||
response: { result: mockResults[0] },
|
||||
});
|
||||
comms.mockRpcCall({
|
||||
request: buildRequestWithReplacedBlockParam(
|
||||
requests[1],
|
||||
blockParamIndex,
|
||||
'0x100',
|
||||
),
|
||||
response: { result: mockResults[1] },
|
||||
});
|
||||
|
||||
const results = await withNetworkClient(
|
||||
{ providerType },
|
||||
({ makeRpcCallsInSeries }) => makeRpcCallsInSeries(requests),
|
||||
);
|
||||
|
||||
expect(results).toStrictEqual([mockResults[0], mockResults[1]]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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, params: buildMockParams({ blockParamIndex, blockParam }) },
|
||||
@ -2813,6 +2998,57 @@ export function testsForRpcMethodSupportingBlockParam(
|
||||
});
|
||||
});
|
||||
|
||||
for (const paramIndex of [...Array(numberOfParameters).keys()]) {
|
||||
if (paramIndex === blockParamIndex) {
|
||||
// testing changes in block param is covered under later tests
|
||||
continue;
|
||||
}
|
||||
it(`does not reuse the result of a previous request if parameter at index "${paramIndex}" differs`, async () => {
|
||||
const firstMockParams = [
|
||||
...new Array(numberOfParameters).fill('some value'),
|
||||
];
|
||||
firstMockParams[blockParamIndex] = blockParam;
|
||||
const secondMockParams = firstMockParams.slice();
|
||||
secondMockParams[paramIndex] = 'another value';
|
||||
const requests = [
|
||||
{
|
||||
method,
|
||||
params: firstMockParams,
|
||||
},
|
||||
{ method, params: secondMockParams },
|
||||
];
|
||||
const mockResults = ['first result', 'second result'];
|
||||
|
||||
await withMockedCommunications({ providerType }, 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.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]]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('reuses the result of a previous request even if the latest block number was updated since', async () => {
|
||||
const requests = [
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user