Add some package management unit tests for JupyterServerInstallation (#11777)

This commit is contained in:
Cory Rivera
2020-08-13 13:12:28 -07:00
committed by GitHub
parent c66726cdb5
commit d68433ec22
3 changed files with 214 additions and 19 deletions

View File

@@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { ConfigurePythonWizard, ConfigurePythonModel } from '../../dialog/configurePython/configurePythonWizard';
import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
import { ConfigurePathPage } from '../../dialog/configurePython/configurePathPage';
import * as should from 'should';
import { PickPackagesPage } from '../../dialog/configurePython/pickPackagesPage';
import { python3DisplayName, allKernelsName } from '../../common/constants';
import { TestContext, createViewContext, TestButton } from '../common';
import { EventEmitter } from 'vscode';
describe('Configure Python Wizard', function () {
let testWizard: ConfigurePythonWizard;
let viewContext: TestContext;
let testInstallation: JupyterServerInstallation;
beforeEach(() => {
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation);
mockInstall.setup(i => i.getInstalledPipPackages(TypeMoq.It.isAnyString())).returns(() => Promise.resolve([]));
mockInstall.setup(i => i.getRequiredPackagesForKernel(TypeMoq.It.isAnyString())).returns(() => [{ name: 'TestPkg', version: '1.0.0'}]);
testInstallation = mockInstall.object;
let mockDoneButton = new TestButton(new EventEmitter<void>());
let mockNextButton = new TestButton(new EventEmitter<void>());
let mockWizard = TypeMoq.Mock.ofType<azdata.window.Wizard>();
mockWizard.setup(w => w.doneButton).returns(() => mockDoneButton);
mockWizard.setup(w => w.nextButton).returns(() => mockNextButton);
let mockPythonWizard = TypeMoq.Mock.ofType(ConfigurePythonWizard);
mockPythonWizard.setup(w => w.showErrorMessage(TypeMoq.It.isAnyString()));
mockPythonWizard.setup(w => w.wizard).returns(() => mockWizard.object);
testWizard = mockPythonWizard.object;
viewContext = createViewContext();
});
// These wizard tests are disabled due to errors with disposable objects
//
// it('Start wizard test', async () => {
// let wizard = new ConfigurePythonWizard(testInstallation);
// await wizard.start();
// await wizard.close();
// await should(wizard.setupComplete).be.resolved();
// });
// it('Reject setup on cancel test', async () => {
// let wizard = new ConfigurePythonWizard(testInstallation);
// await wizard.start(undefined, true);
// await wizard.close();
// await should(wizard.setupComplete).be.rejected();
// });
// it('Error message test', async () => {
// let wizard = new ConfigurePythonWizard(testInstallation);
// await wizard.start();
// should(wizard.wizard.message).be.undefined();
// let testMsg = 'Test message';
// wizard.showErrorMessage(testMsg);
// should(wizard.wizard.message.text).be.equal(testMsg);
// should(wizard.wizard.message.level).be.equal(azdata.window.MessageLevel.Error);
// wizard.clearStatusMessage();
// should(wizard.wizard.message).be.undefined();
// await wizard.close();
// });
it('Configure Path Page test', async () => {
let testPythonLocation = '/not/a/real/path';
let model = <ConfigurePythonModel>{
useExistingPython: true,
pythonPathsPromise: Promise.resolve([{
installDir: testPythonLocation,
version: '4000'
}])
};
let page = azdata.window.createWizardPage('Page 1');
let configurePathPage = new ConfigurePathPage(testWizard, page, model, viewContext.view);
should(await configurePathPage.initialize()).be.true();
// First page, so onPageEnter should do nothing
await should(configurePathPage.onPageEnter()).be.resolved();
should(testWizard.wizard.nextButton.enabled).be.true();
should(await configurePathPage.onPageLeave()).be.true();
should(model.useExistingPython).be.true();
should(model.pythonLocation).be.equal(testPythonLocation);
});
it('Pick Packages Page test', async () => {
let model = <ConfigurePythonModel>{
kernelName: allKernelsName,
installation: testInstallation,
pythonLocation: '/not/a/real/path',
useExistingPython: true
};
let page = azdata.window.createWizardPage('Page 2');
let pickPackagesPage = new PickPackagesPage(testWizard, page, model, viewContext.view);
should(await pickPackagesPage.initialize()).be.true();
should((<any>pickPackagesPage).kernelLabel).not.be.undefined();
should((<any>pickPackagesPage).kernelDropdown).be.undefined();
// Last page, so onPageLeave should do nothing
should(await pickPackagesPage.onPageLeave()).be.true();
await should(pickPackagesPage.onPageEnter()).be.resolved();
should(testWizard.wizard.doneButton.enabled).be.true();
should(model.packagesToInstall).be.deepEqual(testInstallation.getRequiredPackagesForKernel(allKernelsName));
});
it('Undefined kernel test', async () => {
let model = <ConfigurePythonModel>{
kernelName: undefined,
installation: testInstallation,
pythonLocation: '/not/a/real/path',
useExistingPython: true
};
let page = azdata.window.createWizardPage('Page 2');
let pickPackagesPage = new PickPackagesPage(testWizard, page, model, viewContext.view);
should(await pickPackagesPage.initialize()).be.true();
should((<any>pickPackagesPage).kernelLabel).be.undefined();
should((<any>pickPackagesPage).kernelDropdown).not.be.undefined();
await should(pickPackagesPage.onPageEnter()).be.resolved();
should(model.packagesToInstall).be.deepEqual(testInstallation.getRequiredPackagesForKernel(python3DisplayName));
});
});

