1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 11:22:43 +02: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:
Mark Stacey 2023-01-26 12:27:46 -03:30 committed by GitHub
parent 506ee16648
commit 001ea4c951
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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 = [
{