From bfe694763b555b22ef2f8816843f782906a311a6 Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Wed, 2 Sep 2020 11:44:15 -0700 Subject: [PATCH] Add tests for Jupyter Session (#12053) * add tests for Jupyter Session * remove comment and fix description * throw error if spark kernel provider is not mssql * use localize strings and add pythonEnvVarPath test --- .../notebook/src/common/localizedConstants.ts | 2 + .../src/jupyter/jupyterSessionManager.ts | 13 +--- .../src/test/model/sessionManager.test.ts | 70 +++++++++++++++++-- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/extensions/notebook/src/common/localizedConstants.ts b/extensions/notebook/src/common/localizedConstants.ts index bfcabe83c6..cf282585b6 100644 --- a/extensions/notebook/src/common/localizedConstants.ts +++ b/extensions/notebook/src/common/localizedConstants.ts @@ -12,6 +12,8 @@ export const msgNo = localize('msgNo', "No"); // Jupyter Constants /////////////////////////////////////////////////////// export const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', "This sample code loads the file into a data frame and shows the first 10 results."); +export const noBDCConnectionError = localize('noBDCConnectionError', "Spark kernels require a connection to a SQL Server Big Data Cluster master instance."); +export const providerNotValidError = localize('providerNotValidError', "Non-MSSQL providers are not supported for spark kernels."); // Book view-let constants export const allFiles = localize('allFiles', "All Files"); diff --git a/extensions/notebook/src/jupyter/jupyterSessionManager.ts b/extensions/notebook/src/jupyter/jupyterSessionManager.ts index 777edf208f..45347006bf 100644 --- a/extensions/notebook/src/jupyter/jupyterSessionManager.ts +++ b/extensions/notebook/src/jupyter/jupyterSessionManager.ts @@ -17,6 +17,7 @@ import { JupyterKernel } from './jupyterKernel'; import { Deferred } from '../common/promise'; import { JupyterServerInstallation } from './jupyterServerInstallation'; import * as bdc from 'bdc'; +import { noBDCConnectionError, providerNotValidError } from '../common/localizedConstants'; const configBase = { 'kernel_python_credentials': { @@ -287,7 +288,7 @@ export class JupyterSession implements nb.ISession { const endpoints = await this.getClusterEndpoints(connectionProfile.id); const gatewayEndpoint: utils.IEndpoint = endpoints?.find(ep => ep.serviceName.toLowerCase() === KNOX_ENDPOINT_GATEWAY); if (!gatewayEndpoint) { - return Promise.reject(new Error(localize('connectionNotValid', "Spark kernels require a connection to a SQL Server Big Data Cluster master instance."))); + throw new Error(noBDCConnectionError); } let gatewayHostAndPort = utils.getHostAndPortFromEndpoint(gatewayEndpoint.endpoint); connectionProfile.options[KNOX_ENDPOINT_SERVER] = gatewayHostAndPort.host; @@ -308,7 +309,7 @@ export class JupyterSession implements nb.ISession { } } else { - connectionProfile.options[KNOX_ENDPOINT_PORT] = this.getKnoxPortOrDefault(connectionProfile); + throw new Error(providerNotValidError); } this.setHostAndPort(':', connectionProfile); this.setHostAndPort(',', connectionProfile); @@ -355,14 +356,6 @@ export class JupyterSession implements nb.ISession { config.ignore_ssl_errors = utils.getIgnoreSslVerificationConfigSetting(); } - private getKnoxPortOrDefault(connectionProfile: IConnectionProfile): string { - let port = connectionProfile.options[KNOX_ENDPOINT_PORT]; - if (!port) { - port = '30443'; - } - return port; - } - private async getClusterEndpoints(profileId: string): Promise { let serverInfo: ServerInfo = await connection.getServerInfo(profileId); if (!serverInfo || !serverInfo.options) { diff --git a/extensions/notebook/src/test/model/sessionManager.test.ts b/extensions/notebook/src/test/model/sessionManager.test.ts index 751cf10dc3..23fe83dea4 100644 --- a/extensions/notebook/src/test/model/sessionManager.test.ts +++ b/extensions/notebook/src/test/model/sessionManager.test.ts @@ -18,6 +18,7 @@ import 'mocha'; import { JupyterSessionManager, JupyterSession } from '../../jupyter/jupyterSessionManager'; import { Deferred } from '../../common/promise'; import { SessionStub, KernelStub, FutureStub } from '../common'; +import { noBDCConnectionError, providerNotValidError } from '../../common/localizedConstants'; export class TestClusterController implements bdc.IClusterController { getClusterConfig(): Promise { @@ -232,7 +233,7 @@ describe('Jupyter Session', function (): void { should(JSON.parse(result) === expectedResult); }); - it('should configure connection correctly', async function (): Promise { + it('should configure connection correctly for MSSQL and SqlLogin auth type', async function (): Promise { let connectionProfile: IConnectionProfile = { authenticationType: '', connectionName: '', @@ -256,9 +257,6 @@ describe('Jupyter Session', function (): void { let credentials = { [ConnectionOptionSpecialType.password]: 'password' }; sinon.stub(connection, 'getCredentials').returns(Promise.resolve(credentials)); - // Should throw error if there is no connection to big data cluster - await should(session.configureConnection(connectionProfile)).be.rejectedWith('Spark kernels require a connection to a SQL Server Big Data Cluster master instance.'); - // Set up connection info to big data cluster const mockServerInfo: ServerInfo = { serverMajorVersion: 0, @@ -306,4 +304,68 @@ describe('Jupyter Session', function (): void { should(connectionProfile.options['knoxport']).equal('port'); should(connectionProfile.options['user']).equal('knoxUsername'); }); + + it('configure connection should throw error if there is no connection to big data cluster', async function (): Promise { + let connectionProfile: IConnectionProfile = { + authenticationType: '', + connectionName: '', + databaseName: '', + id: 'id', + providerName: 'MSSQL', + options: { + authenticationType: 'SqlLogin', + }, + password: '', + savePassword: false, + saveProfile: false, + serverName: '', + userName: '' + }; + let futureMock = TypeMoq.Mock.ofType(FutureStub); + let kernelMock = TypeMoq.Mock.ofType(KernelStub); + kernelMock.setup(k => k.name).returns(() => 'spark'); + kernelMock.setup(m => m.requestExecute(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => futureMock.object); + mockJupyterSession.setup(s => s.kernel).returns(() => kernelMock.object); + let credentials = { [ConnectionOptionSpecialType.password]: 'password' }; + sinon.stub(connection, 'getCredentials').returns(Promise.resolve(credentials)); + await should(session.configureConnection(connectionProfile)).be.rejectedWith(noBDCConnectionError); + }); + + it('configure connection should throw error if provider is not MSSQL for spark kernel', async function (): Promise { + let connectionProfile: IConnectionProfile = { + authenticationType: '', + connectionName: '', + databaseName: '', + id: 'id', + providerName: 'provider', + options: { + authenticationType: 'SqlLogin', + }, + password: '', + savePassword: false, + saveProfile: false, + serverName: '', + userName: '' + }; + let futureMock = TypeMoq.Mock.ofType(FutureStub); + let kernelMock = TypeMoq.Mock.ofType(KernelStub); + kernelMock.setup(k => k.name).returns(() => 'spark'); + kernelMock.setup(m => m.requestExecute(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => futureMock.object); + mockJupyterSession.setup(s => s.kernel).returns(() => kernelMock.object); + await should(session.configureConnection(connectionProfile)).be.rejectedWith(providerNotValidError); + }); + + it('should set environment variables correctly', function (): void { + let futureMock = TypeMoq.Mock.ofType(FutureStub); + let kernelMock = TypeMoq.Mock.ofType(KernelStub); + kernelMock.setup(m => m.requestExecute(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => futureMock.object); + let spy = sinon.spy(kernelMock.object, 'requestExecute'); + mockJupyterSession.setup(s => s.kernel).returns(() => kernelMock.object); + mockJupyterSession.setup(s => s.path).returns(() => 'path'); + let newSession = new JupyterSession(mockJupyterSession.object, undefined, false, 'pythonEnvVarPath'); + should(newSession).not.be.undefined(); + sinon.assert.calledOnce(spy); + let args = spy.getCall(0).args; + should(args[0].code.includes('pythonEnvVarPath')); + }); });