'use strict' ; import * as vscode from 'vscode' ; import * as path from 'path' ; import * as chai from 'chai' ; import * as sinon from 'sinon' ; import * as sinonChai from 'sinon-chai' ; import { TestUtil } from '../TestUtil' ; import { UserInputUtil } from '../../extension/commands/UserInputUtil' ; import { FabricGatewayHelper } from '../../extension/fabric/FabricGatewayHelper' ; import { VSCodeBlockchainOutputAdapter } from '../../extension/logging/VSCodeBlockchainOutputAdapter' ; import { ExtensionCommands } from '../../ExtensionCommands' ; import { Reporter } from '../../extension/util/Reporter' ; import { FabricEnvironmentRegistryEntry , FabricNode , LogType , FabricGatewayRegistry , FabricGatewayRegistryEntry , FabricEnvironmentRegistry , EnvironmentType } from 'ibm-blockchain-platform-common' ; chai . should ( ) ; chai . use ( sinonChai ) ; describe ( 'AddGatewayCommand' , ( ) => { const rootPath = path . dirname ( __dirname ) ; let mySandBox ; let showInputBoxStub ; let methodChooserStub ; let browseStub ; let copyConnectionProfileStub ; let executeCommandSpy ; let sendTelemetryEventStub ; let logSpy ; before ( async ( ) => { mySandBox = sinon . createSandbox ( ) ; await TestUtil . setupTests ( mySandBox ) ; } ) ; beforeEach ( ( ) => { logSpy = mySandBox . spy ( VSCodeBlockchainOutputAdapter . instance ( ) , 'log' ) ; showInputBoxStub = mySandBox . stub ( vscode . window , 'showInputBox' ) ; executeCommandSpy = mySandBox . spy ( vscode . commands , 'executeCommand' ) ; sendTelemetryEventStub = mySandBox . stub ( Reporter . instance ( ) , 'sendTelemetryEvent' ) ; methodChooserStub = mySandBox . stub ( UserInputUtil , 'showQuickPick' ) . resolves ( UserInputUtil . ADD_GATEWAY_FROM_CCP ) ; } ) ; afterEach ( ( ) => { mySandBox . restore ( ) ; } ) ; describe ( 'addGateway with connection profile' , ( ) => { let ccpOrg1Original ; let ccpOrg1Expected ; let ccpOrg2Original ; let ccpOrg2Expected ; beforeEach ( async ( ) => { ccpOrg1Original = path . join ( rootPath , '../../test/data/connectionOne/connection.json' ) ; ccpOrg1Expected = path . join ( 'blockchain' , 'extension' , 'directory' , 'gatewayOne' , 'connection.json' ) ; ccpOrg2Original = path . join ( rootPath , '../../test/data/connectionTwo/connection.json' ) ; ccpOrg2Expected = path . join ( 'blockchain' , 'extension' , 'directory' , 'gatewayTwo' , 'connection.json' ) ; await FabricGatewayRegistry . instance ( ) . clear ( ) ; await FabricEnvironmentRegistry . instance ( ) . clear ( ) ; browseStub = mySandBox . stub ( UserInputUtil , 'browse' ) ; copyConnectionProfileStub = mySandBox . stub ( FabricGatewayHelper , 'copyConnectionProfile' ) ; copyConnectionProfileStub . onFirstCall ( ) . resolves ( ccpOrg1Expected ) ; copyConnectionProfileStub . onSecondCall ( ) . resolves ( ccpOrg2Expected ) ; } ) ; afterEach ( async ( ) => { mySandBox . restore ( ) ; } ) ; it ( 'should test a gateway can be added' , async ( ) => { showInputBoxStub . onFirstCall ( ) . resolves ( 'myGateway' ) ; browseStub . onFirstCall ( ) . resolves ( ccpOrg1Original ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 1 ) ; gateways [ 0 ] . should . deep . equal ( { name : 'myGateway' , associatedWallet : '' , connectionProfilePath : ccpOrg1Expected } ) ; executeCommandSpy . should . have . been . calledWith ( ExtensionCommands . REFRESH_GATEWAYS ) ; copyConnectionProfileStub . should . have . been . calledOnce ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 1 ) . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'addGatewayCommand' ) ; } ) ; it ( 'should test multiple gateways can be added' , async ( ) => { showInputBoxStub . onFirstCall ( ) . resolves ( 'myGatewayOne' ) ; browseStub . onFirstCall ( ) . resolves ( ccpOrg1Original ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; showInputBoxStub . reset ( ) ; showInputBoxStub . onFirstCall ( ) . resolves ( 'myGatewayTwo' ) ; browseStub . reset ( ) ; browseStub . onFirstCall ( ) . resolves ( ccpOrg2Original ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 2 ) ; gateways [ 0 ] . should . deep . equal ( { name : 'myGatewayOne' , associatedWallet : '' , connectionProfilePath : ccpOrg1Expected } ) ; gateways [ 1 ] . should . deep . equal ( { name : 'myGatewayTwo' , associatedWallet : '' , connectionProfilePath : ccpOrg2Expected } ) ; executeCommandSpy . should . have . been . calledWith ( ExtensionCommands . REFRESH_GATEWAYS ) ; copyConnectionProfileStub . should . have . been . calledTwice ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 1 ) . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; logSpy . getCall ( 2 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 3 ) . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . have . been . calledTwice ; sendTelemetryEventStub . should . have . been . calledWithExactly ( 'addGatewayCommand' ) ; } ) ; it ( 'should test adding a gateway can be cancelled when giving a gateway name' , async ( ) => { showInputBoxStub . onFirstCall ( ) . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . should . have . been . calledOnceWithExactly ( LogType . INFO , undefined , 'addGateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should test adding a gateway can be cancelled when giving a connection profile' , async ( ) => { showInputBoxStub . onFirstCall ( ) . resolves ( 'myGateway' ) ; browseStub . onFirstCall ( ) . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; browseStub . should . have . been . calledOnce ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . should . have . been . calledOnceWithExactly ( LogType . INFO , undefined , 'addGateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should error if a gateway with the same name already exists' , async ( ) => { const error = new Error ( 'A gateway with this name already exists.' ) ; showInputBoxStub . resolves ( 'myGatewayOne' ) ; browseStub . onFirstCall ( ) . resolves ( ccpOrg1Original ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 1 ) ; gateways [ 0 ] . should . deep . equal ( { name : 'myGatewayOne' , associatedWallet : '' , connectionProfilePath : ccpOrg1Expected } ) ; executeCommandSpy . should . have . been . calledWith ( ExtensionCommands . REFRESH_GATEWAYS ) ; copyConnectionProfileStub . should . have . been . calledOnce ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 1 ) . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; logSpy . getCall ( 2 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 3 ) . should . have . been . calledWith ( LogType . ERROR , `Failed to add a new gateway: ${ error . message }` , `Failed to add a new gateway: ${ error . toString ( ) }` ) ; sendTelemetryEventStub . should . have . been . calledOnce ; } ) ; } ) ; describe ( 'add gateway from environment' , ( ) => { let showEnvironmentQuickPickStub ; let showOrgQuickPickStub ; let showNodesInEnvironmentQuickPickStub ; let generateConnectionProfileStub ; let peerNode ; let caNode ; let environmentRegistryEntry ; let connectionProfilePath ; beforeEach ( async ( ) => { await FabricGatewayRegistry . instance ( ) . clear ( ) ; methodChooserStub . resolves ( UserInputUtil . ADD_GATEWAY_FROM_ENVIRONMENT ) ; await FabricEnvironmentRegistry . instance ( ) . clear ( ) ; environmentRegistryEntry = new FabricEnvironmentRegistryEntry ( ) ; environmentRegistryEntry . name = 'myEnv' ; environmentRegistryEntry . environmentType = EnvironmentType . ENVIRONMENT ; await FabricEnvironmentRegistry . instance ( ) . add ( environmentRegistryEntry ) ; showEnvironmentQuickPickStub = mySandBox . stub ( UserInputUtil , 'showFabricEnvironmentQuickPickBox' ) . resolves ( { label : 'myEnv' , data : environmentRegistryEntry } ) ; peerNode = FabricNode . newPeer ( 'peer0.org1.example.com' , 'peer0.org1.example.com' , 'grpc://localhost:7051' , 'Org1' , 'admin' , 'Org1MSP' ) ; showOrgQuickPickStub = mySandBox . stub ( UserInputUtil , 'showOrgQuickPick' ) . resolves ( { label : 'Org1MSP' , data : peerNode } ) ; caNode = FabricNode . newCertificateAuthority ( 'ca.org1.example.com' , 'ca.org1.example.com' , 'http://localhost:7054' , 'ca_name' , 'Org1' , 'admin' , 'Org1MSP' , 'admin' , 'adminpw' ) ; showNodesInEnvironmentQuickPickStub = mySandBox . stub ( UserInputUtil , 'showNodesInEnvironmentQuickPick' ) . resolves ( { label : 'ca.org1.example.com' , data : caNode } ) ; connectionProfilePath = path . join ( 'blockchain' , 'extension' , 'directory' , 'gatewayOne' , 'connection.json' ) ; generateConnectionProfileStub = mySandBox . stub ( FabricGatewayHelper , 'generateConnectionProfile' ) . resolves ( connectionProfilePath ) ; showInputBoxStub . resolves ( 'myGateway' ) ; } ) ; afterEach ( async ( ) => { await FabricEnvironmentRegistry . instance ( ) . clear ( ) ; } ) ; it ( 'should create a gateway from an environment' , async ( ) => { await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 1 ) ; gateways [ 0 ] . should . deep . equal ( { name : 'myGateway' , connectionProfilePath , fromEnvironment : 'myEnv' , environmentGroup : 'myEnv' , associatedWallet : 'Org1' } ) ; executeCommandSpy . should . have . been . calledWith ( ExtensionCommands . REFRESH_GATEWAYS ) ; generateConnectionProfileStub . should . have . been . calledOnceWith ( 'myGateway' , peerNode , caNode ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 1 ) . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . have . been . calledOnceWithExactly ( 'addGatewayCommand' ) ; } ) ; it ( 'should error if there are no non-local environments to create the gateway from' , async ( ) => { mySandBox . stub ( FabricEnvironmentRegistry . instance ( ) , 'getAll' ) . resolves ( [ ] ) ; const error = new Error ( `No environments to choose from. Gateways cannot be created from local environments.` ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; executeCommandSpy . should . have . been . calledWith ( ExtensionCommands . REFRESH_GATEWAYS ) ; generateConnectionProfileStub . should . not . have . been . calledOnceWith ( 'myGateway' , peerNode , caNode ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . getCall ( 1 ) . should . have . been . calledWith ( LogType . ERROR , `Failed to add a new gateway: ${ error . message }` , `Failed to add a new gateway: ${ error . toString ( ) }` ) ; sendTelemetryEventStub . should . not . have . been . calledOnceWithExactly ( 'addGatewayCommand' ) ; } ) ; it ( 'should handle cancel of choosing gateway name' , async ( ) => { showInputBoxStub . onFirstCall ( ) . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . should . have . been . calledOnceWithExactly ( LogType . INFO , undefined , 'addGateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle cancel of choosing method to add gateway' , async ( ) => { methodChooserStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . should . not . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle cancel of choosing environment' , async ( ) => { showEnvironmentQuickPickStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . should . not . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle cancel choosing org' , async ( ) => { showOrgQuickPickStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . should . not . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle cancel choosing ca' , async ( ) => { showNodesInEnvironmentQuickPickStub . resolves ( ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways . length . should . equal ( 0 ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . should . not . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . not . have . been . called ; } ) ; it ( 'should handle no ca found' , async ( ) => { showNodesInEnvironmentQuickPickStub . rejects ( 'some error' ) ; await vscode . commands . executeCommand ( ExtensionCommands . ADD_GATEWAY ) ; const gateways = await FabricGatewayRegistry . instance ( ) . getAll ( ) ; gateways [ 0 ] . should . deep . equal ( { name : 'myGateway' , connectionProfilePath , associatedWallet : 'Org1' , fromEnvironment : 'myEnv' , environmentGroup : 'myEnv' } ) ; logSpy . getCall ( 0 ) . should . have . been . calledWith ( LogType . INFO , undefined , 'addGateway' ) ; logSpy . should . have . been . calledWith ( LogType . INFO , 'Could not find a certifcate authority to add to the connection profile' ) ; logSpy . should . have . been . calledWith ( LogType . SUCCESS , 'Successfully added a new gateway' ) ; sendTelemetryEventStub . should . have . been . called ; } ) ; } ) ; } ) ;