mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
platformService tests and move tests from tdd to bdd (#13131)
This commit is contained in:
@@ -53,3 +53,12 @@ export function throwUnless(condition: boolean, message?: string): asserts condi
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function tryExecuteAction<T>(action: () => T | PromiseLike<T>): Promise<{ result: T | undefined, error: any }> {
|
||||||
|
let error: any, result: T | undefined;
|
||||||
|
try {
|
||||||
|
result = await action();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
return { result, error };
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { OsDistribution, OsRelease } from '../interfaces';
|
|||||||
import { getErrorMessage } from '../common/utils';
|
import { getErrorMessage } from '../common/utils';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const extensionOutputChannel = localize('resourceDeployment.outputChannel', "Deployments");
|
export const extensionOutputChannel = localize('resourceDeployment.outputChannel', "Deployments");
|
||||||
const sudoPromptTitle = 'AzureDataStudio';
|
const sudoPromptTitle = 'AzureDataStudio';
|
||||||
/**
|
/**
|
||||||
* Abstract of platform dependencies
|
* Abstract of platform dependencies
|
||||||
@@ -244,7 +244,6 @@ export class PlatformService implements IPlatformService {
|
|||||||
windowsHide: true
|
windowsHide: true
|
||||||
};
|
};
|
||||||
const child = cp.spawn(command, [], spawnOptions);
|
const child = cp.spawn(command, [], spawnOptions);
|
||||||
|
|
||||||
// Add listeners to print stdout and stderr and exit code
|
// Add listeners to print stdout and stderr and exit code
|
||||||
child.on('exit', (code: number | null, signal: string | null) => {
|
child.on('exit', (code: number | null, signal: string | null) => {
|
||||||
if (code !== null) {
|
if (code !== null) {
|
||||||
@@ -258,7 +257,6 @@ export class PlatformService implements IPlatformService {
|
|||||||
this.outputDataChunk(data, outputChannel, localize('platformService.RunCommand.stdout', " stdout: "));
|
this.outputDataChunk(data, outputChannel, localize('platformService.RunCommand.stdout', " stdout: "));
|
||||||
});
|
});
|
||||||
child.stderr!.on('data', (data: string | Buffer) => { this.outputDataChunk(data, outputChannel, localize('platformService.RunCommand.stderr', " stderr: ")); });
|
child.stderr!.on('data', (data: string | Buffer) => { this.outputDataChunk(data, outputChannel, localize('platformService.RunCommand.stderr', " stderr: ")); });
|
||||||
|
|
||||||
await child;
|
await child;
|
||||||
return stdoutData.join('');
|
return stdoutData.join('');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ function validate(test: TestDefinition, semVerProxy: SymVerProxyTest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suite('SemVeryProxy Tests', function (): void {
|
describe('SemVeryProxy Tests', function (): void {
|
||||||
testDefinitions.forEach((semVerTest: TestDefinition) => {
|
testDefinitions.forEach((semVerTest: TestDefinition) => {
|
||||||
test(semVerTest.testName, () => {
|
it(semVerTest.testName, () => {
|
||||||
const semVerProxy = new SymVerProxyTest(semVerTest.inputVersion);
|
const semVerProxy = new SymVerProxyTest(semVerTest.inputVersion);
|
||||||
validate(semVerTest, semVerProxy);
|
validate(semVerTest, semVerProxy);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const testRunner = require('vscodetestcover');
|
|||||||
const suite = 'resource-deployment Extension Tests';
|
const suite = 'resource-deployment Extension Tests';
|
||||||
|
|
||||||
const mochaOptions: any = {
|
const mochaOptions: any = {
|
||||||
ui: 'tdd',
|
ui: 'bdd',
|
||||||
useColors: true,
|
useColors: true,
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
import { apiService } from '../../services/apiService';
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import { apiService } from '../services/apiService';
|
|
||||||
|
|
||||||
suite('API Service Tests', function (): void {
|
describe('API Service Tests', function (): void {
|
||||||
test('getAzurecoreApi returns azure api', () => {
|
it('get azurecoreApi returns azure api', () => {
|
||||||
const api = apiService.azurecoreApi;
|
const api = apiService.azurecoreApi;
|
||||||
assert(api !== undefined);
|
assert(api !== undefined);
|
||||||
});
|
});
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import { IPlatformService, CommandOptions } from '../services/platformService';
|
import { IPlatformService, CommandOptions } from '../../services/platformService';
|
||||||
import { AzdataService } from '../services/azdataService';
|
import { AzdataService } from '../../services/azdataService';
|
||||||
import { BdcDeploymentType } from '../interfaces';
|
import { BdcDeploymentType } from '../../interfaces';
|
||||||
|
|
||||||
suite('azdata service Tests', function (): void {
|
describe('azdata service Tests', function (): void {
|
||||||
test('azdata service handles deployment types properly', async () => {
|
it('azdata service handles deployment types properly', async () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const azdataService = new AzdataService(mockPlatformService.object);
|
const azdataService = new AzdataService(mockPlatformService.object);
|
||||||
mockPlatformService.setup((service) => service.runCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns((command: string, options: CommandOptions | undefined) => {
|
mockPlatformService.setup((service) => service.runCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns((command: string, options: CommandOptions | undefined) => {
|
||||||
@@ -30,7 +30,7 @@ suite('azdata service Tests', function (): void {
|
|||||||
mockPlatformService.verify((service) => service.runCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(5));
|
mockPlatformService.verify((service) => service.runCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(5));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('azdata service returns correct deployment profiles', async () => {
|
it('azdata service returns correct deployment profiles', async () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const azdataService = new AzdataService(mockPlatformService.object);
|
const azdataService = new AzdataService(mockPlatformService.object);
|
||||||
mockPlatformService.setup((service => service.storagePath())).returns(() => {
|
mockPlatformService.setup((service => service.storagePath())).returns(() => {
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { NotebookService } from '../services/notebookService';
|
import { NotebookService } from '../../services/notebookService';
|
||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import { NotebookPathInfo } from '../interfaces';
|
import { NotebookPathInfo } from '../../interfaces';
|
||||||
import { IPlatformService } from '../services/platformService';
|
import { IPlatformService } from '../../services/platformService';
|
||||||
|
|
||||||
suite('Notebook Service Tests', function (): void {
|
describe('Notebook Service Tests', function (): void {
|
||||||
|
|
||||||
test('getNotebook with string parameter', () => {
|
it('getNotebook with string parameter', () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const notebookService = new NotebookService(mockPlatformService.object, '');
|
const notebookService = new NotebookService(mockPlatformService.object, '');
|
||||||
const notebookInput = 'test-notebook.ipynb';
|
const notebookInput = 'test-notebook.ipynb';
|
||||||
@@ -28,7 +28,7 @@ suite('Notebook Service Tests', function (): void {
|
|||||||
mockPlatformService.verify((service) => service.platform(), TypeMoq.Times.never());
|
mockPlatformService.verify((service) => service.platform(), TypeMoq.Times.never());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getNotebook with NotebookInfo parameter', () => {
|
it('getNotebook with NotebookInfo parameter', () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const notebookService = new NotebookService(mockPlatformService.object, '');
|
const notebookService = new NotebookService(mockPlatformService.object, '');
|
||||||
const notebookWin32 = 'test-notebook-win32.ipynb';
|
const notebookWin32 = 'test-notebook-win32.ipynb';
|
||||||
@@ -58,7 +58,7 @@ suite('Notebook Service Tests', function (): void {
|
|||||||
mockPlatformService.verify((service) => service.platform(), TypeMoq.Times.once());
|
mockPlatformService.verify((service) => service.platform(), TypeMoq.Times.once());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('findNextUntitledEditorName with no name conflict', () => {
|
it('findNextUntitledEditorName with no name conflict', () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const notebookService = new NotebookService(mockPlatformService.object, '');
|
const notebookService = new NotebookService(mockPlatformService.object, '');
|
||||||
const notebookFileName = 'mynotebook.ipynb';
|
const notebookFileName = 'mynotebook.ipynb';
|
||||||
@@ -72,7 +72,7 @@ suite('Notebook Service Tests', function (): void {
|
|||||||
assert.equal(actualFileName, expectedTargetFile, 'target file name is not correct');
|
assert.equal(actualFileName, expectedTargetFile, 'target file name is not correct');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('findNextUntitledEditorName with name conflicts', () => {
|
it('findNextUntitledEditorName with name conflicts', () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const notebookService = new NotebookService(mockPlatformService.object, '');
|
const notebookService = new NotebookService(mockPlatformService.object, '');
|
||||||
const notebookFileName = 'mynotebook.ipynb';
|
const notebookFileName = 'mynotebook.ipynb';
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 fs from 'fs';
|
||||||
|
import 'mocha';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as cp from 'promisify-child-process';
|
||||||
|
import * as should from 'should';
|
||||||
|
import * as sinon from 'sinon';
|
||||||
|
import * as sudo from 'sudo-prompt';
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { tryExecuteAction } from '../../common/utils';
|
||||||
|
import { OsDistribution } from '../../interfaces';
|
||||||
|
import { extensionOutputChannel, PlatformService } from '../../services/platformService';
|
||||||
|
import { TestChildProcessPromise } from '../stubs';
|
||||||
|
|
||||||
|
const globalStoragePath = os.tmpdir();
|
||||||
|
const platformService = new PlatformService(globalStoragePath);
|
||||||
|
|
||||||
|
describe('PlatformService', () => {
|
||||||
|
beforeEach('PlatformService setup', async () => {
|
||||||
|
await platformService.initialize();
|
||||||
|
});
|
||||||
|
afterEach('PlatformService cleanup', () => {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
it('storagePath', () => {
|
||||||
|
const result = platformService.storagePath();
|
||||||
|
result.should.equal(globalStoragePath);
|
||||||
|
});
|
||||||
|
it('platform', () => {
|
||||||
|
const result = platformService.platform();
|
||||||
|
result.should.equal(process.platform);
|
||||||
|
});
|
||||||
|
it('outputChannelName', () => {
|
||||||
|
const result = platformService.outputChannelName();
|
||||||
|
result.should.equal(extensionOutputChannel);
|
||||||
|
});
|
||||||
|
describe('output channel', () => {
|
||||||
|
let outputChannelStub: TypeMoq.IMock<vscode.OutputChannel>;
|
||||||
|
beforeEach('output channel setup', () => {
|
||||||
|
outputChannelStub = TypeMoq.Mock.ofType<vscode.OutputChannel>();
|
||||||
|
});
|
||||||
|
it('showOutputChannel', () => {
|
||||||
|
outputChannelStub.setup(c => c.show(TypeMoq.It.isAny())).callback((preserveFocus => {
|
||||||
|
preserveFocus.should.be.true();
|
||||||
|
}));
|
||||||
|
platformService.showOutputChannel(true);
|
||||||
|
});
|
||||||
|
describe('logToOutputChannel', () => {
|
||||||
|
['', undefined, 'header'].forEach(header => {
|
||||||
|
it(`header = ${header}`, () => {
|
||||||
|
const data = 'data';
|
||||||
|
outputChannelStub.setup(c => c.appendLine(TypeMoq.It.isAny())).callback((line => {
|
||||||
|
line.should.equal(header + line);
|
||||||
|
}));
|
||||||
|
platformService.logToOutputChannel(data, header);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('osDistribution', () => {
|
||||||
|
const result = platformService.osDistribution();
|
||||||
|
switch (process.platform) {
|
||||||
|
case 'darwin': result.should.equal(OsDistribution.darwin); break;
|
||||||
|
case 'win32': result.should.equal(OsDistribution.win32); break;
|
||||||
|
case 'linux': result.should.equal(OsDistribution.debian); break;
|
||||||
|
default: result.should.equal(OsDistribution.others); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
describe('file/directory', () => {
|
||||||
|
const filePath = __filename;
|
||||||
|
const contents = __dirname; //a known value
|
||||||
|
[true, false, 'throws'].forEach((fileExists => {
|
||||||
|
it(`fileExists - ${fileExists}`, async () => {
|
||||||
|
switch (fileExists) {
|
||||||
|
case true: (await platformService.fileExists(filePath)).should.be.true(); break;
|
||||||
|
case false: {
|
||||||
|
sinon.stub(fs.promises, 'access').rejects({ code: 'ENOENT' });
|
||||||
|
(await platformService.fileExists(filePath)).should.be.false();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'throws': {
|
||||||
|
sinon.stub(fs.promises, 'access').rejects({});
|
||||||
|
const { error } = await tryExecuteAction(() => platformService.fileExists(filePath));
|
||||||
|
should(error).not.be.undefined();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new Error('unexpected error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
describe('deleteFile', () => {
|
||||||
|
[true, false].forEach(fileExists => {
|
||||||
|
it(`fileExists - ${fileExists}`, async () => {
|
||||||
|
if (fileExists) {
|
||||||
|
const stub = sinon.stub(fs.promises, 'unlink').resolves();
|
||||||
|
await platformService.deleteFile(filePath);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(filePath);
|
||||||
|
} else {
|
||||||
|
sinon.stub(fs.promises, 'access').rejects({ code: 'ENOENT' }); // causes fileExists to return false
|
||||||
|
const stub = sinon.stub(fs.promises, 'unlink').resolves();
|
||||||
|
await platformService.deleteFile(filePath);
|
||||||
|
stub.callCount.should.equal(0); // verifies that unlink was not called
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
[true, false].forEach(async ignoreError => {
|
||||||
|
it(`throws with ignoreError: ${ignoreError}`, async () => {
|
||||||
|
const stub = sinon.stub(fs.promises, 'unlink').throws();
|
||||||
|
const { error } = await tryExecuteAction(() => platformService.deleteFile(filePath, ignoreError));
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(filePath);
|
||||||
|
if (ignoreError) {
|
||||||
|
should(error).be.undefined();
|
||||||
|
} else {
|
||||||
|
should(error).not.be.undefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('openFile', () => {
|
||||||
|
const stub = sinon.stub(vscode.commands, 'executeCommand').resolves(); //resolves with a known string
|
||||||
|
platformService.openFile(filePath);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal('vscode.open');
|
||||||
|
stub.getCall(0).args[1].path.should.equal(filePath);
|
||||||
|
});
|
||||||
|
it('readTextFile', async () => {
|
||||||
|
sinon.stub(fs.promises, 'readFile').resolves(contents);
|
||||||
|
const result = await platformService.readTextFile(filePath);
|
||||||
|
result.should.equal(contents);
|
||||||
|
});
|
||||||
|
it('saveTextFile', async () => {
|
||||||
|
const stub = sinon.stub(fs.promises, 'writeFile').resolves(); //resolves with a known string
|
||||||
|
await platformService.saveTextFile(contents, filePath);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(filePath);
|
||||||
|
stub.getCall(0).args[1].should.equal(contents);
|
||||||
|
});
|
||||||
|
it('copyFile', async () => {
|
||||||
|
const target = __dirname; //arbitrary path
|
||||||
|
const stub = sinon.stub(fs.promises, 'copyFile').resolves();
|
||||||
|
await platformService.copyFile(filePath, target);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(filePath);
|
||||||
|
stub.getCall(0).args[1].should.equal(target);
|
||||||
|
});
|
||||||
|
it('makeDirectory ', async () => {
|
||||||
|
const target = __dirname; //arbitrary path
|
||||||
|
sinon.stub(fs.promises, 'access').rejects({ code: 'ENOENT' }); // this simulates the target directory to not Exist.
|
||||||
|
const stub = sinon.stub(fs.promises, 'mkdir').resolves();
|
||||||
|
await platformService.makeDirectory(target);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('showErrorMessage', () => {
|
||||||
|
const error = __dirname; //arbitrary known string
|
||||||
|
const stub = sinon.stub(vscode.window, 'showErrorMessage').resolves(); //resolves with a known string
|
||||||
|
platformService.showErrorMessage(error);
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
stub.getCall(0).args[0].should.equal(error);
|
||||||
|
});
|
||||||
|
describe('isNotebookNameUsed', () => {
|
||||||
|
[true, false].forEach((isUsed => {
|
||||||
|
it(`return value: ${isUsed}`, () => {
|
||||||
|
const title = __filename; //arbitrary known string
|
||||||
|
if (isUsed) {
|
||||||
|
sinon.stub(azdata.nb, 'notebookDocuments').get(() => [{ isUntitled: true, fileName: title }]);
|
||||||
|
sinon.stub(vscode.workspace, 'textDocuments').get(() => [{ isUntitled: true, fileName: title }]);
|
||||||
|
} else {
|
||||||
|
sinon.stub(azdata.nb, 'notebookDocuments').get(() => [{ isUntitled: true, fileName: '' }]);
|
||||||
|
sinon.stub(vscode.workspace, 'textDocuments').get(() => [{ isUntitled: true, fileName: '' }]);
|
||||||
|
}
|
||||||
|
const result = platformService.isNotebookNameUsed(title);
|
||||||
|
result.should.equal(isUsed);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
describe('runCommand', () => {
|
||||||
|
[
|
||||||
|
{ commandSucceeds: true },
|
||||||
|
{ commandSucceeds: false, ignoreError: true },
|
||||||
|
{ commandSucceeds: false, ignoreError: false },
|
||||||
|
].forEach(({ commandSucceeds, ignoreError }) => {
|
||||||
|
if (ignoreError && commandSucceeds) {
|
||||||
|
return; //exit out of the loop as we do not handle ignoreError when command is successful
|
||||||
|
}
|
||||||
|
it(`non-sudo, commandSucceeds: ${commandSucceeds}, ignoreError: ${ignoreError}`, async () => {
|
||||||
|
const command = __dirname; // arbitrary command string, and success string on successful execution and error string on error
|
||||||
|
const child = new TestChildProcessPromise<cp.Output>();
|
||||||
|
const stub = sinon.stub(cp, 'spawn').returns(child);
|
||||||
|
const runningCommand = platformService.runCommand(command, { commandTitle: 'title', ignoreError: ignoreError });
|
||||||
|
// fake runCommand to behave like echo, returning the command back as stdout/stderr/error.
|
||||||
|
// TestChildProcessPromise object shares the stdout/stderr stream for convenience with the child stream.
|
||||||
|
if (commandSucceeds) {
|
||||||
|
child.emit('data', command);
|
||||||
|
child.emit('exit', 0, null); //resolve with 0 exit code
|
||||||
|
child.resolve({ stdout: command });
|
||||||
|
} else {
|
||||||
|
child.emit('data', command);
|
||||||
|
child.emit('exit', 1, null); // resolve with non-zero exit code
|
||||||
|
child.reject({ stderr: command });
|
||||||
|
}
|
||||||
|
const { result, error } = await tryExecuteAction(() => runningCommand);
|
||||||
|
verifyCommandExecution(stub, result, error, command, commandSucceeds, ignoreError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`sudo, commandSucceeds: ${commandSucceeds}, ignoreError: ${ignoreError}`, async () => {
|
||||||
|
const command = __dirname; // arbitrary command string, and success string on successful execution
|
||||||
|
const stub = sinon.stub(sudo, 'exec').callsFake((cmd, _options, cb) => {
|
||||||
|
// behaves like echo, returning the _cmd back as stdout/stderr/error.
|
||||||
|
if (commandSucceeds) {
|
||||||
|
cb(''/* error */, cmd/* stdout */, ''/* stderr */);
|
||||||
|
} else {
|
||||||
|
cb(cmd/* error */, ''/* stdout */, cmd/* stderr */);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { error, result } = await tryExecuteAction(() => platformService.runCommand(command, { commandTitle: 'title', ignoreError: ignoreError, sudo: true, workingDirectory: __dirname }));
|
||||||
|
verifyCommandExecution(stub, result, error, command, commandSucceeds, ignoreError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function verifyCommandExecution(stub: sinon.SinonStub, result: string | undefined, error: any, command: string, commandSucceeds: boolean | undefined, ignoreError: boolean | undefined) {
|
||||||
|
stub.callCount.should.equal(1);
|
||||||
|
if (commandSucceeds) {
|
||||||
|
result!.should.equal(command);
|
||||||
|
} else {
|
||||||
|
if (ignoreError) {
|
||||||
|
should(error).be.undefined();
|
||||||
|
(result === undefined || result.length === 0).should.be.true('result should be an empty string or be undefined when an error occurs');
|
||||||
|
} else {
|
||||||
|
should(error).not.be.undefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,14 @@ import * as TypeMoq from 'typemoq';
|
|||||||
import assert = require('assert');
|
import assert = require('assert');
|
||||||
import should = require('should');
|
import should = require('should');
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { ResourceTypeService, processWhenClause } from '../services/resourceTypeService';
|
import { ResourceTypeService, processWhenClause } from '../../services/resourceTypeService';
|
||||||
import { IPlatformService } from '../services/platformService';
|
import { IPlatformService } from '../../services/platformService';
|
||||||
import { ToolsService } from '../services/toolsService';
|
import { ToolsService } from '../../services/toolsService';
|
||||||
import { NotebookService } from '../services/notebookService';
|
import { NotebookService } from '../../services/notebookService';
|
||||||
|
|
||||||
suite('Resource Type Service Tests', function (): void {
|
describe('Resource Type Service Tests', function (): void {
|
||||||
|
|
||||||
test('test resource types', () => {
|
it('test resource types', () => {
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
const toolsService = new ToolsService(mockPlatformService.object);
|
const toolsService = new ToolsService(mockPlatformService.object);
|
||||||
const notebookService = new NotebookService(mockPlatformService.object, '');
|
const notebookService = new NotebookService(mockPlatformService.object, '');
|
||||||
@@ -47,7 +47,7 @@ suite('Resource Type Service Tests', function (): void {
|
|||||||
assert(validationErrors.length === 0, `Validation errors detected in the package.json: ${validationErrors.join(EOL)}.`);
|
assert(validationErrors.length === 0, `Validation errors detected in the package.json: ${validationErrors.join(EOL)}.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Selected options containing all when clauses should return true', () => {
|
it('Selected options containing all when clauses should return true', () => {
|
||||||
const whenSelectedTrue: { when: string; selectedOptions: { option: string, value: string }[] }[] = [
|
const whenSelectedTrue: { when: string; selectedOptions: { option: string, value: string }[] }[] = [
|
||||||
{
|
{
|
||||||
when: 'resourceType=sql-bdc && newType=sql-windows-setup', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }]
|
when: 'resourceType=sql-bdc && newType=sql-windows-setup', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }]
|
||||||
@@ -62,16 +62,16 @@ suite('Resource Type Service Tests', function (): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('When clause that reads "true" (ignoring case) should always return true', () => {
|
it('When clause that reads "true" (ignoring case) should always return true', () => {
|
||||||
should(processWhenClause(undefined, [])).be.true('undefined when clause should always return true');
|
should(processWhenClause(undefined, [])).be.true('undefined when clause should always return true');
|
||||||
should(processWhenClause('TrUe', [])).be.true(`"true" when clause should always return true`);
|
should(processWhenClause('TrUe', [])).be.true(`"true" when clause should always return true`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('No selected options returns false', () => {
|
it('No selected options returns false', () => {
|
||||||
should(processWhenClause('newType=empty', [])).be.false('No selected options should return false');
|
should(processWhenClause('newType=empty', [])).be.false('No selected options should return false');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unfulfilled or partially fulfilled when clauses return false', () => {
|
it('Unfulfilled or partially fulfilled when clauses return false', () => {
|
||||||
const whenSelectedFalse: { when: string; selectedOptions: { option: string, value: string }[] }[] = [
|
const whenSelectedFalse: { when: string; selectedOptions: { option: string, value: string }[] }[] = [
|
||||||
{
|
{
|
||||||
when: 'resourceType=sql-bdc && dneType=does-not-exist', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }]
|
when: 'resourceType=sql-bdc && dneType=does-not-exist', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }]
|
||||||
@@ -85,7 +85,7 @@ suite('Resource Type Service Tests', function (): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('An invalid when clause should always return false', () => {
|
it('An invalid when clause should always return false', () => {
|
||||||
should(processWhenClause('badWhenClause', [{ option: 'bad', value: 'WhenClause' }])).be.false(`invalid when clause should return false`);
|
should(processWhenClause('badWhenClause', [{ option: 'bad', value: 'WhenClause' }])).be.false(`invalid when clause should return false`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import { ITool, ToolType } from '../../interfaces';
|
||||||
|
import { IPlatformService } from '../../services/platformService';
|
||||||
|
import { ToolsService } from '../../services/toolsService';
|
||||||
|
|
||||||
|
|
||||||
|
const tools: { name: string; type: ToolType }[] = [
|
||||||
|
{ name: 'azure-cli', type: ToolType.AzCli },
|
||||||
|
{ name: 'docker', type: ToolType.Docker },
|
||||||
|
{ name: 'kubectl', type: ToolType.KubeCtl },
|
||||||
|
{ name: 'azdata', type: ToolType.Azdata }
|
||||||
|
];
|
||||||
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
|
const toolsService = new ToolsService(mockPlatformService.object);
|
||||||
|
|
||||||
|
describe('Tools Service Tests', function (): void {
|
||||||
|
|
||||||
|
it('run getToolByName with all known values', () => {
|
||||||
|
const missingTypes: string[] = [];
|
||||||
|
// Make sure all the enum values are covered
|
||||||
|
for (const type in ToolType) {
|
||||||
|
if (typeof ToolType[type] === 'number') {
|
||||||
|
if (tools.findIndex(element => element.type === parseInt(ToolType[type])) === -1) {
|
||||||
|
missingTypes.push(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(missingTypes.length === 0).should.be.true(`the following enum values are not included in the test:${missingTypes.join(',')}`);
|
||||||
|
|
||||||
|
tools.forEach(toolInfo => {
|
||||||
|
const tool = toolsService.getToolByName(toolInfo.name);
|
||||||
|
(!!tool).should.be.true(`The tool: ${toolInfo.name} is not recognized`);
|
||||||
|
(tool!.type).should.equal(toolInfo.type, 'returned notebook name does not match expected value');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('run getToolByName with a name that is not defined', () => {
|
||||||
|
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
||||||
|
const toolsService = new ToolsService(mockPlatformService.object);
|
||||||
|
const tool = toolsService.getToolByName('no-such-tool');
|
||||||
|
(tool === undefined).should.be.true('for a not defined tool, expected value is undefined');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get/set tools for CurrentProvider', () => {
|
||||||
|
const iTools: ITool[] = tools.map(toolInfo => {
|
||||||
|
const tool = toolsService.getToolByName(toolInfo.name);
|
||||||
|
(!!tool).should.be.true(`The tool: ${toolInfo.name} is not recognized`);
|
||||||
|
tool!.type.should.equal(toolInfo.type, 'returned notebook name does not match expected value');
|
||||||
|
return tool!;
|
||||||
|
});
|
||||||
|
toolsService.toolsForCurrentProvider = iTools;
|
||||||
|
iTools.should.deepEqual(toolsService.toolsForCurrentProvider, 'toolsForCurrentProvider did not return the value we set');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'mocha';
|
|
||||||
import assert = require('assert');
|
|
||||||
import * as TypeMoq from 'typemoq';
|
|
||||||
import { ToolsService } from '../services/toolsService';
|
|
||||||
import { ToolType } from '../interfaces';
|
|
||||||
import { isNumber } from 'util';
|
|
||||||
import { IPlatformService } from '../services/platformService';
|
|
||||||
|
|
||||||
suite('Tools Service Tests', function (): void {
|
|
||||||
|
|
||||||
test('run getToolByName with all known values', () => {
|
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
|
||||||
const toolsService = new ToolsService(mockPlatformService.object);
|
|
||||||
|
|
||||||
const tools: { name: string; type: ToolType }[] = [
|
|
||||||
{ name: 'azure-cli', type: ToolType.AzCli },
|
|
||||||
{ name: 'docker', type: ToolType.Docker },
|
|
||||||
{ name: 'kubectl', type: ToolType.KubeCtl },
|
|
||||||
{ name: 'azdata', type: ToolType.Azdata }];
|
|
||||||
|
|
||||||
const missingTypes: string[] = [];
|
|
||||||
|
|
||||||
// Make sure all the enum values are covered
|
|
||||||
for (const type in ToolType) {
|
|
||||||
if (isNumber(ToolType[type])) {
|
|
||||||
if (tools.findIndex(element => element.type === parseInt(ToolType[type])) === -1) {
|
|
||||||
missingTypes.push(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(missingTypes.length === 0, `the following enum values are not included in the test:${missingTypes.join(',')}`);
|
|
||||||
|
|
||||||
tools.forEach(toolInfo => {
|
|
||||||
const tool = toolsService.getToolByName(toolInfo.name);
|
|
||||||
assert(!!tool, `The tool: ${toolInfo.name} is not recognized`);
|
|
||||||
assert.equal(tool!.type, toolInfo.type, 'returned notebook name does not match expected value');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('run getToolByName with a name that is not defined', () => {
|
|
||||||
const mockPlatformService = TypeMoq.Mock.ofType<IPlatformService>();
|
|
||||||
const toolsService = new ToolsService(mockPlatformService.object);
|
|
||||||
const tool = toolsService.getToolByName('no-such-tool');
|
|
||||||
assert(tool === undefined, 'for a not defined tool, expected value is undefined');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -35,14 +35,14 @@ const testValidations = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
suite('Validation', () => {
|
describe('Validation', () => {
|
||||||
suite('createValidation and validate input Box', () => {
|
describe('createValidation and validate input Box', () => {
|
||||||
setup(() => {
|
beforeEach(() => {
|
||||||
sinon.restore(); //cleanup all previously defined sinon mocks
|
sinon.restore(); //cleanup all previously defined sinon mocks
|
||||||
inputBoxStub = sinon.stub(inputBox, 'updateProperty').resolves();
|
inputBoxStub = sinon.stub(inputBox, 'updateProperty').resolves();
|
||||||
});
|
});
|
||||||
testValidations.forEach(testObj => {
|
testValidations.forEach(testObj => {
|
||||||
test(`validationType: ${testObj.type}`, async () => {
|
it(`validationType: ${testObj.type}`, async () => {
|
||||||
const validation = createValidation(testObj, async () => undefined, async (_varName: string) => undefined);
|
const validation = createValidation(testObj, async () => undefined, async (_varName: string) => undefined);
|
||||||
switch (testObj.type) {
|
switch (testObj.type) {
|
||||||
case ValidationType.IsInteger: should(validation).be.instanceOf(IntegerValidation); break;
|
case ValidationType.IsInteger: should(validation).be.instanceOf(IntegerValidation); break;
|
||||||
@@ -59,7 +59,7 @@ suite('Validation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('IntegerValidation', () => {
|
describe('IntegerValidation', () => {
|
||||||
// all the below test values are arbitrary representative values or sentinel values for integer validation
|
// all the below test values are arbitrary representative values or sentinel values for integer validation
|
||||||
[
|
[
|
||||||
{ value: '342520596781', expected: true },
|
{ value: '342520596781', expected: true },
|
||||||
@@ -72,7 +72,7 @@ suite('Validation', () => {
|
|||||||
{ value: NaN, expected: false },
|
{ value: NaN, expected: false },
|
||||||
].forEach((testObj) => {
|
].forEach((testObj) => {
|
||||||
const displayTestValue = getDisplayString(testObj.value);
|
const displayTestValue = getDisplayString(testObj.value);
|
||||||
test(`testValue:${displayTestValue}`, async () => {
|
it(`testValue:${displayTestValue}`, async () => {
|
||||||
const validationDescription = `value: ${displayTestValue} was not an integer`;
|
const validationDescription = `value: ${displayTestValue} was not an integer`;
|
||||||
const validation = new IntegerValidation(
|
const validation = new IntegerValidation(
|
||||||
{ type: ValidationType.IsInteger, description: validationDescription },
|
{ type: ValidationType.IsInteger, description: validationDescription },
|
||||||
@@ -83,7 +83,7 @@ suite('Validation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('RegexValidation', () => {
|
describe('RegexValidation', () => {
|
||||||
const testRegex = '^[0-9]+$';
|
const testRegex = '^[0-9]+$';
|
||||||
// tests
|
// tests
|
||||||
[
|
[
|
||||||
@@ -97,7 +97,7 @@ suite('Validation', () => {
|
|||||||
{ value: undefined, expected: false },
|
{ value: undefined, expected: false },
|
||||||
].forEach(testOb => {
|
].forEach(testOb => {
|
||||||
const displayTestValue = getDisplayString(testOb.value);
|
const displayTestValue = getDisplayString(testOb.value);
|
||||||
test(`regex: /${testRegex}/, testValue:${displayTestValue}, expect result: ${testOb.expected}`, async () => {
|
it(`regex: /${testRegex}/, testValue:${displayTestValue}, expect result: ${testOb.expected}`, async () => {
|
||||||
const validationDescription = `value:${displayTestValue} did not match the regex:/${testRegex}/`;
|
const validationDescription = `value:${displayTestValue} did not match the regex:/${testRegex}/`;
|
||||||
const validation = new RegexValidation(
|
const validation = new RegexValidation(
|
||||||
{ type: ValidationType.IsInteger, description: validationDescription, regex: testRegex },
|
{ type: ValidationType.IsInteger, description: validationDescription, regex: testRegex },
|
||||||
@@ -108,7 +108,7 @@ suite('Validation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('LessThanOrEqualsValidation', () => {
|
describe('LessThanOrEqualsValidation', () => {
|
||||||
const targetVariableName = 'comparisonTarget';
|
const targetVariableName = 'comparisonTarget';
|
||||||
// tests - when operands are mix of string and number then number comparison is performed
|
// tests - when operands are mix of string and number then number comparison is performed
|
||||||
[
|
[
|
||||||
@@ -147,7 +147,7 @@ suite('Validation', () => {
|
|||||||
].forEach(testObj => {
|
].forEach(testObj => {
|
||||||
const displayTestValue = getDisplayString(testObj.value);
|
const displayTestValue = getDisplayString(testObj.value);
|
||||||
const displayTargetValue = getDisplayString(testObj.targetValue);
|
const displayTargetValue = getDisplayString(testObj.targetValue);
|
||||||
test(`testValue:${displayTestValue}, targetValue:${displayTargetValue}`, async () => {
|
it(`testValue:${displayTestValue}, targetValue:${displayTargetValue}`, async () => {
|
||||||
const validationDescription = `${displayTestValue} did not test as <= ${displayTargetValue}`;
|
const validationDescription = `${displayTestValue} did not test as <= ${displayTargetValue}`;
|
||||||
const validation = new LessThanOrEqualsValidation(
|
const validation = new LessThanOrEqualsValidation(
|
||||||
{ type: ValidationType.IsInteger, description: validationDescription, target: targetVariableName },
|
{ type: ValidationType.IsInteger, description: validationDescription, target: targetVariableName },
|
||||||
@@ -159,7 +159,7 @@ suite('Validation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('GreaterThanOrEqualsValidation', () => {
|
describe('GreaterThanOrEqualsValidation', () => {
|
||||||
const targetVariableName = 'comparisonTarget';
|
const targetVariableName = 'comparisonTarget';
|
||||||
// tests - when operands are mix of string and number then number comparison is performed
|
// tests - when operands are mix of string and number then number comparison is performed
|
||||||
[
|
[
|
||||||
@@ -190,7 +190,7 @@ suite('Validation', () => {
|
|||||||
].forEach(testObj => {
|
].forEach(testObj => {
|
||||||
const displayTestValue = getDisplayString(testObj.value);
|
const displayTestValue = getDisplayString(testObj.value);
|
||||||
const displayTargetValue = getDisplayString(testObj.targetValue);
|
const displayTargetValue = getDisplayString(testObj.targetValue);
|
||||||
test(`testValue:${displayTestValue}, targetValue:${displayTargetValue}`, async () => {
|
it(`testValue:${displayTestValue}, targetValue:${displayTargetValue}`, async () => {
|
||||||
const validationDescription = `${displayTestValue} did not test as >= ${displayTargetValue}`;
|
const validationDescription = `${displayTestValue} did not test as >= ${displayTargetValue}`;
|
||||||
const validation = new GreaterThanOrEqualsValidation(
|
const validation = new GreaterThanOrEqualsValidation(
|
||||||
{ type: ValidationType.IsInteger, description: validationDescription, target: targetVariableName },
|
{ type: ValidationType.IsInteger, description: validationDescription, target: targetVariableName },
|
||||||
|
|||||||
Reference in New Issue
Block a user