code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_create_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  	"code.vegaprotocol.io/vega/libs/jsonrpc"
    24  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    25  	"code.vegaprotocol.io/vega/wallet/api"
    26  	"code.vegaprotocol.io/vega/wallet/api/mocks"
    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 TestAdminCreateWallet(t *testing.T) {
    35  	t.Run("Documentation matches the code", testAdminCreateWalletSchemaCorrect)
    36  	t.Run("Creating a wallet with invalid params fails", testCreatingWalletWithInvalidParamsFails)
    37  	t.Run("Creating a wallet with valid params succeeds", testCreatingWalletWithValidParamsSucceeds)
    38  	t.Run("Creating a wallet that already exists fails", testCreatingWalletThatAlreadyExistsFails)
    39  	t.Run("Getting internal error during verification does not create the wallet", testGettingInternalErrorDuringVerificationDoesNotCreateWallet)
    40  	t.Run("Getting internal error during saving does not create the wallet", testGettingInternalErrorDuringSavingDoesNotCreateWallet)
    41  }
    42  
    43  func testAdminCreateWalletSchemaCorrect(t *testing.T) {
    44  	assertEqualSchema(t, "admin.create_wallet", api.AdminCreateWalletParams{}, api.AdminCreateWalletResult{})
    45  }
    46  
    47  func testCreatingWalletWithInvalidParamsFails(t *testing.T) {
    48  	tcs := []struct {
    49  		name          string
    50  		params        interface{}
    51  		expectedError error
    52  	}{
    53  		{
    54  			name:          "with nil params",
    55  			params:        nil,
    56  			expectedError: api.ErrParamsRequired,
    57  		}, {
    58  			name:          "with wrong type of params",
    59  			params:        "test",
    60  			expectedError: api.ErrParamsDoNotMatch,
    61  		}, {
    62  			name: "with empty name",
    63  			params: api.AdminCreateWalletParams{
    64  				Wallet:     "",
    65  				Passphrase: vgrand.RandomStr(5),
    66  			},
    67  			expectedError: api.ErrWalletIsRequired,
    68  		}, {
    69  			name: "with empty passphrase",
    70  			params: api.AdminCreateWalletParams{
    71  				Wallet:     vgrand.RandomStr(5),
    72  				Passphrase: "",
    73  			},
    74  			expectedError: api.ErrPassphraseIsRequired,
    75  		},
    76  	}
    77  
    78  	for _, tc := range tcs {
    79  		t.Run(tc.name, func(tt *testing.T) {
    80  			// given
    81  			ctx := context.Background()
    82  
    83  			// setup
    84  			handler := newCreateWalletHandler(tt)
    85  
    86  			// when
    87  			result, errorDetails := handler.handle(t, ctx, tc.params)
    88  
    89  			// then
    90  			require.Empty(tt, result)
    91  			assertInvalidParams(tt, errorDetails, tc.expectedError)
    92  		})
    93  	}
    94  }
    95  
    96  func testCreatingWalletWithValidParamsSucceeds(t *testing.T) {
    97  	// given
    98  	ctx := context.Background()
    99  	passphrase := vgrand.RandomStr(5)
   100  	name := vgrand.RandomStr(5)
   101  	var createdWallet wallet.Wallet
   102  
   103  	// setup
   104  	handler := newCreateWalletHandler(t)
   105  	// -- expected calls
   106  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, nil)
   107  	handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), passphrase).Times(1).DoAndReturn(func(_ context.Context, w wallet.Wallet, passphrase string) error {
   108  		createdWallet = w
   109  		return nil
   110  	})
   111  
   112  	// when
   113  	result, errorDetails := handler.handle(t, ctx, api.AdminCreateWalletParams{
   114  		Wallet:     name,
   115  		Passphrase: passphrase,
   116  	})
   117  
   118  	// then
   119  	require.Nil(t, errorDetails)
   120  	// Verify generated wallet.
   121  	assert.Equal(t, name, createdWallet.Name())
   122  	// Verify the first generated key.
   123  	assert.Len(t, createdWallet.ListKeyPairs(), 1)
   124  	keyPair := createdWallet.ListKeyPairs()[0]
   125  	assert.Equal(t, []wallet.Metadata{{Key: "name", Value: "Key 1"}}, keyPair.Metadata())
   126  	// Verify the result.
   127  	assert.Equal(t, name, result.Wallet.Name)
   128  	assert.NotEmpty(t, result.Wallet.RecoveryPhrase)
   129  	assert.Equal(t, uint32(2), result.Wallet.KeyDerivationVersion)
   130  	assert.Equal(t, keyPair.PublicKey(), result.Key.PublicKey)
   131  	assert.Equal(t, keyPair.AlgorithmName(), result.Key.Algorithm.Name)
   132  	assert.Equal(t, keyPair.AlgorithmVersion(), result.Key.Algorithm.Version)
   133  	assert.Equal(t, keyPair.Metadata(), result.Key.Metadata)
   134  }
   135  
   136  func testCreatingWalletThatAlreadyExistsFails(t *testing.T) {
   137  	// given
   138  	ctx := context.Background()
   139  	passphrase := vgrand.RandomStr(5)
   140  	name := vgrand.RandomStr(5)
   141  
   142  	// setup
   143  	handler := newCreateWalletHandler(t)
   144  	// -- expected calls
   145  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(true, nil)
   146  
   147  	// when
   148  	result, errorDetails := handler.handle(t, ctx, api.AdminCreateWalletParams{
   149  		Wallet:     name,
   150  		Passphrase: passphrase,
   151  	})
   152  
   153  	// then
   154  	require.NotNil(t, errorDetails)
   155  	assert.Empty(t, result)
   156  	assertInvalidParams(t, errorDetails, api.ErrWalletAlreadyExists)
   157  }
   158  
   159  func testGettingInternalErrorDuringVerificationDoesNotCreateWallet(t *testing.T) {
   160  	// given
   161  	ctx := context.Background()
   162  	passphrase := vgrand.RandomStr(5)
   163  	name := vgrand.RandomStr(5)
   164  
   165  	// setup
   166  	handler := newCreateWalletHandler(t)
   167  	// -- expected calls
   168  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, assert.AnError)
   169  
   170  	// when
   171  	result, errorDetails := handler.handle(t, ctx, api.AdminCreateWalletParams{
   172  		Wallet:     name,
   173  		Passphrase: passphrase,
   174  	})
   175  
   176  	// then
   177  	require.NotNil(t, errorDetails)
   178  	assert.Empty(t, result)
   179  	assertInternalError(t, errorDetails, fmt.Errorf("could not verify the wallet exists: %w", assert.AnError))
   180  }
   181  
   182  func testGettingInternalErrorDuringSavingDoesNotCreateWallet(t *testing.T) {
   183  	// given
   184  	ctx := context.Background()
   185  	passphrase := vgrand.RandomStr(5)
   186  	name := vgrand.RandomStr(5)
   187  
   188  	// setup
   189  	handler := newCreateWalletHandler(t)
   190  	// -- expected calls
   191  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, nil)
   192  	handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), passphrase).Times(1).Return(assert.AnError)
   193  
   194  	// when
   195  	result, errorDetails := handler.handle(t, ctx, api.AdminCreateWalletParams{
   196  		Wallet:     name,
   197  		Passphrase: passphrase,
   198  	})
   199  
   200  	// then
   201  	require.NotNil(t, errorDetails)
   202  	assert.Empty(t, result)
   203  	assertInternalError(t, errorDetails, fmt.Errorf("could not save the wallet: %w", assert.AnError))
   204  }
   205  
   206  type createWalletHandler struct {
   207  	*api.AdminCreateWallet
   208  	ctrl        *gomock.Controller
   209  	walletStore *mocks.MockWalletStore
   210  }
   211  
   212  func (h *createWalletHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminCreateWalletResult, *jsonrpc.ErrorDetails) {
   213  	t.Helper()
   214  
   215  	rawResult, err := h.Handle(ctx, params)
   216  	if rawResult != nil {
   217  		result, ok := rawResult.(api.AdminCreateWalletResult)
   218  		if !ok {
   219  			t.Fatal("AdminCreateWallet handler result is not a AdminCreateWalletResult")
   220  		}
   221  		return result, err
   222  	}
   223  	return api.AdminCreateWalletResult{}, err
   224  }
   225  
   226  func newCreateWalletHandler(t *testing.T) *createWalletHandler {
   227  	t.Helper()
   228  
   229  	ctrl := gomock.NewController(t)
   230  	walletStore := mocks.NewMockWalletStore(ctrl)
   231  
   232  	return &createWalletHandler{
   233  		AdminCreateWallet: api.NewAdminCreateWallet(walletStore),
   234  		ctrl:              ctrl,
   235  		walletStore:       walletStore,
   236  	}
   237  }