@@ -18,8 +18,6 @@ import type { InternalAccount } from '@metamask/keyring-internal-api';
1818import { MultichainAccountWallet } from './MultichainAccountWallet' ;
1919import type { MockAccountProvider } from './tests' ;
2020import {
21- getMultichainAccountServiceMessenger ,
22- getRootMessenger ,
2321 MOCK_HD_ACCOUNT_1 ,
2422 MOCK_HD_KEYRING_1 ,
2523 MOCK_SNAP_ACCOUNT_2 ,
@@ -39,6 +37,7 @@ import type {
3937 AllowedEvents ,
4038 MultichainAccountServiceActions ,
4139 MultichainAccountServiceEvents ,
40+ MultichainAccountServiceMessenger ,
4241} from './types' ;
4342
4443function setup ( {
@@ -65,18 +64,21 @@ function setup({
6564} = { } ) : {
6665 wallet : MultichainAccountWallet < Bip44Account < InternalAccount > > ;
6766 providers : MockAccountProvider [ ] ;
67+ messenger : MultichainAccountServiceMessenger ;
6868} {
6969 providers ??= accounts . map ( ( providerAccounts ) => {
7070 return setupAccountProvider ( { accounts : providerAccounts } ) ;
7171 } ) ;
7272
73+ const serviceMessenger = getMultichainAccountServiceMessenger ( messenger ) ;
74+
7375 const wallet = new MultichainAccountWallet < Bip44Account < InternalAccount > > ( {
7476 entropySource,
7577 providers,
76- messenger : getMultichainAccountServiceMessenger ( messenger ) ,
78+ messenger : serviceMessenger ,
7779 } ) ;
7880
79- return { wallet, providers } ;
81+ return { wallet, providers, messenger : serviceMessenger } ;
8082}
8183
8284describe ( 'MultichainAccountWallet' , ( ) => {
@@ -440,85 +442,28 @@ describe('MultichainAccountWallet', () => {
440442 } ) ;
441443
442444 it ( 'returns true during alignment and false after completion' , async ( ) => {
443- const { wallet } = setup ( ) ;
444-
445- // Start alignment (don't await yet)
446- const alignmentPromise = wallet . alignGroups ( ) ;
445+ const { wallet, messenger } = setup ( ) ;
447446
448- // Check if alignment is in progress
449- expect ( wallet . getIsAlignmentInProgress ( ) ) . toBe ( true ) ;
450-
451- // Wait for completion
452- await alignmentPromise ;
453-
454- // Should be false after completion
455447 expect ( wallet . getIsAlignmentInProgress ( ) ) . toBe ( false ) ;
456- } ) ;
457- } ) ;
458-
459- describe ( 'concurrent alignment prevention' , ( ) => {
460- it ( 'prevents concurrent alignGroups calls' , async ( ) => {
461- // Setup with EVM account in group 0, Sol account in group 1 (missing group 0)
462- const mockEvmAccount = MockAccountBuilder . from ( MOCK_HD_ACCOUNT_1 )
463- . withEntropySource ( MOCK_HD_KEYRING_1 . metadata . id )
464- . withGroupIndex ( 0 )
465- . get ( ) ;
466- const mockSolAccount = MockAccountBuilder . from ( MOCK_SOL_ACCOUNT_1 )
467- . withEntropySource ( MOCK_HD_KEYRING_1 . metadata . id )
468- . withGroupIndex ( 1 )
469- . get ( ) ;
470- const { wallet, providers } = setup ( {
471- accounts : [ [ mockEvmAccount ] , [ mockSolAccount ] ] ,
472- } ) ;
473-
474- // Make provider createAccounts slow to ensure concurrency
475- providers [ 1 ] . createAccounts . mockImplementation (
476- ( ) => new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( [ ] ) , 50 ) ) ,
477- ) ;
478-
479- // Start first alignment
480- const firstAlignment = wallet . alignGroups ( ) ;
481448
482- // Start second alignment while first is still running
483- const secondAlignment = wallet . alignGroups ( ) ;
484-
485- // Both should complete without error
486- await Promise . all ( [ firstAlignment , secondAlignment ] ) ;
487-
488- // Provider should only be called once (not twice due to concurrency protection)
489- expect ( providers [ 1 ] . createAccounts ) . toHaveBeenCalledTimes ( 1 ) ;
490- } ) ;
491-
492- it ( 'prevents concurrent alignGroup calls' , async ( ) => {
493- // Setup with EVM account in group 0, Sol account in group 1 (missing group 0)
494- const mockEvmAccount = MockAccountBuilder . from ( MOCK_HD_ACCOUNT_1 )
495- . withEntropySource ( MOCK_HD_KEYRING_1 . metadata . id )
496- . withGroupIndex ( 0 )
497- . get ( ) ;
498- const mockSolAccount = MockAccountBuilder . from ( MOCK_SOL_ACCOUNT_1 )
499- . withEntropySource ( MOCK_HD_KEYRING_1 . metadata . id )
500- . withGroupIndex ( 1 )
501- . get ( ) ;
502- const { wallet, providers } = setup ( {
503- accounts : [ [ mockEvmAccount ] , [ mockSolAccount ] ] ,
449+ const mockWalletStatusChange = jest . fn ( ) . mockImplementationOnce ( ( ) => {
450+ // Listen for wallet status change and check that the alignment is in progress.
451+ expect ( wallet . getIsAlignmentInProgress ( ) ) . toBe ( true ) ;
504452 } ) ;
505453
506- // Make provider createAccounts slow to ensure concurrency
507- providers [ 1 ] . createAccounts . mockImplementation (
508- ( ) => new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( [ ] ) , 50 ) ) ,
454+ messenger . subscribe (
455+ 'MultichainAccountService:walletStatusChange' ,
456+ mockWalletStatusChange ,
509457 ) ;
510458
511- // Start first alignment
512- const firstAlignment = wallet . alignGroup ( 0 ) ;
513-
514- // Start second alignment while first is still running
515- const secondAlignment = wallet . alignGroup ( 0 ) ;
459+ // Start alignment (don't await yet)
460+ const alignmentPromise = wallet . alignGroups ( ) ;
516461
517- // Both should complete without error
518- await Promise . all ( [ firstAlignment , secondAlignment ] ) ;
462+ // Wait for completion
463+ await alignmentPromise ;
519464
520- // Provider should only be called once (not twice due to concurrency protection)
521- expect ( providers [ 1 ] . createAccounts ) . toHaveBeenCalledTimes ( 1 ) ;
465+ // Should be false after completion
466+ expect ( wallet . getIsAlignmentInProgress ( ) ) . toBe ( false ) ;
522467 } ) ;
523468 } ) ;
524469
@@ -551,6 +496,7 @@ describe('MultichainAccountWallet', () => {
551496 jest . useFakeTimers ( ) ;
552497 const discovery = wallet . discoverAndCreateAccounts ( ) ;
553498 // Allow fast provider microtasks to run and advance maxGroupIndex first
499+ await Promise . resolve ( ) ; // Mutex lock.
554500 await Promise . resolve ( ) ;
555501 await Promise . resolve ( ) ;
556502 jest . advanceTimersByTime ( 100 ) ;
0 commit comments