code.vegaprotocol.io/vega@v0.79.0/wallet/api/client_connect_wallet_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api_test
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"testing"
    22  
    23  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    24  	"code.vegaprotocol.io/vega/wallet/api"
    25  	"code.vegaprotocol.io/vega/wallet/api/mocks"
    26  	"code.vegaprotocol.io/vega/wallet/preferences"
    27  	"code.vegaprotocol.io/vega/wallet/wallet"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestConnectWallet(t *testing.T) {
    35  	t.Run("Connecting to one of the existing wallet with valid params succeeds", testConnectingToOneOfMultipleWalletsWithValidParamsSucceeds)
    36  	t.Run("Connecting to the only wallet with valid params succeeds", testConnectingToTheOnlyWalletWithValidParamsSucceeds)
    37  	t.Run("Connecting to one of the existing wallet when already unlocked skips the passphrase", testConnectingToOneOfMultipleWalletsWhenAlreadyUnlockedSkipsPassphrase)
    38  	t.Run("Connecting to the only wallet when already unlocked skips the passphrase", testConnectingToTheOnlyWalletWhenAlreadyUnlockedSkipsPassphrase)
    39  	t.Run("Connecting to a connected wallet disconnects the previous one and generates a new token", testConnectingToConnectedWalletDisconnectsPreviousOneAndGeneratesNewToken)
    40  	t.Run("Connecting to a wallet without wallets fails", testConnectingWalletWithoutWalletsFails)
    41  	t.Run("Getting internal error during the wallet listing does not connect to a wallet", testGettingInternalErrorDuringWalletListingDoesNotConnectToWallet)
    42  	t.Run("Refusing a wallet connection does not connect to a wallet", testRefusingWalletConnectionDoesNotConnectToWallet)
    43  	t.Run("Canceling the review does not connect to a wallet", testCancelingTheReviewDoesNotConnectToWallet)
    44  	t.Run("Interrupting the request during the review does not connect to a wallet", testInterruptingTheRequestDuringReviewDoesNotConnectToWallet)
    45  	t.Run("Getting internal error during the review does not connect to a wallet", testGettingInternalErrorDuringReviewDoesNotConnectToWallet)
    46  	t.Run("Cancelling the wallet selection does not connect to a wallet", testCancellingTheWalletSelectionDoesNotConnectToWallet)
    47  	t.Run("Interrupting the request during the wallet selection does not connect to a wallet", testInterruptingTheRequestDuringWalletSelectionDoesNotConnectToWallet)
    48  	t.Run("Getting internal error during the wallet selection does not connect to a wallet", testGettingInternalErrorDuringWalletSelectionDoesNotConnectToWallet)
    49  	t.Run("Selecting a non-existing wallet does not connect to a wallet", testSelectingNonExistingWalletDoesNotConnectToWallet)
    50  	t.Run("Getting internal error during the wallet lock state verification does not connect to a wallet", testGettingInternalErrorDuringWalletLockStateVerificationDoesNotConnectToWallet)
    51  	t.Run("Getting internal error during the passphrase request does not connect to a wallet", testGettingInternalErrorDuringPassphraseRequestDoesNotConnectToWallet)
    52  	t.Run("Cancelling the passphrase request does not connect to a wallet", testCancellingThePassphraseRequestDoesNotConnectToWallet)
    53  	t.Run("Interrupting the request during the passphrase request does not connect to a wallet", testInterruptingTheRequestDuringPassphraseRequestDoesNotConnectToWallet)
    54  	t.Run("Getting internal error during the wallet unlocking does not connect to a wallet", testGettingInternalErrorDuringWalletUnlockingDoesNotConnectToWallet)
    55  	t.Run("Getting internal error during the wallet verification does not connect to a wallet", testGettingInternalErrorDuringWalletVerificationDoesNotConnectToWallet)
    56  	t.Run("Using the wrong passphrase does not connect to a wallet", testUsingWrongPassphraseDoesNotConnectToWallet)
    57  	t.Run("Getting internal error during the wallet retrieval does not connect to a wallet", testGettingInternalErrorDuringWalletRetrievalDoesNotConnectToWallet)
    58  }
    59  
    60  func testConnectingToOneOfMultipleWalletsWithValidParamsSucceeds(t *testing.T) {
    61  	// given
    62  	ctx, traceID := clientContextForTest()
    63  	hostname := vgrand.RandomStr(5) + ".xyz"
    64  	expectedPermissions := wallet.Permissions{
    65  		PublicKeys: wallet.PublicKeysPermission{
    66  			Access:      wallet.ReadAccess,
    67  			AllowedKeys: []string{},
    68  		},
    69  	}
    70  	expectedSelectedWallet := walletWithPerms(t, hostname, expectedPermissions)
    71  	nonSelectedWallet := walletWithPerms(t, hostname, wallet.Permissions{})
    72  
    73  	passphrase := vgrand.RandomStr(5)
    74  	availableWallets := []string{
    75  		expectedSelectedWallet.Name(),
    76  		nonSelectedWallet.Name(),
    77  	}
    78  
    79  	// setup
    80  	// -- expected calls
    81  	handler := newConnectWalletHandler(t)
    82  	handler.walletStore.EXPECT().WalletExists(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
    83  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedSelectedWallet.Name()).Times(1).Return(false, nil)
    84  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
    85  	handler.walletStore.EXPECT().GetWallet(ctx, expectedSelectedWallet.Name()).Times(1).Return(expectedSelectedWallet, nil)
    86  	handler.walletStore.EXPECT().UnlockWallet(ctx, expectedSelectedWallet.Name(), passphrase).Times(1).Return(nil)
    87  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
    88  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
    89  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
    90  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, availableWallets).Times(1).Return(expectedSelectedWallet.Name(), nil)
    91  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), expectedSelectedWallet.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
    92  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
    93  
    94  	// when
    95  	token, errorDetails := handler.Handle(ctx, hostname)
    96  
    97  	// then
    98  	require.Nil(t, errorDetails)
    99  	assert.NotEmpty(t, token)
   100  }
   101  
   102  func testConnectingToTheOnlyWalletWithValidParamsSucceeds(t *testing.T) {
   103  	// given
   104  	ctx, traceID := clientContextForTest()
   105  	hostname := vgrand.RandomStr(5) + ".xyz"
   106  	expectedPermissions := wallet.Permissions{
   107  		PublicKeys: wallet.PublicKeysPermission{
   108  			Access:      wallet.ReadAccess,
   109  			AllowedKeys: []string{},
   110  		},
   111  	}
   112  	onlyWallet := walletWithPerms(t, hostname, expectedPermissions)
   113  
   114  	passphrase := vgrand.RandomStr(5)
   115  	availableWallets := []string{onlyWallet.Name()}
   116  
   117  	// setup
   118  	// -- expected calls
   119  	handler := newConnectWalletHandler(t)
   120  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, onlyWallet.Name()).Times(1).Return(false, nil)
   121  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
   122  	handler.walletStore.EXPECT().GetWallet(ctx, onlyWallet.Name()).Times(1).Return(onlyWallet, nil)
   123  	handler.walletStore.EXPECT().UnlockWallet(ctx, onlyWallet.Name(), passphrase).Times(1).Return(nil)
   124  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   125  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   126  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   127  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), onlyWallet.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
   128  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
   129  
   130  	// when
   131  	token, errorDetails := handler.Handle(ctx, hostname)
   132  
   133  	// then
   134  	require.Nil(t, errorDetails)
   135  	assert.NotEmpty(t, token)
   136  }
   137  
   138  func testConnectingToOneOfMultipleWalletsWhenAlreadyUnlockedSkipsPassphrase(t *testing.T) {
   139  	// given
   140  	ctx, traceID := clientContextForTest()
   141  	hostname := vgrand.RandomStr(5) + ".xyz"
   142  	expectedPermissions := wallet.Permissions{
   143  		PublicKeys: wallet.PublicKeysPermission{
   144  			Access:      wallet.ReadAccess,
   145  			AllowedKeys: []string{},
   146  		},
   147  	}
   148  	expectedSelectedWallet := walletWithPerms(t, hostname, expectedPermissions)
   149  	nonSelectedWallet := walletWithPerms(t, hostname, wallet.Permissions{})
   150  
   151  	availableWallets := []string{
   152  		expectedSelectedWallet.Name(),
   153  		nonSelectedWallet.Name(),
   154  	}
   155  
   156  	// setup
   157  	// -- expected calls
   158  	handler := newConnectWalletHandler(t)
   159  	handler.walletStore.EXPECT().WalletExists(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
   160  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
   161  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
   162  	handler.walletStore.EXPECT().GetWallet(ctx, expectedSelectedWallet.Name()).Times(1).Return(expectedSelectedWallet, nil)
   163  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   164  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   165  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   166  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, availableWallets).Times(1).Return(expectedSelectedWallet.Name(), nil)
   167  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
   168  
   169  	// when
   170  	token, errorDetails := handler.Handle(ctx, hostname)
   171  
   172  	// then
   173  	require.Nil(t, errorDetails)
   174  	assert.NotEmpty(t, token)
   175  }
   176  
   177  func testConnectingToTheOnlyWalletWhenAlreadyUnlockedSkipsPassphrase(t *testing.T) {
   178  	// given
   179  	ctx, traceID := clientContextForTest()
   180  	hostname := vgrand.RandomStr(5) + ".xyz"
   181  	expectedPermissions := wallet.Permissions{
   182  		PublicKeys: wallet.PublicKeysPermission{
   183  			Access:      wallet.ReadAccess,
   184  			AllowedKeys: []string{},
   185  		},
   186  	}
   187  	onlyWallet := walletWithPerms(t, hostname, expectedPermissions)
   188  
   189  	availableWallets := []string{onlyWallet.Name()}
   190  
   191  	// setup
   192  	// -- expected calls
   193  	handler := newConnectWalletHandler(t)
   194  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, onlyWallet.Name()).Times(1).Return(true, nil)
   195  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
   196  	handler.walletStore.EXPECT().GetWallet(ctx, onlyWallet.Name()).Times(1).Return(onlyWallet, nil)
   197  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   198  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   199  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   200  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
   201  
   202  	// when
   203  	token, errorDetails := handler.Handle(ctx, hostname)
   204  
   205  	// then
   206  	require.Nil(t, errorDetails)
   207  	assert.NotEmpty(t, token)
   208  }
   209  
   210  func testConnectingToConnectedWalletDisconnectsPreviousOneAndGeneratesNewToken(t *testing.T) {
   211  	// given
   212  	ctx, traceID := clientContextForTest()
   213  	hostname := vgrand.RandomStr(5) + ".xyz"
   214  	expectedPermissions := wallet.Permissions{
   215  		PublicKeys: wallet.PublicKeysPermission{
   216  			Access:      wallet.ReadAccess,
   217  			AllowedKeys: []string{},
   218  		},
   219  	}
   220  	expectedSelectedWallet := walletWithPerms(t, hostname, expectedPermissions)
   221  	nonSelectedWallet := walletWithPerms(t, hostname, wallet.Permissions{})
   222  
   223  	passphrase := vgrand.RandomStr(5)
   224  	availableWallets := []string{
   225  		expectedSelectedWallet.Name(),
   226  		nonSelectedWallet.Name(),
   227  	}
   228  
   229  	// setup
   230  	handler := newConnectWalletHandler(t)
   231  	// -- expected calls
   232  	handler.walletStore.EXPECT().WalletExists(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
   233  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedSelectedWallet.Name()).Times(1).Return(false, nil)
   234  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
   235  	handler.walletStore.EXPECT().UnlockWallet(ctx, expectedSelectedWallet.Name(), passphrase).Times(1).Return(nil)
   236  	handler.walletStore.EXPECT().GetWallet(ctx, expectedSelectedWallet.Name()).Times(1).Return(expectedSelectedWallet, nil)
   237  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   238  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   239  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   240  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, availableWallets).Times(1).Return(expectedSelectedWallet.Name(), nil)
   241  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), expectedSelectedWallet.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
   242  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
   243  
   244  	// when
   245  	wallet1, errorDetails := handler.Handle(ctx, hostname)
   246  
   247  	// then
   248  	assert.Nil(t, errorDetails)
   249  	assert.NotEmpty(t, wallet1)
   250  
   251  	// setup
   252  	// -- expected calls
   253  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
   254  	handler.walletStore.EXPECT().WalletExists(ctx, expectedSelectedWallet.Name()).Times(1).Return(true, nil)
   255  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(availableWallets, nil)
   256  	handler.walletStore.EXPECT().GetWallet(ctx, expectedSelectedWallet.Name()).Times(1).Return(expectedSelectedWallet, nil)
   257  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   258  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   259  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   260  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, availableWallets).Times(1).Return(expectedSelectedWallet.Name(), nil)
   261  	handler.interactor.EXPECT().NotifySuccessfulRequest(ctx, traceID, uint8(4), api.WalletConnectionSuccessfullyEstablished).Times(1)
   262  
   263  	// when
   264  	wallet2, errorDetails := handler.Handle(ctx, hostname)
   265  
   266  	// then
   267  	assert.Nil(t, errorDetails)
   268  	assert.Equal(t, wallet2, wallet1)
   269  }
   270  
   271  func testConnectingWalletWithoutWalletsFails(t *testing.T) {
   272  	// given
   273  	ctx, traceID := clientContextForTest()
   274  	hostname := vgrand.RandomStr(5) + ".xyz"
   275  
   276  	// setup
   277  	handler := newConnectWalletHandler(t)
   278  	// -- expected calls
   279  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   280  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   281  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ApplicationErrorType, api.ErrNoWalletToConnectTo).Times(1)
   282  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{}, nil)
   283  
   284  	// when
   285  	result, errorDetails := handler.Handle(ctx, hostname)
   286  
   287  	// then
   288  	assertApplicationCancellationError(t, errorDetails)
   289  	assert.Empty(t, result)
   290  }
   291  
   292  func testGettingInternalErrorDuringWalletListingDoesNotConnectToWallet(t *testing.T) {
   293  	// given
   294  	ctx, traceID := clientContextForTest()
   295  	hostname := vgrand.RandomStr(5) + ".xyz"
   296  
   297  	// setup
   298  	handler := newConnectWalletHandler(t)
   299  	// -- expected calls
   300  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   301  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   302  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return(nil, assert.AnError)
   303  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("could not list the available wallets: %w", assert.AnError)).Times(1)
   304  
   305  	// when
   306  	result, errorDetails := handler.Handle(ctx, hostname)
   307  
   308  	// then
   309  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   310  	assert.Empty(t, result)
   311  }
   312  
   313  func testRefusingWalletConnectionDoesNotConnectToWallet(t *testing.T) {
   314  	// given
   315  	ctx, traceID := clientContextForTest()
   316  	hostname := vgrand.RandomStr(5) + ".xyz"
   317  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   318  
   319  	// setup
   320  	handler := newConnectWalletHandler(t)
   321  	// -- expected calls
   322  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   323  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   324  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.RejectedOnlyThisTime), nil)
   325  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name()}, nil)
   326  
   327  	// when
   328  	result, errorDetails := handler.Handle(ctx, hostname)
   329  
   330  	// then
   331  	assertUserRejectionError(t, errorDetails, api.ErrUserRejectedWalletConnection)
   332  	assert.Empty(t, result)
   333  }
   334  
   335  func testCancelingTheReviewDoesNotConnectToWallet(t *testing.T) {
   336  	// given
   337  	ctx, traceID := clientContextForTest()
   338  	hostname := vgrand.RandomStr(5) + ".xyz"
   339  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   340  
   341  	// setup
   342  	handler := newConnectWalletHandler(t)
   343  	// -- expected calls
   344  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   345  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   346  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return("", api.ErrUserCloseTheConnection)
   347  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ApplicationErrorType, api.ErrConnectionClosed).Times(1)
   348  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name()}, nil)
   349  
   350  	// when
   351  	result, errorDetails := handler.Handle(ctx, hostname)
   352  
   353  	// then
   354  	assertConnectionClosedError(t, errorDetails)
   355  	assert.Empty(t, result)
   356  }
   357  
   358  func testInterruptingTheRequestDuringReviewDoesNotConnectToWallet(t *testing.T) {
   359  	// given
   360  	ctx, traceID := clientContextForTest()
   361  	hostname := vgrand.RandomStr(5) + ".xyz"
   362  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   363  
   364  	// setup
   365  	handler := newConnectWalletHandler(t)
   366  	// -- expected calls
   367  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   368  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   369  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return("", api.ErrRequestInterrupted)
   370  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ServerErrorType, api.ErrRequestInterrupted).Times(1)
   371  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name()}, nil)
   372  
   373  	// when
   374  	result, errorDetails := handler.Handle(ctx, hostname)
   375  
   376  	// then
   377  	assertRequestInterruptionError(t, errorDetails)
   378  	assert.Empty(t, result)
   379  }
   380  
   381  func testGettingInternalErrorDuringReviewDoesNotConnectToWallet(t *testing.T) {
   382  	// given
   383  	ctx, traceID := clientContextForTest()
   384  	hostname := vgrand.RandomStr(5) + ".xyz"
   385  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   386  
   387  	// setup
   388  	handler := newConnectWalletHandler(t)
   389  	// -- expected calls
   390  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   391  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   392  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return("", assert.AnError)
   393  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("reviewing the wallet connection failed: %w", assert.AnError)).Times(1)
   394  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name()}, nil)
   395  
   396  	// when
   397  	result, errorDetails := handler.Handle(ctx, hostname)
   398  
   399  	// then
   400  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   401  	assert.Empty(t, result)
   402  }
   403  
   404  func testCancellingTheWalletSelectionDoesNotConnectToWallet(t *testing.T) {
   405  	// given
   406  	ctx, traceID := clientContextForTest()
   407  	hostname := vgrand.RandomStr(5) + ".xyz"
   408  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   409  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   410  
   411  	// setup
   412  	handler := newConnectWalletHandler(t)
   413  	// -- expected calls
   414  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   415  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   416  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   417  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   418  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return("", api.ErrUserCloseTheConnection)
   419  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ApplicationErrorType, api.ErrConnectionClosed).Times(1)
   420  
   421  	// when
   422  	result, errorDetails := handler.Handle(ctx, hostname)
   423  
   424  	// then
   425  	assertConnectionClosedError(t, errorDetails)
   426  	assert.Empty(t, result)
   427  }
   428  
   429  func testInterruptingTheRequestDuringWalletSelectionDoesNotConnectToWallet(t *testing.T) {
   430  	// given
   431  	ctx, traceID := clientContextForTest()
   432  	hostname := vgrand.RandomStr(5) + ".xyz"
   433  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   434  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   435  
   436  	// setup
   437  	handler := newConnectWalletHandler(t)
   438  	// -- expected calls
   439  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   440  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   441  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   442  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   443  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return("", api.ErrRequestInterrupted)
   444  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ServerErrorType, api.ErrRequestInterrupted).Times(1)
   445  
   446  	// when
   447  	result, errorDetails := handler.Handle(ctx, hostname)
   448  
   449  	// then
   450  	assertRequestInterruptionError(t, errorDetails)
   451  	assert.Empty(t, result)
   452  }
   453  
   454  func testGettingInternalErrorDuringWalletSelectionDoesNotConnectToWallet(t *testing.T) {
   455  	// given
   456  	ctx, traceID := clientContextForTest()
   457  	hostname := vgrand.RandomStr(5) + ".xyz"
   458  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   459  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   460  
   461  	// setup
   462  	handler := newConnectWalletHandler(t)
   463  	// -- expected calls
   464  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   465  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   466  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   467  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   468  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return("", assert.AnError)
   469  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("requesting the wallet selection failed: %w", assert.AnError)).Times(1)
   470  
   471  	// when
   472  	result, errorDetails := handler.Handle(ctx, hostname)
   473  
   474  	// then
   475  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   476  	assert.Empty(t, result)
   477  }
   478  
   479  func testSelectingNonExistingWalletDoesNotConnectToWallet(t *testing.T) {
   480  	// given
   481  	ctx, traceID := clientContextForTest()
   482  	cancelCtx, cancelFn := context.WithCancel(ctx)
   483  	hostname := vgrand.RandomStr(5) + ".xyz"
   484  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   485  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   486  	nonExistingWallet := vgrand.RandomStr(5)
   487  
   488  	// setup
   489  	handler := newConnectWalletHandler(t)
   490  	// -- expected calls
   491  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(cancelCtx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   492  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(cancelCtx, traceID).Times(1)
   493  	handler.interactor.EXPECT().RequestWalletConnectionReview(cancelCtx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   494  	handler.walletStore.EXPECT().ListWallets(cancelCtx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   495  	handler.interactor.EXPECT().RequestWalletSelection(cancelCtx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(nonExistingWallet, nil)
   496  	handler.walletStore.EXPECT().WalletExists(cancelCtx, nonExistingWallet).Times(1).Return(false, nil)
   497  	gomock.InOrder(
   498  		handler.interactor.EXPECT().NotifyError(cancelCtx, traceID, api.UserErrorType, api.ErrWalletDoesNotExist).Times(1).Do(func(_ context.Context, _ string, _ api.ErrorType, _ error) {
   499  			// Once everything has been called once, we cancel the handler to break the loop.
   500  			cancelFn()
   501  		}),
   502  		handler.interactor.EXPECT().NotifyError(cancelCtx, traceID, api.ApplicationErrorType, api.ErrRequestInterrupted).Times(1),
   503  	)
   504  
   505  	// when
   506  	result, errorDetails := handler.Handle(cancelCtx, hostname)
   507  
   508  	// then
   509  	assertRequestInterruptionError(t, errorDetails)
   510  	assert.Empty(t, result)
   511  }
   512  
   513  func testGettingInternalErrorDuringWalletRetrievalDoesNotConnectToWallet(t *testing.T) {
   514  	// given
   515  	ctx, traceID := clientContextForTest()
   516  	hostname := vgrand.RandomStr(5) + ".xyz"
   517  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   518  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   519  
   520  	// setup
   521  	handler := newConnectWalletHandler(t)
   522  	// -- expected calls
   523  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   524  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   525  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   526  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   527  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   528  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(false, assert.AnError)
   529  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("could not verify the wallet existence: %w", assert.AnError)).Times(1)
   530  
   531  	// when
   532  	result, errorDetails := handler.Handle(ctx, hostname)
   533  
   534  	// then
   535  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   536  	assert.Empty(t, result)
   537  }
   538  
   539  func testUsingWrongPassphraseDoesNotConnectToWallet(t *testing.T) {
   540  	// given
   541  	ctx, traceID := clientContextForTest()
   542  	cancelCtx, cancelFn := context.WithCancel(ctx)
   543  	hostname := vgrand.RandomStr(5) + ".xyz"
   544  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   545  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   546  	passphrase := vgrand.RandomStr(4)
   547  
   548  	// setup
   549  	handler := newConnectWalletHandler(t)
   550  	// -- expected calls
   551  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(cancelCtx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   552  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(cancelCtx, traceID).Times(1)
   553  	handler.interactor.EXPECT().RequestWalletConnectionReview(cancelCtx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   554  	handler.walletStore.EXPECT().ListWallets(cancelCtx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   555  	handler.interactor.EXPECT().RequestWalletSelection(cancelCtx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   556  	handler.walletStore.EXPECT().WalletExists(cancelCtx, wallet1.Name()).Times(1).Return(true, nil)
   557  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(cancelCtx, wallet1.Name()).Times(1).Return(false, nil)
   558  	handler.walletStore.EXPECT().UnlockWallet(cancelCtx, wallet1.Name(), passphrase).Times(1).Return(wallet.ErrWrongPassphrase)
   559  	handler.interactor.EXPECT().RequestPassphrase(cancelCtx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
   560  	gomock.InOrder(
   561  		handler.interactor.EXPECT().NotifyError(cancelCtx, traceID, api.UserErrorType, wallet.ErrWrongPassphrase).Times(1).Do(func(_ context.Context, _ string, _ api.ErrorType, _ error) {
   562  			// Once everything has been called once, we cancel the handler to break the loop.
   563  			cancelFn()
   564  		}),
   565  		handler.interactor.EXPECT().NotifyError(cancelCtx, traceID, api.ApplicationErrorType, api.ErrRequestInterrupted).Times(1),
   566  	)
   567  
   568  	// when
   569  	result, errorDetails := handler.Handle(cancelCtx, hostname)
   570  
   571  	// then
   572  	assertRequestInterruptionError(t, errorDetails)
   573  	assert.Empty(t, result)
   574  }
   575  
   576  func testGettingInternalErrorDuringWalletLockStateVerificationDoesNotConnectToWallet(t *testing.T) {
   577  	// given
   578  	ctx, traceID := clientContextForTest()
   579  	hostname := vgrand.RandomStr(5) + ".xyz"
   580  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   581  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   582  
   583  	// setup
   584  	handler := newConnectWalletHandler(t)
   585  	// -- expected calls
   586  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   587  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   588  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   589  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   590  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   591  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   592  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, assert.AnError)
   593  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("could not verify whether the wallet is already unlock or not: %w", assert.AnError)).Times(1)
   594  
   595  	// when
   596  	result, errorDetails := handler.Handle(ctx, hostname)
   597  
   598  	// then
   599  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   600  	assert.Empty(t, result)
   601  }
   602  
   603  func testGettingInternalErrorDuringPassphraseRequestDoesNotConnectToWallet(t *testing.T) {
   604  	// given
   605  	ctx, traceID := clientContextForTest()
   606  	hostname := vgrand.RandomStr(5) + ".xyz"
   607  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   608  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   609  
   610  	// setup
   611  	handler := newConnectWalletHandler(t)
   612  	// -- expected calls
   613  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   614  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   615  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   616  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   617  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   618  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   619  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, nil)
   620  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return("", assert.AnError)
   621  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("requesting the wallet passphrase failed: %w", assert.AnError)).Times(1)
   622  
   623  	// when
   624  	result, errorDetails := handler.Handle(ctx, hostname)
   625  
   626  	// then
   627  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   628  	assert.Empty(t, result)
   629  }
   630  
   631  func testInterruptingTheRequestDuringPassphraseRequestDoesNotConnectToWallet(t *testing.T) {
   632  	// given
   633  	ctx, traceID := clientContextForTest()
   634  	hostname := vgrand.RandomStr(5) + ".xyz"
   635  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   636  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   637  
   638  	// setup
   639  	handler := newConnectWalletHandler(t)
   640  	// -- expected calls
   641  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   642  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   643  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   644  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   645  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   646  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   647  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, nil)
   648  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return("", api.ErrRequestInterrupted)
   649  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ServerErrorType, api.ErrRequestInterrupted).Times(1)
   650  
   651  	// when
   652  	result, errorDetails := handler.Handle(ctx, hostname)
   653  
   654  	// then
   655  	assertRequestInterruptionError(t, errorDetails)
   656  	assert.Empty(t, result)
   657  }
   658  
   659  func testCancellingThePassphraseRequestDoesNotConnectToWallet(t *testing.T) {
   660  	// given
   661  	ctx, traceID := clientContextForTest()
   662  	hostname := vgrand.RandomStr(5) + ".xyz"
   663  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   664  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   665  
   666  	// setup
   667  	handler := newConnectWalletHandler(t)
   668  	// -- expected calls
   669  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   670  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   671  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   672  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   673  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   674  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   675  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, nil)
   676  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return("", api.ErrUserCloseTheConnection)
   677  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.ApplicationErrorType, api.ErrConnectionClosed).Times(1)
   678  
   679  	// when
   680  	result, errorDetails := handler.Handle(ctx, hostname)
   681  
   682  	// then
   683  	assertConnectionClosedError(t, errorDetails)
   684  	assert.Empty(t, result)
   685  }
   686  
   687  func testGettingInternalErrorDuringWalletUnlockingDoesNotConnectToWallet(t *testing.T) {
   688  	// given
   689  	ctx, traceID := clientContextForTest()
   690  	hostname := vgrand.RandomStr(5) + ".xyz"
   691  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   692  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   693  	passphrase := vgrand.RandomStr(5)
   694  
   695  	// setup
   696  	handler := newConnectWalletHandler(t)
   697  	// -- expected calls
   698  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   699  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   700  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   701  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   702  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   703  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   704  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
   705  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, nil)
   706  	handler.walletStore.EXPECT().UnlockWallet(ctx, wallet1.Name(), passphrase).Times(1).Return(assert.AnError)
   707  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("could not unlock the wallet: %w", assert.AnError)).Times(1)
   708  
   709  	// when
   710  	result, errorDetails := handler.Handle(ctx, hostname)
   711  
   712  	// then
   713  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   714  	assert.Empty(t, result)
   715  }
   716  
   717  func testGettingInternalErrorDuringWalletVerificationDoesNotConnectToWallet(t *testing.T) {
   718  	// given
   719  	ctx, traceID := clientContextForTest()
   720  	hostname := vgrand.RandomStr(5) + ".xyz"
   721  	wallet1 := walletWithPerms(t, hostname, wallet.Permissions{})
   722  	wallet2 := walletWithPerms(t, hostname, wallet.Permissions{})
   723  	passphrase := vgrand.RandomStr(5)
   724  
   725  	// setup
   726  	handler := newConnectWalletHandler(t)
   727  	// -- expected calls
   728  	handler.interactor.EXPECT().NotifyInteractionSessionBegan(ctx, traceID, api.WalletConnectionWorkflow, uint8(4)).Times(1).Return(nil)
   729  	handler.interactor.EXPECT().NotifyInteractionSessionEnded(ctx, traceID).Times(1)
   730  	handler.interactor.EXPECT().RequestWalletConnectionReview(ctx, traceID, uint8(1), hostname).Times(1).Return(string(preferences.ApprovedOnlyThisTime), nil)
   731  	handler.walletStore.EXPECT().ListWallets(ctx).Times(1).Return([]string{wallet1.Name(), wallet2.Name()}, nil)
   732  	handler.interactor.EXPECT().RequestWalletSelection(ctx, traceID, uint8(2), hostname, []string{wallet1.Name(), wallet2.Name()}).Times(1).Return(wallet1.Name(), nil)
   733  	handler.walletStore.EXPECT().WalletExists(ctx, wallet1.Name()).Times(1).Return(true, nil)
   734  	handler.interactor.EXPECT().RequestPassphrase(ctx, traceID, uint8(3), wallet1.Name(), api.PassphraseRequestReasonUnlockWallet).Times(1).Return(passphrase, nil)
   735  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, wallet1.Name()).Times(1).Return(false, nil)
   736  	handler.walletStore.EXPECT().UnlockWallet(ctx, wallet1.Name(), passphrase).Times(1).Return(nil)
   737  	handler.walletStore.EXPECT().GetWallet(ctx, wallet1.Name()).Times(1).Return(nil, assert.AnError)
   738  	handler.interactor.EXPECT().NotifyError(ctx, traceID, api.InternalErrorType, fmt.Errorf("could not retrieve the wallet: %w", assert.AnError)).Times(1)
   739  
   740  	// when
   741  	result, errorDetails := handler.Handle(ctx, hostname)
   742  
   743  	// then
   744  	assertInternalError(t, errorDetails, api.ErrCouldNotConnectToWallet)
   745  	assert.Empty(t, result)
   746  }
   747  
   748  type connectWalletHandler struct {
   749  	*api.ClientConnectWallet
   750  	ctrl        *gomock.Controller
   751  	walletStore *mocks.MockWalletStore
   752  	interactor  *mocks.MockInteractor
   753  }
   754  
   755  func newConnectWalletHandler(t *testing.T) *connectWalletHandler {
   756  	t.Helper()
   757  
   758  	ctrl := gomock.NewController(t)
   759  	walletStore := mocks.NewMockWalletStore(ctrl)
   760  	interactor := mocks.NewMockInteractor(ctrl)
   761  
   762  	return &connectWalletHandler{
   763  		ClientConnectWallet: api.NewConnectWallet(walletStore, interactor),
   764  		ctrl:                ctrl,
   765  		walletStore:         walletStore,
   766  		interactor:          interactor,
   767  	}
   768  }