'use strict' ; import * as sinon from 'sinon' ; import { TestUtil } from '../TestUtil' ; import * as vscode from 'vscode' ; import { ExtensionCommands } from '../../ExtensionCommands' ; import { WalletTreeItem } from '../../extension/explorer/wallets/WalletTreeItem' ; import { BlockchainWalletExplorerProvider } from '../../extension/explorer/walletExplorer' ; import { UserInputUtil } from '../../extension/commands/UserInputUtil' ; import * as path from 'path' ; import * as fs from 'fs-extra' ; import { VSCodeBlockchainOutputAdapter } from '../../extension/logging/VSCodeBlockchainOutputAdapter' ; import { Reporter } from '../../extension/util/Reporter' ; import { LogType , FabricWalletRegistryEntry , FabricWalletRegistry , FabricRuntimeUtil } from 'ibm-blockchain-platform-common' ; import { ExtensionUtil } from '../../extension/util/ExtensionUtil' ; describe ( 'exportWalletCommand' , ( ) => { const mySandBox = sinon . createSandbox ( ) ; let walletTreeItem ; let getWorkspaceFoldersStub ; let workspaceFolder ; let saveDialogBoxStub ; const fakeTargetPath = path . join ( '/' , 'a' , 'fake' , 'path' ) ; let fsEnsureDirStub ; let fsReaddirStub ; let fsCopyStub ; let fsPathExistsStub ; let fsRemoveStub ; let logSpy ; let sendTelemetryEventStub ; let showWalletsQuickPickBoxStub ; before ( async ( ) => { await TestUtil . setupTests ( mySandBox ) ; } ) ; beforeEach ( async ( ) => { const blockchainWalletExplorerProvider = ExtensionUtil . getBlockchainWalletExplorerProvider ( ) ; walletTreeItem = new WalletTreeItem ( blockchainWalletExplorerProvider , 'myWallet' , [ ] , 0 , { name : 'myWallet' , walletPath : '/some/path' , managedWallet : false } ) ; workspaceFolder = { name : 'myFolder' , uri : vscode . Uri . file ( 'myPath' ) } ; getWorkspaceFoldersStub = mySandBox . stub ( UserInputUtil , 'getWorkspaceFolders' ) . returns ( [ workspaceFolder ] ) ; saveDialogBoxStub = mySandBox . stub ( vscode . window , 'showSaveDialog' ) . resolves ( vscode . Uri . file ( fakeTargetPath ) ) ; fsEnsureDirStub = mySandBox . stub ( fs , 'ensureDir' ) . resolves ( ) ; fsReaddirStub = mySandBox . stub ( fs , 'readdir' ) ; fsReaddirStub . onFirstCall ( ) . resolves ( [ 'admin.id' , 'notAnId' , 'another.id' ] ) ; fsCopyStub = mySandBox . stub ( fs , 'copy' ) . resolves ( ) ; fsPathExistsStub = mySandBox . stub ( fs , 'pathExists' ) . resolves ( ) ; fsRemoveStub = mySandBox . stub ( fs , 'remove' ) . resolves ( ) ; logSpy = mySandBox . spy ( VSCodeBlockchainOutputAdapter . instance ( ) , 'log' ) ; sendTelemetryEventStub = mySandBox . stub ( Reporter . instance ( ) , 'sendTelemetryEvent' ) ; showWalletsQuickPickBoxStub = mySandBox . stub ( UserInputUtil , 'showWalletsQuickPickBox' ) . resolves ( { label : 'myWallet' , data : { walletPath : '/some/path' , name : 'myWallet' } } ) ; } ) ; afterEach ( async ( ) => { mySandBox . restore ( ) ; } ) ; it ( 'should export the wallet by right-clicking on a wallet in the tree' , async ( ) => { await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET , walletTreeItem ) ; showWalletsQuickPickBoxStub . should . not . have . been . called ; fsEnsureDirStub . should . have . been . calledOnce ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . have . been . calledTwice ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'admin.id' ) , path . join ( fakeTargetPath , 'admin.id' ) ) ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'another.id' ) , path . join ( fakeTargetPath , 'another.id' ) ) ; fsCopyStub . should . not . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'notAnId' ) , path . join ( fakeTargetPath , 'notAnId' ) ) ; logSpy . should . have . been . calledWithExactly ( LogType . SUCCESS , `Successfully exported wallet ${ walletTreeItem . registryEntry . name } to ${ fakeTargetPath }` ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'exportWallet' ) ; } ) ; it ( 'should export the wallet when command is called from the command palette' , async ( ) => { await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; showWalletsQuickPickBoxStub . should . have . been . calledOnce ; fsEnsureDirStub . should . have . been . calledOnce ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . have . been . calledTwice ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'admin.id' ) , path . join ( fakeTargetPath , 'admin.id' ) ) ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'another.id' ) , path . join ( fakeTargetPath , 'another.id' ) ) ; fsCopyStub . should . not . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'notAnId' ) , path . join ( fakeTargetPath , 'notAnId' ) ) ; logSpy . should . have . been . calledWithExactly ( LogType . SUCCESS , `Successfully exported wallet ${ walletTreeItem . registryEntry . name } to ${ fakeTargetPath }` ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'exportWallet' ) ; } ) ; it ( 'should handle the user cancelling choosing a wallet to export' , async ( ) => { showWalletsQuickPickBoxStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; fsEnsureDirStub . should . not . have . been . called ; fsReaddirStub . should . not . have . been . called ; fsCopyStub . should . not . have . been . called ; logSpy . should . have . been . calledOnceWithExactly ( LogType . INFO , undefined , 'exportWalletCommand' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle no open workspace folders' , async ( ) => { getWorkspaceFoldersStub . returns ( [ ] ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; showWalletsQuickPickBoxStub . should . have . been . calledOnce ; saveDialogBoxStub . should . have . been . calledOnce ; fsEnsureDirStub . should . have . been . calledOnce ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . have . been . calledTwice ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'admin.id' ) , path . join ( fakeTargetPath , 'admin.id' ) ) ; fsCopyStub . should . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'another.id' ) , path . join ( fakeTargetPath , 'another.id' ) ) ; fsCopyStub . should . not . have . been . calledWith ( path . join ( walletTreeItem . registryEntry . walletPath , 'notAnId' ) , path . join ( fakeTargetPath , 'notAnId' ) ) ; logSpy . should . have . been . calledWithExactly ( LogType . SUCCESS , `Successfully exported wallet ${ walletTreeItem . registryEntry . name } to ${ fakeTargetPath }` ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'exportWallet' ) ; } ) ; it ( 'should handle the user cancelling chosing where to export the wallet to' , async ( ) => { saveDialogBoxStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; fsEnsureDirStub . should . not . have . been . called ; fsReaddirStub . should . not . have . been . called ; fsCopyStub . should . not . have . been . called ; logSpy . should . have . been . calledOnceWithExactly ( LogType . INFO , undefined , 'exportWalletCommand' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle errors when exporting the wallet' , async ( ) => { const error = new Error ( 'what the fabric has happened' ) ; fsEnsureDirStub . rejects ( error ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; fsEnsureDirStub . should . have . been . calledOnceWithExactly ( fakeTargetPath ) ; logSpy . should . have . been . calledWithExactly ( LogType . ERROR , `Issue exporting wallet: ${ error . message }` , `Issue exporting wallet: ${ error . toString ( ) }` ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle errors when exporting the wallet and clean up anything already exported' , async ( ) => { fsPathExistsStub . resolves ( true ) ; const error = new Error ( 'what the fabric has happened' ) ; fsCopyStub . rejects ( error ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; fsEnsureDirStub . should . have . been . calledOnce ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . have . been . calledOnceWithExactly ( path . join ( walletTreeItem . registryEntry . walletPath , 'admin.id' ) , path . join ( fakeTargetPath , 'admin.id' ) ) ; fsRemoveStub . should . have . been . calledOnceWithExactly ( fakeTargetPath ) ; logSpy . should . have . been . calledWithExactly ( LogType . ERROR , `Issue exporting wallet: ${ error . message }` , `Issue exporting wallet: ${ error . toString ( ) }` ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should be able to export local wallet' , async ( ) => { fsCopyStub . restore ( ) ; await TestUtil . startLocalFabric ( ) ; fsCopyStub = mySandBox . stub ( fs , 'copy' ) . resolves ( ) ; const localWallet = await FabricWalletRegistry . instance ( ) . get ( 'Org1' , FabricRuntimeUtil . LOCAL_FABRIC ) ; showWalletsQuickPickBoxStub . resolves ( { label : localWallet . displayName , data : localWallet } ) ; fsEnsureDirStub . resetHistory ( ) ; fsCopyStub . resetHistory ( ) ; logSpy . resetHistory ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; showWalletsQuickPickBoxStub . should . have . been . calledOnce ; fsEnsureDirStub . should . have . been . calledOnce ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . have . been . calledTwice ; fsCopyStub . should . have . been . calledWith ( path . join ( localWallet . walletPath , 'admin.id' ) , path . join ( fakeTargetPath , 'admin.id' ) ) ; fsCopyStub . should . have . been . calledWith ( path . join ( localWallet . walletPath , 'another.id' ) , path . join ( fakeTargetPath , 'another.id' ) ) ; fsCopyStub . should . not . have . been . calledWith ( path . join ( localWallet . walletPath , 'notAnId' ) , path . join ( fakeTargetPath , 'notAnId' ) ) ; logSpy . should . have . been . calledWithExactly ( LogType . SUCCESS , `Successfully exported wallet ${ localWallet . displayName } to ${ fakeTargetPath }` ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'exportWallet' ) ; } ) ; it ( 'should handle exporting an empty wallet' , async ( ) => { fsReaddirStub . onFirstCall ( ) . resolves ( [ ] ) ; await vscode . commands . executeCommand ( ExtensionCommands . EXPORT_WALLET ) ; showWalletsQuickPickBoxStub . should . have . been . calledOnce ; fsEnsureDirStub . should . have . been . calledOnceWithExactly ( fakeTargetPath ) ; fsReaddirStub . should . have . been . calledOnce ; fsCopyStub . should . not . have . been . called ; logSpy . should . have . been . calledWithExactly ( LogType . SUCCESS , `Successfully exported wallet ${ walletTreeItem . registryEntry . name } to ${ fakeTargetPath }` ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'exportWallet' ) ; } ) ; } ) ;