View File

@@ -0,0 +1,196 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as should from 'should';
import * as sinon from 'sinon';
import * as TypeMoq from 'typemoq';
import * as uuid from 'uuid';
import * as fs from 'fs-extra';
import { JupyterServerInstallation, PythonPkgDetails } from '../../jupyter/jupyterServerInstallation';
describe('Jupyter Server Installation', function () {
let outputChannelStub: TypeMoq.IMock<vscode.OutputChannel>;
let installation: JupyterServerInstallation;
beforeEach(function (): void {
outputChannelStub = TypeMoq.Mock.ofType<vscode.OutputChannel>();
outputChannelStub.setup(c => c.show(TypeMoq.It.isAny()));
outputChannelStub.setup(c => c.appendLine(TypeMoq.It.isAnyString()));
installation = new JupyterServerInstallation('', outputChannelStub.object);
});
afterEach(function (): void {
sinon.restore();
});
it('Get pip packages', async function() {
// Should return nothing if passed an invalid python path
let fakePath = uuid.v4();
let pkgResult = await installation.getInstalledPipPackages(fakePath);
should(pkgResult).not.be.undefined();
should(pkgResult.length).be.equal(0);
// Should return nothing if python is not installed
sinon.stub(JupyterServerInstallation, 'isPythonInstalled').returns(false);
pkgResult = await installation.getInstalledPipPackages();
should(pkgResult).not.be.undefined();
should(pkgResult.length).be.equal(0);
// Should return nothing on error
sinon.restore();
sinon.stub(JupyterServerInstallation, 'isPythonInstalled').returns(true);
sinon.stub(installation, 'executeBufferedCommand').rejects(new Error('Expected test failure.'));
pkgResult = await installation.getInstalledPipPackages();
should(pkgResult).not.be.undefined();
should(pkgResult.length).be.equal(0);
outputChannelStub.verify(c => c.appendLine(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
// Normal use case
sinon.restore();
let testPackages: PythonPkgDetails[] = [{
name: 'TestPkg1',
version: '1.2.3'
}, {
name: 'TestPkg2',
version: '4.5.6'
}];
sinon.stub(JupyterServerInstallation, 'isPythonInstalled').returns(true);
sinon.stub(installation, 'executeBufferedCommand').resolves(JSON.stringify(testPackages));
pkgResult = await installation.getInstalledPipPackages();
should(pkgResult).be.deepEqual(testPackages);
});
it('Install pip package', async function() {
let commandStub = sinon.stub(installation, 'executeStreamedCommand').resolves();
// Should not execute any commands when passed an empty package list
await installation.installPipPackages(undefined, false);
should(commandStub.called).be.false();
await installation.installPipPackages([], false);
should(commandStub.called).be.false();
// Install package using exact version
let testPackages = [{
name: 'TestPkg1',
version: '1.2.3'
}, {
name: 'TestPkg2',
version: '4.5.6'
}];
await installation.installPipPackages(testPackages, false);
should(commandStub.calledOnce).be.true();
let commandStr = commandStub.args[0][0] as string;
should(commandStr.includes('"TestPkg1==1.2.3" "TestPkg2==4.5.6"')).be.true();
// Install package using minimum version
await installation.installPipPackages(testPackages, true);
should(commandStub.calledTwice).be.true();
commandStr = commandStub.args[1][0] as string;
should(commandStr.includes('"TestPkg1>=1.2.3" "TestPkg2>=4.5.6"')).be.true();
});
it('Uninstall pip package', async function() {
let commandStub = sinon.stub(installation, 'executeStreamedCommand').resolves();
let testPackages = [{
name: 'jupyter',
version: '1.0.0'
}, {
name: 'TestPkg2',
version: '4.5.6'
}];
await installation.uninstallPipPackages(testPackages);
should(commandStub.calledOnce).be.true();
let commandStr = commandStub.args[0][0] as string;
should(commandStr.includes('"jupyter==1.0.0" "TestPkg2==4.5.6"')).be.true();
});
it('Get conda packages', async function() {
// Should return nothing if conda is not installed
sinon.stub(fs, 'existsSync').returns(false);
let pkgResult = await installation.getInstalledCondaPackages();
should(pkgResult).not.be.undefined();
should(pkgResult.length).be.equal(0);
// Should return nothing on error
sinon.restore();
sinon.stub(fs, 'existsSync').returns(true);
sinon.stub(installation, 'executeBufferedCommand').rejects(new Error('Expected test failure.'));
pkgResult = await installation.getInstalledCondaPackages();
should(pkgResult).not.be.undefined();
should(pkgResult.length).be.equal(0);
outputChannelStub.verify(c => c.appendLine(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
// Normal use case
sinon.restore();
let testPackages: PythonPkgDetails[] = [{
name: 'TestPkg1',
version: '1.2.3',
channel: 'conda'
}, {
name: 'TestPkg2',
version: '4.5.6',
channel: 'pypi'
}, {
name: 'TestPkg3',
version: '7.8.9',
channel: 'conda'
}];
sinon.stub(fs, 'existsSync').returns(true);
sinon.stub(installation, 'executeBufferedCommand').resolves(JSON.stringify(testPackages));
pkgResult = await installation.getInstalledCondaPackages();
let filteredPackages = testPackages.filter(pkg => pkg.channel !== 'pypi');
should(pkgResult).be.deepEqual(filteredPackages);
});
it('Install conda package', async function() {
let commandStub = sinon.stub(installation, 'executeStreamedCommand').resolves();
// Should not execute any commands when passed an empty package list
await installation.installCondaPackages(undefined, false);
should(commandStub.called).be.false();
await installation.installCondaPackages([], false);
should(commandStub.called).be.false();
// Install package using exact version
let testPackages = [{
name: 'TestPkg1',
version: '1.2.3'
}, {
name: 'TestPkg2',
version: '4.5.6'
}];
await installation.installCondaPackages(testPackages, false);
should(commandStub.calledOnce).be.true();
let commandStr = commandStub.args[0][0] as string;
should(commandStr.includes('"TestPkg1==1.2.3" "TestPkg2==4.5.6"')).be.true();
// Install package using minimum version
await installation.installCondaPackages(testPackages, true);
should(commandStub.calledTwice).be.true();
commandStr = commandStub.args[1][0] as string;
should(commandStr.includes('"TestPkg1>=1.2.3" "TestPkg2>=4.5.6"')).be.true();
});
it('Uninstall conda package', async function() {
let commandStub = sinon.stub(installation, 'executeStreamedCommand').resolves();
let testPackages = [{
name: 'jupyter',
version: '1.0.0'
}, {
name: 'TestPkg2',
version: '4.5.6'
}];
await installation.uninstallCondaPackages(testPackages);
should(commandStub.calledOnce).be.true();
let commandStr = commandStub.args[0][0] as string;
should(commandStr.includes('"jupyter==1.0.0" "TestPkg2==4.5.6"')).be.true();
});
});