Skip to content

Commit 179582f

Browse files
committed
feat(TokenBalanceController): fix test
1 parent 3d18fab commit 179582f

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

packages/assets-controllers/src/TokenBalancesController.test.ts

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Messenger } from '@metamask/base-controller';
22
import { toHex } from '@metamask/controller-utils';
3-
import * as controllerUtils from '@metamask/controller-utils';
43
import type { InternalAccount } from '@metamask/keyring-internal-api';
54
import type { NetworkState } from '@metamask/network-controller';
65
import type { PreferencesState } from '@metamask/preferences-controller';
@@ -9,6 +8,7 @@ import BN from 'bn.js';
98
import { useFakeTimers } from 'sinon';
109

1110
import * as multicall from './multicall';
11+
import { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';
1212
import type {
1313
AllowedActions,
1414
AllowedEvents,
@@ -2986,7 +2986,12 @@ describe('TokenBalancesController', () => {
29862986
// Mock the multicall function to never resolve (simulating a hanging request)
29872987
const mockGetTokenBalances = jest
29882988
.spyOn(multicall, 'getTokenBalancesForMultipleAddresses')
2989-
.mockImplementation(() => new Promise(() => {})); // Never resolves
2989+
.mockImplementation(
2990+
() =>
2991+
new Promise(() => {
2992+
// Never resolves - intentional hanging promise for timeout test
2993+
}),
2994+
); // Never resolves
29902995

29912996
// Start the balance update - this should timeout after 15000ms
29922997
const updatePromise = controller.updateBalances({ chainIds: [chainId] });
@@ -3062,4 +3067,141 @@ describe('TokenBalancesController', () => {
30623067
}).not.toThrow();
30633068
});
30643069
});
3070+
3071+
describe('Additional coverage tests', () => {
3072+
it('should construct controller with allowExternalServices returning false', () => {
3073+
// Test line 197: allowExternalServices = () => false
3074+
const { controller } = setupController({
3075+
config: {
3076+
allowExternalServices: () => false,
3077+
useAccountsAPI: true, // This should be ignored when allowExternalServices is false
3078+
},
3079+
});
3080+
3081+
expect(controller).toBeDefined();
3082+
// Verify that AccountsAPI fetcher is not created when external services are disabled
3083+
expect(controller.state.tokenBalances).toStrictEqual({});
3084+
});
3085+
3086+
it('should handle inactive controller during polling', async () => {
3087+
const chainId = '0x1';
3088+
const { controller } = setupController();
3089+
3090+
// Start polling then immediately stop it
3091+
controller.startPolling({ chainIds: [chainId] });
3092+
controller.stopAllPolling();
3093+
3094+
// Manually call _executePoll when controller is inactive (covers line 335)
3095+
// This should return early without doing anything
3096+
await expect(
3097+
controller._executePoll({ chainIds: [chainId] }),
3098+
).resolves.not.toThrow();
3099+
});
3100+
3101+
it('should clear existing timer when starting polling for same interval', () => {
3102+
const chainId1 = '0x1';
3103+
const chainId2 = '0x89'; // Polygon
3104+
3105+
const { controller } = setupController({
3106+
config: {
3107+
interval: 1000, // Default interval
3108+
chainPollingIntervals: {
3109+
[chainId1]: { interval: 5000 },
3110+
[chainId2]: { interval: 5000 }, // Same interval as chainId1
3111+
},
3112+
},
3113+
});
3114+
3115+
// Start polling for first chain
3116+
controller.startPolling({ chainIds: [chainId1] });
3117+
3118+
// Start polling for second chain with same interval (covers line 359)
3119+
// This should clear the existing timer and create a new one
3120+
controller.startPolling({ chainIds: [chainId1, chainId2] });
3121+
3122+
// Verify controller is defined and functioning
3123+
expect(controller).toBeDefined();
3124+
expect(controller.state.tokenBalances).toStrictEqual({});
3125+
3126+
controller.stopAllPolling();
3127+
});
3128+
3129+
it('should skip fetcher when no chains are supported', async () => {
3130+
const chainId = '0x999'; // Unsupported chain
3131+
const account = createMockInternalAccount();
3132+
3133+
const tokens = {
3134+
allDetectedTokens: {},
3135+
allTokens: {
3136+
[chainId]: {
3137+
[account.address]: [
3138+
{
3139+
address: '0x0000000000000000000000000000000000000001',
3140+
symbol: 'TEST',
3141+
decimals: 18,
3142+
},
3143+
],
3144+
},
3145+
},
3146+
};
3147+
3148+
const { controller } = setupController({
3149+
tokens,
3150+
listAccounts: [account],
3151+
config: { useAccountsAPI: false },
3152+
});
3153+
3154+
// Mock the RpcBalanceFetcher to not support this specific chain
3155+
const mockSupports = jest
3156+
.spyOn(RpcBalanceFetcher.prototype, 'supports')
3157+
.mockReturnValue(false);
3158+
3159+
// This should trigger the continue statement (line 440) when no chains are supported
3160+
await controller.updateBalances({ chainIds: [chainId] });
3161+
3162+
expect(mockSupports).toHaveBeenCalledWith(chainId);
3163+
mockSupports.mockRestore();
3164+
});
3165+
3166+
it('should restart polling when tokens change and controller is active', () => {
3167+
const chainId = '0x1';
3168+
const accountAddress = '0x0000000000000000000000000000000000000000';
3169+
const tokenAddress = '0x0000000000000000000000000000000000000001';
3170+
const account = createMockInternalAccount({ address: accountAddress });
3171+
3172+
const { controller, messenger } = setupController({
3173+
listAccounts: [account],
3174+
});
3175+
3176+
// Start polling to make controller active
3177+
controller.startPolling({ chainIds: [chainId] });
3178+
3179+
// Simulate tokens state change that should restart polling (covers lines 672-673)
3180+
const newTokensState = {
3181+
allTokens: {
3182+
[chainId]: {
3183+
[accountAddress]: [
3184+
{ address: tokenAddress, symbol: 'NEW', decimals: 18 },
3185+
],
3186+
},
3187+
},
3188+
allDetectedTokens: {},
3189+
detectedTokens: [],
3190+
tokens: [],
3191+
ignoredTokens: [],
3192+
allIgnoredTokens: {},
3193+
};
3194+
3195+
// This should trigger the polling restart logic
3196+
messenger.publish('TokensController:stateChange', newTokensState, [
3197+
{ op: 'replace', path: [], value: newTokensState },
3198+
]);
3199+
3200+
// Verify controller state was updated
3201+
expect(controller).toBeDefined();
3202+
expect(controller.state.tokenBalances).toStrictEqual({});
3203+
3204+
controller.stopAllPolling();
3205+
});
3206+
});
30653207
});

0 commit comments

Comments
 (0)