Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/PollingBlockTracker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@

await withPollingBlockTracker(async ({ blockTracker }) => {
expect(blockTracker.isRunning()).toBe(false);
blockTracker.getLatestBlock();

Check warning on line 177 in src/PollingBlockTracker.test.ts

View workflow job for this annotation

GitHub Actions / Build, Lint, and Test (20.x)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator

Check warning on line 177 in src/PollingBlockTracker.test.ts

View workflow job for this annotation

GitHub Actions / Build, Lint, and Test (18.x)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator

Check warning on line 177 in src/PollingBlockTracker.test.ts

View workflow job for this annotation

GitHub Actions / Build, Lint, and Test (22.x)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
expect(blockTracker.isRunning()).toBe(false);
});
});
Expand Down Expand Up @@ -902,6 +902,87 @@
},
);
});

it('should reject pending latest block request if block tracker is destroyed before fetch completes on second getLatestBlock call', async () => {
const setTimeoutRecorder = recordCallsToSetTimeout();
const blockTrackerOptions = {
pollingInterval: 100,
blockResetDuration: 200,
};

await withPollingBlockTracker(
{
provider: {
stubs: [
{
methodName: 'eth_blockNumber',
result: '0x0',
},
{
methodName: 'eth_blockNumber',
result: '0x0',
},
],
},
blockTracker: blockTrackerOptions,
},
async ({ blockTracker }) => {
// Step 1: Start the block tracker
blockTracker.on('latest', EMPTY_FUNCTION);

// Step 2: Wait for the first block update to resolve
await new Promise((resolve) => {
blockTracker.on('sync', resolve);
});
expect(blockTracker.getCurrentBlock()).toBe('0x0');
expect(blockTracker.isRunning()).toBe(true);

// Clear the current block to force a new request for the next getLatestBlock
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This comment should probably read:

Suggested change
// Clear the current block to force a new request for the next getLatestBlock
// Stop the block tracker.

// When the block tracker stops, there may be two `setTimeout`s in
// play: one to go to the next iteration of the block tracker
// loop, another to expire the current block number cache. We don't
// know which one has been added first, so we have to find it.
blockTracker.removeAllListeners();
await setTimeoutRecorder.nextMatchingDuration(
blockTrackerOptions.blockResetDuration,
);
expect(blockTracker.getCurrentBlock()).toBeNull();

// Restart the tracker for the second call
blockTracker.on('latest', EMPTY_FUNCTION);

// Step 3: Immediately after, call getLatestBlock
const secondBlockPromise = blockTracker.getLatestBlock();

// Step 4: Immediately after, destroy the block tracker
await blockTracker.destroy();

// Verify block tracker state
expect(blockTracker.isRunning()).toBe(false);
expect(blockTracker.getCurrentBlock()).toBeNull();

// The call to getLatestBlock would then never resolve (should be rejected)
await expect(secondBlockPromise).rejects.toThrow(
'Block tracker destroyed',
);

// Verify that the block reset timeout is set up
expect(
setTimeoutRecorder.calls.some((call) => {
return call.duration === blockTrackerOptions.blockResetDuration;
}),
).toBe(true);

// Wait for the block reset timeout to complete
await setTimeoutRecorder.nextMatchingDuration(
blockTrackerOptions.blockResetDuration,
);

// Verify that the current block is still null after the timeout
expect(blockTracker.getCurrentBlock()).toBeNull();
},
);
});
});
});

Expand Down
Loading