ML - dashboard icons and links (#10153)

* ML - dashboard icons and links
This commit is contained in:
Leila Lali
2020-04-28 21:21:30 -07:00
committed by GitHub
parent 046995f2a5
commit 04af41c424
145 changed files with 387 additions and 134 deletions

View File

@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* 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 { ProcessService } from '../../common/processService';
import * as utils from '../../common/utils';
import should = require('should');
interface TestContext {
outputChannel: vscode.OutputChannel;
}
function createContext(): TestContext {
return {
outputChannel: {
name: '',
append: () => { },
appendLine: () => { },
clear: () => { },
show: () => { },
hide: () => { },
dispose: () => { }
}
};
}
function execFolderListCommand(context: TestContext, service : ProcessService): Promise<string> {
if (utils.isWindows()) {
return service.execScripts('cmd', ['dir', '.'], [], context.outputChannel);
} else {
return service.execScripts('/bin/sh', ['-c', 'ls'], [], context.outputChannel);
}
}
function execFolderListBufferedCommand(context: TestContext, service : ProcessService): Promise<string> {
if (utils.isWindows()) {
return service.executeBufferedCommand('dir', context.outputChannel);
} else {
return service.executeBufferedCommand('ls', context.outputChannel);
}
}
describe('Process Service', () => {
it('Executing a valid script should return successfully', async function (): Promise<void> {
const context = createContext();
let service = new ProcessService();
await should(execFolderListCommand(context, service)).resolved();
});
it('execFolderListCommand should reject if command time out @UNSTABLE@', async function (): Promise<void> {
const context = createContext();
let service = new ProcessService();
service.timeout = 10;
await should(execFolderListCommand(context, service)).rejected();
});
it('executeBufferedCommand should resolve give valid script', async function (): Promise<void> {
const context = createContext();
let service = new ProcessService();
service.timeout = 2000;
await should(execFolderListBufferedCommand(context, service)).resolved();
});
});

View File

@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
const testRunner = require('vscodetestcover');
const suite = 'machine learning Extension Tests';
const mochaOptions: any = {
ui: 'bdd',
useColors: true,
timeout: 10000
};
// set relevant mocha options from the environment
if (process.env.ADS_TEST_GREP) {
mochaOptions.grep = process.env.ADS_TEST_GREP;
console.log(`setting options.grep to: ${mochaOptions.grep}`);
}
if (process.env.ADS_TEST_INVERT_GREP) {
mochaOptions.invert = parseInt(process.env.ADS_TEST_INVERT_GREP);
console.log(`setting options.invert to: ${mochaOptions.invert}`);
}
if (process.env.ADS_TEST_TIMEOUT) {
mochaOptions.timeout = parseInt(process.env.ADS_TEST_TIMEOUT);
console.log(`setting options.timeout to: ${mochaOptions.timeout}`);
}
if (process.env.ADS_TEST_RETRIES) {
mochaOptions.retries = parseInt(process.env.ADS_TEST_RETRIES);
console.log(`setting options.retries to: ${mochaOptions.retries}`);
}
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
mochaOptions.reporter = 'mocha-multi-reporters';
mochaOptions.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
testRunner.configure(mochaOptions, { coverConfig: '../../coverConfig.json' });
export = testRunner;

View File

@@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* 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 'mocha';
import * as TypeMoq from 'typemoq';
import * as path from 'path';
import { ApiWrapper } from '../common/apiWrapper';
import { QueryRunner } from '../common/queryRunner';
import { ProcessService } from '../common/processService';
import MainController from '../controllers/mainController';
import { PackageManager } from '../packageManagement/packageManager';
import * as nbExtensionApis from '../typings/notebookServices';
interface TestContext {
notebookExtension: vscode.Extension<any>;
jupyterInstallation: nbExtensionApis.IJupyterServerInstallation;
jupyterController: nbExtensionApis.IJupyterController;
nbExtensionApis: nbExtensionApis.IExtensionApi;
apiWrapper: TypeMoq.IMock<ApiWrapper>;
queryRunner: TypeMoq.IMock<QueryRunner>;
processService: TypeMoq.IMock<ProcessService>;
context: vscode.ExtensionContext;
outputChannel: vscode.OutputChannel;
extension: vscode.Extension<any>;
packageManager: TypeMoq.IMock<PackageManager>;
workspaceConfig: vscode.WorkspaceConfiguration;
}
function createContext(): TestContext {
let packages = new Map<string, nbExtensionApis.IPackageManageProvider>();
let jupyterInstallation: nbExtensionApis.IJupyterServerInstallation = {
installCondaPackages: () => { return Promise.resolve(); },
getInstalledPipPackages: () => { return Promise.resolve([]); },
installPipPackages: () => { return Promise.resolve(); },
uninstallPipPackages: () => { return Promise.resolve(); },
uninstallCondaPackages: () => { return Promise.resolve(); },
executeBufferedCommand: () => { return Promise.resolve(''); },
executeStreamedCommand: () => { return Promise.resolve(); },
pythonExecutable: '',
pythonInstallationPath: '',
installPythonPackage: () => { return Promise.resolve(); }
};
let jupyterController = {
jupyterInstallation: jupyterInstallation
};
let extensionPath = path.join(__dirname, '..', '..');
let extensionApi: nbExtensionApis.IExtensionApi = {
getJupyterController: () => { return jupyterController; },
registerPackageManager: (providerId: string, packageManagerProvider: nbExtensionApis.IPackageManageProvider) => {
packages.set(providerId, packageManagerProvider);
},
getPackageManagers: () => { return packages; },
};
return {
jupyterInstallation: jupyterInstallation,
jupyterController: jupyterController,
nbExtensionApis: extensionApi,
notebookExtension: {
id: '',
extensionPath: '',
isActive: true,
packageJSON: '',
extensionKind: vscode.ExtensionKind.UI,
exports: extensionApi,
activate: () => {return Promise.resolve();},
extensionUri: vscode.Uri.parse('')
},
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
queryRunner: TypeMoq.Mock.ofType(QueryRunner),
processService: TypeMoq.Mock.ofType(ProcessService),
packageManager: TypeMoq.Mock.ofType(PackageManager),
context: {
subscriptions: [],
workspaceState: {
get: () => {return undefined;},
update: () => {return Promise.resolve();}
},
globalState: {
get: () => {return Promise.resolve();},
update: () => {return Promise.resolve();}
},
extensionPath: extensionPath,
asAbsolutePath: () => {return '';},
storagePath: '',
globalStoragePath: '',
logPath: '',
extensionUri: vscode.Uri.parse('')
},
outputChannel: {
name: '',
append: () => { },
appendLine: () => { },
clear: () => { },
show: () => { },
hide: () => { },
dispose: () => { }
},
extension: {
id: '',
extensionPath: '',
isActive: true,
packageJSON: {},
extensionKind: vscode.ExtensionKind.UI,
exports: {},
activate: () => { return Promise.resolve(); },
extensionUri: vscode.Uri.parse('')
},
workspaceConfig: {
get: () => {return 'value';},
has: () => {return true;},
inspect: () => {return undefined;},
update: () => {return Promise.reject();},
}
};
}
function createController(testContext: TestContext): MainController {
let controller = new MainController(testContext.context, testContext.apiWrapper.object, testContext.queryRunner.object, testContext.processService.object, testContext.packageManager.object);
return controller;
}
describe('Main Controller', () => {
it('Should create new instance successfully', async function (): Promise<void> {
let testContext = createContext();
testContext.apiWrapper.setup(x => x.createOutputChannel(TypeMoq.It.isAny())).returns(() => testContext.outputChannel);
should.doesNotThrow(() => createController(testContext));
});
it('initialize Should install dependencies successfully', async function (): Promise<void> {
let testContext = createContext();
testContext.apiWrapper.setup(x => x.getExtension(TypeMoq.It.isAny())).returns(() => testContext.notebookExtension);
testContext.apiWrapper.setup(x => x.getConfiguration(TypeMoq.It.isAny())).returns(() => testContext.workspaceConfig);
testContext.apiWrapper.setup(x => x.createOutputChannel(TypeMoq.It.isAny())).returns(() => testContext.outputChannel);
testContext.apiWrapper.setup(x => x.getExtension(TypeMoq.It.isAny())).returns(() => testContext.extension);
testContext.packageManager.setup(x => x.managePackages()).returns(() => Promise.resolve());
testContext.packageManager.setup(x => x.installDependencies()).returns(() => Promise.resolve());
testContext.apiWrapper.setup(x => x.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()));
let controller = createController(testContext);
await controller.activate();
should.notEqual(controller.config.requiredSqlPythonPackages.find(x => x.name ==='sqlmlutils'), undefined);
});
});

View File

@@ -0,0 +1,232 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { ApiWrapper } from '../../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { AzureModelRegistryService } from '../../modelManagement/azureModelRegistryService';
import { Config } from '../../configurations/config';
import { HttpClient } from '../../common/httpClient';
import { azureResource } from '../../typings/azure-resource';
import * as utils from '../utils';
import { Workspace, WorkspacesListByResourceGroupResponse } from '@azure/arm-machinelearningservices/esm/models';
import { WorkspaceModel, AssetsQueryByIdResponse, Asset, GetArtifactContentInformation2Response } from '../../modelManagement/interfaces';
import { AzureMachineLearningWorkspaces, Workspaces } from '@azure/arm-machinelearningservices';
import { WorkspaceModels } from '../../modelManagement/workspacesModels';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
config: TypeMoq.IMock<Config>;
httpClient: TypeMoq.IMock<HttpClient>;
outputChannel: vscode.OutputChannel;
op: azdata.BackgroundOperation;
accounts: azdata.Account[];
subscriptions: azureResource.AzureResourceSubscription[];
groups: azureResource.AzureResourceResourceGroup[];
workspaces: Workspace[];
models: WorkspaceModel[];
client: TypeMoq.IMock<AzureMachineLearningWorkspaces>;
workspacesClient: TypeMoq.IMock<Workspaces>;
modelClient: TypeMoq.IMock<WorkspaceModels>;
}
function createContext(): TestContext {
const context = utils.createContext();
const workspaces = TypeMoq.Mock.ofType(Workspaces);
const credentials = {
signRequest: () => {
return Promise.resolve(undefined!!);
}
};
const client = TypeMoq.Mock.ofInstance(new AzureMachineLearningWorkspaces(credentials, 'subscription'));
client.setup(x => x.apiVersion).returns(() => '20180101');
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
config: TypeMoq.Mock.ofType(Config),
httpClient: TypeMoq.Mock.ofType(HttpClient),
outputChannel: context.outputChannel,
op: context.op,
accounts: [
{
key: {
providerId: '',
accountId: 'a1'
},
displayInfo: {
contextualDisplayName: '',
accountType: '',
displayName: 'a1',
userId: 'a1'
},
properties:
{
tenants: [
{
id: '1',
}
]
}
,
isStale: true
}
],
subscriptions: [
{
name: 's1',
id: 's1'
}
],
groups: [
{
name: 'g1',
id: 'g1'
}
],
workspaces: [{
name: 'w1',
id: 'w1'
}
],
models: [
{
name: 'm1',
id: 'm1',
url: 'aml://asset/test.test'
}
],
client: client,
workspacesClient: workspaces,
modelClient: TypeMoq.Mock.ofInstance(new WorkspaceModels(client.object))
};
}
describe('AzureModelRegistryService', () => {
it('getAccounts should return the list of accounts successfully', async function (): Promise<void> {
let testContext = createContext();
const accounts = testContext.accounts;
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
testContext.apiWrapper.setup(x => x.getAllAccounts()).returns(() => Promise.resolve(accounts));
let actual = await service.getAccounts();
should.deepEqual(actual, testContext.accounts);
});
it('getSubscriptions should return the list of subscriptions successfully', async function (): Promise<void> {
let testContext = createContext();
const expected = testContext.subscriptions;
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ subscriptions: expected, errors: [] }));
let actual = await service.getSubscriptions(testContext.accounts[0]);
should.deepEqual(actual, expected);
});
it('getGroups should return the list of groups successfully', async function (): Promise<void> {
let testContext = createContext();
const expected = testContext.groups;
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ resourceGroups: expected, errors: [] }));
let actual = await service.getGroups(testContext.accounts[0], testContext.subscriptions[0]);
should.deepEqual(actual, expected);
});
it('getWorkspaces should return the list of workspaces successfully', async function (): Promise<void> {
let testContext = createContext();
const response: WorkspacesListByResourceGroupResponse = Object.assign(new Array<Workspace>(...testContext.workspaces), {
_response: undefined!
});
const expected = testContext.workspaces;
testContext.workspacesClient.setup(x => x.listByResourceGroup(TypeMoq.It.isAny())).returns(() => Promise.resolve(response));
testContext.workspacesClient.setup(x => x.listBySubscription()).returns(() => Promise.resolve(response));
testContext.client.setup(x => x.workspaces).returns(() => testContext.workspacesClient.object);
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
service.AzureMachineLearningClient = testContext.client.object;
let actual = await service.getWorkspaces(testContext.accounts[0], testContext.subscriptions[0], testContext.groups[0]);
should.deepEqual(actual, expected);
});
it('getModels should return the list of models successfully', async function (): Promise<void> {
let testContext = createContext();
testContext.config.setup(x => x.amlApiVersion).returns(() => '2018');
testContext.config.setup(x => x.amlModelManagementUrl).returns(() => 'test.url');
const expected = testContext.models;
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
service.AzureMachineLearningClient = testContext.client.object;
service.ModelClient = testContext.modelClient.object;
testContext.modelClient.setup(x => x.listModels(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(testContext.models));
let actual = await service.getModels(testContext.accounts[0], testContext.subscriptions[0], testContext.groups[0], testContext.workspaces[0]);
should.deepEqual(actual, expected);
});
it('downloadModel should download model artifact successfully', async function (): Promise<void> {
let testContext = createContext();
const asset: Asset =
{
id: '1',
name: 'asset',
artifacts: [
{
id: '/1/2/3/4/5/'
}
]
};
const assetResponse: AssetsQueryByIdResponse = Object.assign(asset, {
_response: undefined!
});
const artifactResponse: GetArtifactContentInformation2Response = Object.assign({
contentUri: 'downloadUrl'
}, {
_response: undefined!
});
testContext.config.setup(x => x.amlApiVersion).returns(() => '2018');
testContext.config.setup(x => x.amlModelManagementUrl).returns(() => 'test.url');
testContext.config.setup(x => x.amlExperienceUrl).returns(() => 'test.url');
testContext.client.setup(x => x.sendOperationRequest(TypeMoq.It.isAny(),
TypeMoq.It.is(p => p.path !== undefined && p.path.startsWith('modelmanagement')), TypeMoq.It.isAny())).returns(() => Promise.resolve(assetResponse));
testContext.client.setup(x => x.sendOperationRequest(TypeMoq.It.isAny(),
TypeMoq.It.is(p => p.path !== undefined && p.path.startsWith('artifact')), TypeMoq.It.isAny())).returns(() => Promise.resolve(artifactResponse));
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.httpClient.setup(x => x.download(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
let service = new AzureModelRegistryService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.httpClient.object,
testContext.outputChannel);
service.AzureMachineLearningClient = testContext.client.object;
service.ModelClient = testContext.modelClient.object;
testContext.modelClient.setup(x => x.listModels(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(testContext.models));
let actual = await service.downloadModel(testContext.accounts[0], testContext.subscriptions[0], testContext.groups[0], testContext.workspaces[0], testContext.models[0]);
should.notEqual(actual, undefined);
testContext.httpClient.verify(x => x.download(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
});

View File

@@ -0,0 +1,453 @@
/*---------------------------------------------------------------------------------------------
* 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 utils from '../../common/utils';
import { ApiWrapper } from '../../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { Config } from '../../configurations/config';
import { DeployedModelService } from '../../modelManagement/deployedModelService';
import { QueryRunner } from '../../common/queryRunner';
import { ImportedModel } from '../../modelManagement/interfaces';
import { ModelPythonClient } from '../../modelManagement/modelPythonClient';
import * as path from 'path';
import * as os from 'os';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import * as fs from 'fs';
import { ModelConfigRecent } from '../../modelManagement/modelConfigRecent';
import { DatabaseTable } from '../../prediction/interfaces';
import * as queries from '../../modelManagement/queries';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
config: TypeMoq.IMock<Config>;
queryRunner: TypeMoq.IMock<QueryRunner>;
modelClient: TypeMoq.IMock<ModelPythonClient>;
recentModels: TypeMoq.IMock<ModelConfigRecent>;
importTable: DatabaseTable;
}
function createContext(): TestContext {
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
config: TypeMoq.Mock.ofType(Config),
queryRunner: TypeMoq.Mock.ofType(QueryRunner),
modelClient: TypeMoq.Mock.ofType(ModelPythonClient),
recentModels: TypeMoq.Mock.ofType(ModelConfigRecent),
importTable: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
};
}
describe('DeployedModelService', () => {
it('getDeployedModels should fail with no connection', async function (): Promise<void> {
const testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
let importTable: DatabaseTable = {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
};
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let service = new DeployedModelService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.queryRunner.object,
testContext.modelClient.object,
testContext.recentModels.object);
await should(service.getDeployedModels(importTable)).rejected();
});
it('getDeployedModels should returns models successfully', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
const expected: ImportedModel[] = [
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
deploymentTime: '2018-01-01',
version: '1.1',
framework: 'onnx',
frameworkVersion: '1',
deployedBy: '1',
runId: 'run1',
table: testContext.importTable
}
];
const result = {
rowCount: 1,
columnInfo: [],
rows: [
[
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'name1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'desc1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1.1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '2018-01-01',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'onnx',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '2018-01-01',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'run1',
isNull: false,
invariantCultureDisplayValue: ''
}
]
]
};
let service = new DeployedModelService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.queryRunner.object,
testContext.modelClient.object,
testContext.recentModels.object);
testContext.queryRunner.setup(x => x.safeRunQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(result));
const actual = await service.getDeployedModels(testContext.importTable);
should.deepEqual(actual, expected);
});
it('loadModelParameters should load parameters using python client successfully', async function (): Promise<void> {
const testContext = createContext();
const expected = {
inputs: [
{
'name': 'p1',
'type': 'int'
},
{
'name': 'p2',
'type': 'varchar'
}
],
outputs: [
{
'name': 'o1',
'type': 'int'
},
]
};
testContext.modelClient.setup(x => x.loadModelParameters(TypeMoq.It.isAny())).returns(() => Promise.resolve(expected));
let service = new DeployedModelService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.queryRunner.object,
testContext.modelClient.object,
testContext.recentModels.object);
const actual = await service.loadModelParameters('');
should.deepEqual(actual, expected);
});
it('downloadModel should download model successfully', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
const tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${UUID.generateUuid()}`);
await fs.promises.writeFile(tempFilePath, 'test');
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
deploymentTime: '2018-01-01',
version: '1.1',
framework: 'onnx',
frameworkVersion: '1',
deployedBy: '1',
runId: 'run1',
table: testContext.importTable
};
const result = {
rowCount: 1,
columnInfo: [],
rows: [
[
{
displayValue: await utils.readFileInHex(tempFilePath),
isNull: false,
invariantCultureDisplayValue: ''
}
]
]
};
let service = new DeployedModelService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.queryRunner.object,
testContext.modelClient.object,
testContext.recentModels.object);
testContext.queryRunner.setup(x => x.safeRunQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(result));
testContext.config.setup(x => x.registeredModelDatabaseName).returns(() => 'db');
testContext.config.setup(x => x.registeredModelTableName).returns(() => 'table');
testContext.config.setup(x => x.registeredModelTableSchemaName).returns(() => 'dbo');
const actual = await service.downloadModel(model);
should.notEqual(actual, undefined);
});
it('deployLocalModel should returns models successfully', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
deploymentTime: '2018-01-01',
version: '1.1',
framework: 'onnx',
frameworkVersion: '1',
deployedBy: '1',
runId: 'run1',
table: testContext.importTable
};
const row = [
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'name1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'desc1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1.1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '2018-01-01',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'onnx',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '2018-01-01',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'run1',
isNull: false,
invariantCultureDisplayValue: ''
}
];
const result = {
rowCount: 1,
columnInfo: [],
rows: [row]
};
let updatedResult = {
rowCount: 1,
columnInfo: [],
rows: [row, row]
};
let deployed = false;
let service = new DeployedModelService(
testContext.apiWrapper.object,
testContext.config.object,
testContext.queryRunner.object,
testContext.modelClient.object,
testContext.recentModels.object);
testContext.queryRunner.setup(x => x.runWithDatabaseChange(TypeMoq.It.isAny(), TypeMoq.It.is(x => x.indexOf('INSERT INTO') > 0), TypeMoq.It.isAny())).returns(() => {
deployed = true;
return Promise.resolve(result);
});
testContext.queryRunner.setup(x => x.safeRunQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
return deployed ? Promise.resolve(updatedResult) : Promise.resolve(result);
});
testContext.queryRunner.setup(x => x.runWithDatabaseChange(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(result));
testContext.config.setup(x => x.registeredModelDatabaseName).returns(() => 'db');
testContext.config.setup(x => x.registeredModelTableName).returns(() => 'table');
testContext.config.setup(x => x.registeredModelTableSchemaName).returns(() => 'dbo');
let tempFilePath: string = '';
try {
tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${UUID.generateUuid()}`);
await fs.promises.writeFile(tempFilePath, 'test');
await should(service.deployLocalModel(tempFilePath, model, testContext.importTable)).resolved();
}
finally {
await utils.deleteFile(tempFilePath);
}
});
it('getConfigureQuery should escape db name', async function (): Promise<void> {
const testContext = createContext();
testContext.importTable.databaseName = 'd[]b';
testContext.importTable.tableName = 'ta[b]le';
testContext.importTable.schema = 'dbo';
const expected = `
IF NOT EXISTS
( SELECT t.name, s.name
FROM sys.tables t join sys.schemas s on t.schema_id=t.schema_id
WHERE t.name = 'ta[b]le'
AND s.name = 'dbo'
)
BEGIN
CREATE TABLE [dbo].[ta[[b]]le](
[model_id] [int] IDENTITY(1,1) NOT NULL,
[model_name] [varchar](256) NOT NULL,
[model_framework] [varchar](256) NULL,
[model_framework_version] [varchar](256) NULL,
[model] [varbinary](max) NOT NULL,
[model_version] [varchar](256) NULL,
[model_creation_time] [datetime2] NULL,
[model_deployment_time] [datetime2] NULL,
[deployed_by] [int] NULL,
[model_description] [varchar](256) NULL,
[run_id] [varchar](256) NULL,
CONSTRAINT [ta[[b]]le_models_pk] PRIMARY KEY CLUSTERED
(
[model_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER TABLE [dbo].[ta[[b]]le] ADD CONSTRAINT [ta[[b]]le_deployment_time] DEFAULT (getdate()) FOR [model_deployment_time]
END
`;
const actual = queries.getConfigureTableQuery(testContext.importTable);
should.equal(actual.indexOf(expected) >= 0, true, `actual: ${actual} \n expected: ${expected}`);
});
it('getDeployedModelsQuery should escape db name', async function (): Promise<void> {
const testContext = createContext();
testContext.importTable.databaseName = 'd[]b';
testContext.importTable.tableName = 'ta[b]le';
testContext.importTable.schema = 'dbo';
const expected = `
SELECT model_id, model_name, model_description, model_version, model_creation_time, model_framework, model_framework_version, model_deployment_time, deployed_by, run_id
FROM [d[[]]b].[dbo].[ta[[b]]le]
WHERE model_name not like 'MLmodel' and model_name not like 'conda.yaml'
ORDER BY model_id
`;
const actual = queries.getDeployedModelsQuery(testContext.importTable);
should.deepEqual(expected, actual);
});
it('getInsertModelQuery should escape db name', async function (): Promise<void> {
const testContext = createContext();
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
version: '1.1',
table: testContext.importTable
};
const expected = `INSERT INTO [dbo].[tb]
(model_name, model, model_version, model_description, model_creation_time, model_framework, model_framework_version, run_id)
VALUES (
'name1',
,
'1.1',
'desc1',
'2018-01-01',
'',
'',
'')`;
const actual = queries.getInsertModelQuery(model, testContext.importTable);
should.equal(actual.indexOf(expected) >= 0, true, `actual: ${actual} \n expected: ${expected}`);
});
it('getModelContentQuery should escape db name', async function (): Promise<void> {
const testContext = createContext();
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
version: '1.1',
table: testContext.importTable
};
model.table = {
databaseName: 'd[]b', tableName: 'ta[b]le', schema: 'dbo'
};
const expected = `
SELECT model
FROM [d[[]]b].[dbo].[ta[[b]]le]
WHERE model_id = 1;
`;
const actual = queries.getModelContentQuery(model);
should.deepEqual(actual, expected, `actual: ${actual} \n expected: ${expected}`);
});
});

View File

@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { ApiWrapper } from '../../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { Config } from '../../configurations/config';
import * as utils from '../utils';
import { ProcessService } from '../../common/processService';
import { PackageManager } from '../../packageManagement/packageManager';
import { ModelPythonClient } from '../../modelManagement/modelPythonClient';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
config: TypeMoq.IMock<Config>;
outputChannel: vscode.OutputChannel;
op: azdata.BackgroundOperation;
processService: TypeMoq.IMock<ProcessService>;
packageManager: TypeMoq.IMock<PackageManager>;
}
function createContext(): TestContext {
const context = utils.createContext();
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
config: TypeMoq.Mock.ofType(Config),
outputChannel: context.outputChannel,
op: context.op,
processService: TypeMoq.Mock.ofType(ProcessService),
packageManager: TypeMoq.Mock.ofType(PackageManager)
};
}
describe('ModelPythonClient', () => {
it('deployModel should deploy the model successfully', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
const modelPath = 'C:\\test';
let service = new ModelPythonClient(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.processService.object,
testContext.config.object,
testContext.packageManager.object);
testContext.packageManager.setup(x => x.installRequiredPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.config.setup(x => x.pythonExecutable).returns(() => 'pythonPath');
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(),
TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
await service.deployModel(connection, modelPath);
});
it('loadModelParameters should load model parameters successfully', async function (): Promise<void> {
const testContext = createContext();
const modelPath = 'C:\\test';
const expected = {
inputs: [
{
'name': 'p1',
'type': 'int'
},
{
'name': 'p2',
'type': 'varchar'
}
],
outputs: [
{
'name': 'o1',
'type': 'int'
},
]
};
const parametersJson = `
{
"inputs": [
{
"name": "p1",
"type": "int"
},
{
"name": "p2",
"type": "varchar"
}
],
"outputs": [
{
"name": "o1",
"type": "int"
}
]
}
`;
let service = new ModelPythonClient(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.processService.object,
testContext.config.object,
testContext.packageManager.object);
testContext.packageManager.setup(x => x.installRequiredPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.config.setup(x => x.pythonExecutable).returns(() => 'pythonPath');
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(),
TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(parametersJson));
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
const actual = await service.loadModelParameters(modelPath);
should.deepEqual(actual, expected);
});
});

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* 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 { QueryRunner } from '../../common/queryRunner';
import { ApiWrapper } from '../../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { PackageManagementService } from '../../packageManagement/packageManagementService';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
queryRunner: TypeMoq.IMock<QueryRunner>;
}
function createContext(): TestContext {
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
queryRunner: TypeMoq.Mock.ofType(QueryRunner)
};
}
describe('Package Management Service', () => {
it('openDocuments should open document in browser successfully', async function (): Promise<void> {
const context = createContext();
context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
should.equal(await serverConfigManager.openDocuments(), true);
});
it('isMachineLearningServiceEnabled should return true if external script is enabled', async function (): Promise<void> {
const context = createContext();
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
let connection = new azdata.connection.ConnectionProfile();
should.equal(await serverConfigManager.isMachineLearningServiceEnabled(connection), true);
});
it('isRInstalled should return true if R is installed', async function (): Promise<void> {
const context = createContext();
context.queryRunner.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
let connection = new azdata.connection.ConnectionProfile();
should.equal(await serverConfigManager.isRInstalled(connection), true);
});
it('isPythonInstalled should return true if Python is installed', async function (): Promise<void> {
const context = createContext();
context.queryRunner.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
let connection = new azdata.connection.ConnectionProfile();
should.equal(await serverConfigManager.isPythonInstalled(connection), true);
});
it('enableExternalScriptConfig should show error message if did not updated successfully', async function (): Promise<void> {
const context = createContext();
context.queryRunner.setup(x => x.updateExternalScriptConfig(TypeMoq.It.isAny(), true)).returns(() => Promise.resolve());
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
context.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
context.apiWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
context.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
let connection = new azdata.connection.ConnectionProfile();
await serverConfigManager.enableExternalScriptConfig(connection);
context.apiWrapper.verify(x => x.showErrorMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
});

View File

@@ -0,0 +1,273 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
import { PackageManager } from '../../packageManagement/packageManager';
import { createContext, TestContext } from './utils';
describe('Package Manager', () => {
it('Should initialize SQL package manager successfully', async function (): Promise<void> {
let testContext = createContext();
should.doesNotThrow(() => createPackageManager(testContext));
});
it('Manage Package command Should execute the command for valid connection', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('Manage Package command Should execute the command if r installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('Manage Package command Should show an error for connection without python installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny()));
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.showInfoMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('Manage Package command Should show an error for no connection', async function (): Promise<void> {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny()));
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.showInfoMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('installDependencies Should download sqlmlutils if does not exist', async function (): Promise<void> {
let testContext = createContext();
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"},
{"name":"sqlmlutils","version":"1.1.1"}
]`;
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve(installedPackages);});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
testContext.httpClient.verify(x => x.download(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('installDependencies Should not install packages if already installed', async function (): Promise<void> {
let testContext = createContext();
let packagesInstalled = false;
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"},
{"name":"sqlmlutils","version":"1.1.1"}
]`;
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip install') > 0) {
packagesInstalled = true;
}
return Promise.resolve(installedPackages);
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
should.equal(packagesInstalled, false);
});
it('installDependencies Should install packages that are not already installed', async function (): Promise<void> {
let testContext = createContext();
let packagesInstalled = false;
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"}
]`;
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip install') > 0) {
packagesInstalled = true;
}
return Promise.resolve(installedPackages);
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
should.equal(packagesInstalled, true);
});
it('installDependencies Should not install packages if runtime is disabled in setting', async function (): Promise<void> {
let testContext = createContext();
testContext.config.setup(x => x.rEnabled).returns(() => false);
testContext.config.setup(x => x.pythonEnabled).returns(() => false);
let packagesInstalled = false;
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"}
]`;
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip install') > 0 || command.indexOf('install.packages') > 0) {
packagesInstalled = true;
}
return Promise.resolve(installedPackages);
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
should.equal(packagesInstalled, false);
});
it('installDependencies Should install packages that have older version installed', async function (): Promise<void> {
let testContext = createContext();
let packagesInstalled = false;
let installedPackages = `[
{"name":"sqlmlutils","version":"0.1.1"}
]`;
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip install') > 0) {
packagesInstalled = true;
}
return Promise.resolve(installedPackages);
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
should.equal(packagesInstalled, true);
});
it('installDependencies Should install packages if list packages fails', async function (): Promise<void> {
let testContext = createContext();
let packagesInstalled = false;
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command,) => {
if (command.indexOf('pip list') > 0) {
return Promise.reject();
} else if (command.indexOf('pip install') > 0) {
packagesInstalled = true;
return Promise.resolve('');
} else {
return Promise.resolve('');
}
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
should.equal(packagesInstalled, true);
});
it('installDependencies Should fail if download packages fails', async function (): Promise<void> {
let testContext = createContext();
let packagesInstalled = false;
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"}
]`;
testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
operationInfo.operation(testContext.op);
});
testContext.httpClient.setup(x => x.download(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.reject());
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip list') > 0) {
return Promise.resolve(installedPackages);
} else if (command.indexOf('pip install') > 0) {
return Promise.reject();
} else {
return Promise.resolve('');
}
});
let packageManager = createPackageManager(testContext);
await should(packageManager.installDependencies()).rejected();
should.equal(testContext.getOpStatus(), azdata.TaskStatus.Failed);
should.equal(packagesInstalled, false);
});
function createPackageManager(testContext: TestContext): PackageManager {
testContext.config.setup(x => x.requiredSqlPythonPackages).returns( () => [
{ name: 'pymssql', version: '2.1.4' },
{ name: 'sqlmlutils', version: '' }
]);
testContext.config.setup(x => x.requiredSqlRPackages).returns( () => [
{ name: 'RODBCext', repository: 'https://cran.microsoft.com' },
{ name: 'sqlmlutils', fileName: 'sqlmlutils_0.7.1.zip', downloadUrl: 'https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.zip?raw=true'}
]);
testContext.httpClient.setup(x => x.download(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.config.setup(x => x.pythonExecutable).returns(() => 'python');
testContext.config.setup(x => x.rExecutable).returns(() => 'r');
testContext.config.setup(x => x.rEnabled).returns(() => true);
testContext.config.setup(x => x.pythonEnabled).returns(() => true);
let packageManager = new PackageManager(
testContext.outputChannel,
'',
testContext.apiWrapper.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);
packageManager.init();
packageManager.dependenciesInstalled = true;
return packageManager;
}
});

View File

@@ -0,0 +1,399 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
import { SqlPythonPackageManageProvider } from '../../packageManagement/sqlPythonPackageManageProvider';
import { createContext, TestContext } from './utils';
import * as nbExtensionApis from '../../typings/notebookServices';
describe('SQL Python Package Manager', () => {
it('Should create SQL package manager successfully', async function (): Promise<void> {
let testContext = createContext();
should.doesNotThrow(() => createProvider(testContext));
});
it('Should return provider Id and target correctly', async function (): Promise<void> {
let testContext = createContext();
let provider = createProvider(testContext);
should.deepEqual(SqlPythonPackageManageProvider.ProviderId, provider.providerId);
should.deepEqual({ location: 'SQL', packageType: 'Python' }, provider.packageTarget);
});
it('listPackages Should return packages sorted by name', async function (): Promise<void> {
let testContext = createContext();
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'b-name',
'version': '1.1.1'
},
{
'name': 'a-name',
'version': '1.1.2'
}
];
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
should.deepEqual(actual, expected);
});
it('listPackages Should return packages sorted by name and version', async function (): Promise<void> {
let testContext = createContext();
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'b-name',
'version': '1.1.1'
},
{
'name': 'b-name',
'version': '1.1.2'
}
];
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'b-name',
'version': '1.1.1'
},
{
'name': 'b-name',
'version': '1.1.2'
}
];
should.deepEqual(actual, expected);
});
it('listPackages Should return empty packages if undefined packages returned', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
let packages: nbExtensionApis.IPackageDetails[];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
it('listPackages Should return empty packages if empty packages returned', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
it('installPackages Should install given packages successfully', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
let credentials = { [azdata.ConnectionOptionSpecialType.password]: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((path, scripts: string[]) => {
if (path && scripts.find(x => x.indexOf('install') > 0) &&
scripts.find(x => x.indexOf('port=1433') > 0) &&
scripts.find(x => x.indexOf('server="serverName"') > 0) &&
scripts.find(x => x.indexOf('database="databaseName"') > 0) &&
scripts.find(x => x.indexOf('package="a-name"') > 0) &&
scripts.find(x => x.indexOf('version="1.1.2"') > 0) &&
scripts.find(x => x.indexOf('pwd="password"') > 0)) {
packagesUpdated = true;
}
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
it('uninstallPackages Should uninstall given packages successfully', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
let credentials = { [azdata.ConnectionOptionSpecialType.password]: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((path, scripts: string[]) => {
if (path && scripts.find(x => x.indexOf('uninstall') > 0) &&
scripts.find(x => x.indexOf('port=1433') > 0) &&
scripts.find(x => x.indexOf('server="serverName"') > 0) &&
scripts.find(x => x.indexOf('database="databaseName"') > 0) &&
scripts.find(x => x.indexOf('package_name="a-name"') > 0) &&
scripts.find(x => x.indexOf('pwd="password"') > 0)) {
packagesUpdated = true;
}
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
it('installPackages Should include port name in the script', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName,3433';
connection.databaseName = 'databaseName';
let credentials = { [azdata.ConnectionOptionSpecialType.password]: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((path, scripts: string[]) => {
if (path && scripts.find(x => x.indexOf('install') > 0) &&
scripts.find(x => x.indexOf('port=3433') > 0) &&
scripts.find(x => x.indexOf('server="serverName"') > 0) &&
scripts.find(x => x.indexOf('database="databaseName"') > 0) &&
scripts.find(x => x.indexOf('package="a-name"') > 0) &&
scripts.find(x => x.indexOf('version="1.1.2"') > 0) &&
scripts.find(x => x.indexOf('pwd="password"') > 0)) {
packagesUpdated = true;
}
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
it('installPackages Should not install any packages give empty list', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
];
let connection = new azdata.connection.ConnectionProfile();
let credentials = { ['azdata.ConnectionOptionSpecialType.password']: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
packagesUpdated = true;
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
it('uninstallPackages Should not uninstall any packages give empty list', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
];
let connection = new azdata.connection.ConnectionProfile();
let credentials = { ['azdata.ConnectionOptionSpecialType.password']: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
packagesUpdated = true;
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
it('canUseProvider Should return false for no connection', async function (): Promise<void> {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('canUseProvider Should return false if connection does not have python installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('canUseProvider Should return true if connection has python installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, true);
});
it('canUseProvider Should return false if python is disabled in setting', async function (): Promise<void> {
let testContext = createContext();
let provider = createProvider(testContext);
testContext.config.setup(x => x.pythonEnabled).returns(() => false);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('getPackageOverview Should return package info using python packages provider', async function (): Promise<void> {
let testContext = createContext();
let packagePreview = {
name: 'package name',
versions: ['0.0.2', '0.0.1'],
summary: 'package summary'
};
testContext.httpClient.setup(x => x.fetch(TypeMoq.It.isAny())).returns(() => {
return Promise.resolve(`{"info":{"summary":"package summary"}, "releases":{"0.0.1":[{"comment_text":""}], "0.0.2":[{"comment_text":""}]}}`);
});
let provider = createProvider(testContext);
let actual = await provider.getPackageOverview('package name');
should.deepEqual(actual, packagePreview);
});
it('getLocations Should return empty array for no connection', async function (): Promise<void> {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
let actual = await provider.getLocations();
should.deepEqual(actual, []);
});
it('getLocations Should return database names for valid connection', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
const databaseNames = [
'db1',
'db2'
];
const expected = [
{
displayName: 'db1',
name: 'db1'
},
{
displayName: 'db2',
name: 'db2'
}
];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.listDatabases(connection.connectionId)).returns(() => { return Promise.resolve(databaseNames); });
let provider = createProvider(testContext);
let actual = await provider.getLocations();
should.deepEqual(actual, expected);
});
function createProvider(testContext: TestContext): SqlPythonPackageManageProvider {
testContext.config.setup(x => x.pythonExecutable).returns(() => 'python');
testContext.config.setup(x => x.pythonEnabled).returns(() => true);
return new SqlPythonPackageManageProvider(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);
}
});

View File

@@ -0,0 +1,325 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
import { SqlRPackageManageProvider } from '../../packageManagement/sqlRPackageManageProvider';
import { createContext, TestContext } from './utils';
import * as nbExtensionApis from '../../typings/notebookServices';
describe('SQL R Package Manager', () => {
it('Should create SQL package manager successfully', async function (): Promise<void> {
let testContext = createContext();
should.doesNotThrow(() => createProvider(testContext));
});
it('Should return provider Id and target correctly', async function (): Promise<void> {
let testContext = createContext();
let provider = createProvider(testContext);
should.deepEqual(SqlRPackageManageProvider.ProviderId, provider.providerId);
should.deepEqual({ location: 'SQL', packageType: 'R' }, provider.packageTarget);
});
it('listPackages Should return packages sorted by name', async function (): Promise<void> {
let testContext = createContext();
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'b-name',
'version': '1.1.1'
},
{
'name': 'a-name',
'version': '1.1.2'
}
];
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
should.deepEqual(actual, expected);
});
it('listPackages Should return empty packages if undefined packages returned', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
let packages: nbExtensionApis.IPackageDetails[];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
it('listPackages Should return empty packages if empty packages returned', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
it('installPackages Should install given packages successfully', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
let credentials = { [azdata.ConnectionOptionSpecialType.password]: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((path, scripts: string[]) => {
if (path && scripts.find(x => x.indexOf('install') > 0) &&
scripts.find(x => x.indexOf('server="serverName"') > 0) &&
scripts.find(x => x.indexOf('database="databaseName"') > 0) &&
scripts.find(x => x.indexOf('"a-name"') > 0) &&
scripts.find(x => x.indexOf('pwd="password"') > 0)) {
packagesUpdated = true;
}
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
it('uninstallPackages Should uninstall given packages successfully', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
{
'name': 'a-name',
'version': '1.1.2'
},
{
'name': 'b-name',
'version': '1.1.1'
}
];
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
let credentials = { [azdata.ConnectionOptionSpecialType.password]: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((path, scripts: string[]) => {
if (path && scripts.find(x => x.indexOf('remove') > 0) &&
scripts.find(x => x.indexOf('server="serverName"') > 0) &&
scripts.find(x => x.indexOf('database="databaseName"') > 0) &&
scripts.find(x => x.indexOf('"a-name"') > 0) &&
scripts.find(x => x.indexOf('pwd="password"') > 0)) {
packagesUpdated = true;
}
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
it('installPackages Should not install any packages give empty list', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
];
let connection = new azdata.connection.ConnectionProfile();
let credentials = { ['azdata.ConnectionOptionSpecialType.password']: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
packagesUpdated = true;
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
it('uninstallPackages Should not uninstall any packages give empty list', async function (): Promise<void> {
let testContext = createContext();
let packagesUpdated = false;
let packages: nbExtensionApis.IPackageDetails[] = [
];
let connection = new azdata.connection.ConnectionProfile();
let credentials = { ['azdata.ConnectionOptionSpecialType.password']: 'password' };
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.getCredentials(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(credentials); });
testContext.processService.setup(x => x.execScripts(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
packagesUpdated = true;
return Promise.resolve('');
});
let provider = createProvider(testContext);
await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
it('canUseProvider Should return false for no connection', async function (): Promise<void> {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('canUseProvider Should return false if connection does not have r installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('canUseProvider Should return true if connection has r installed', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.serverConfigManager.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
should.deepEqual(actual, true);
});
it('canUseProvider Should return false if r is disabled in setting', async function (): Promise<void> {
let testContext = createContext();
let provider = createProvider(testContext);
testContext.config.setup(x => x.rEnabled).returns(() => false);
let actual = await provider.canUseProvider();
should.deepEqual(actual, false);
});
it('getPackageOverview Should return package info successfully', async function (): Promise<void> {
let testContext = createContext();
let packagePreview = {
'name': 'a-name',
'versions': ['Latest'],
'summary': ''
};
testContext.httpClient.setup(x => x.fetch(TypeMoq.It.isAny())).returns(() => {
return Promise.resolve(``);
});
let provider = createProvider(testContext);
let actual = await provider.getPackageOverview('a-name');
should.deepEqual(actual, packagePreview);
});
it('getLocations Should return empty array for no connection', async function (): Promise<void> {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
let actual = await provider.getLocations();
should.deepEqual(actual, []);
});
it('getLocations Should return database names for valid connection', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
const databaseNames = [
'db1',
'db2'
];
const expected = [
{
displayName: 'db1',
name: 'db1'
},
{
displayName: 'db2',
name: 'db2'
}
];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.listDatabases(connection.connectionId)).returns(() => { return Promise.resolve(databaseNames); });
let provider = createProvider(testContext);
let actual = await provider.getLocations();
should.deepEqual(actual, expected);
});
function createProvider(testContext: TestContext): SqlRPackageManageProvider {
testContext.config.setup(x => x.rExecutable).returns(() => 'r');
testContext.config.setup(x => x.rEnabled).returns(() => true);
testContext.config.setup(x => x.rPackagesRepository).returns(() => 'http://cran.r-project.org');
return new SqlRPackageManageProvider(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);
}
});

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* 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 azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../common/apiWrapper';
import { QueryRunner } from '../../common/queryRunner';
import { ProcessService } from '../../common/processService';
import { Config } from '../../configurations/config';
import { HttpClient } from '../../common/httpClient';
import * as utils from '../utils';
import { PackageManagementService } from '../../packageManagement/packageManagementService';
export interface TestContext {
outputChannel: vscode.OutputChannel;
processService: TypeMoq.IMock<ProcessService>;
apiWrapper: TypeMoq.IMock<ApiWrapper>;
queryRunner: TypeMoq.IMock<QueryRunner>;
config: TypeMoq.IMock<Config>;
op: azdata.BackgroundOperation;
getOpStatus: () => azdata.TaskStatus;
httpClient: TypeMoq.IMock<HttpClient>;
serverConfigManager: TypeMoq.IMock<PackageManagementService>;
}
export function createContext(): TestContext {
const context = utils.createContext();
return {
outputChannel: context.outputChannel,
processService: TypeMoq.Mock.ofType(ProcessService),
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
queryRunner: TypeMoq.Mock.ofType(QueryRunner),
config: TypeMoq.Mock.ofType(Config),
httpClient: TypeMoq.Mock.ofType(HttpClient),
op: context.op,
getOpStatus: context.getOpStatus,
serverConfigManager: TypeMoq.Mock.ofType(PackageManagementService)
};
}

View File

@@ -0,0 +1,301 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { ApiWrapper } from '../../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { PredictService } from '../../prediction/predictService';
import { QueryRunner } from '../../common/queryRunner';
import { ImportedModel } from '../../modelManagement/interfaces';
import { PredictParameters, DatabaseTable, TableColumn } from '../../prediction/interfaces';
import * as path from 'path';
import * as os from 'os';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import * as fs from 'fs';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
importTable: DatabaseTable;
queryRunner: TypeMoq.IMock<QueryRunner>;
}
function createContext(): TestContext {
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
importTable: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
},
queryRunner: TypeMoq.Mock.ofType(QueryRunner)
};
}
describe('PredictService', () => {
it('getDatabaseList should return databases successfully', async function (): Promise<void> {
const testContext = createContext();
const expected: string[] = [
'db1',
'db2'
];
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.apiWrapper.setup(x => x.listDatabases(TypeMoq.It.isAny())).returns(() => { return Promise.resolve(expected); });
let service = new PredictService(
testContext.apiWrapper.object,
testContext.queryRunner.object);
const actual = await service.getDatabaseList();
should.deepEqual(actual, expected);
});
it('getTableList should return tables successfully', async function (): Promise<void> {
const testContext = createContext();
const expected: DatabaseTable[] = [
{
databaseName: 'db1',
schema: 'dbo',
tableName: 'tb1'
},
{
databaseName: 'db1',
tableName: 'tb2',
schema: 'dbo'
}
];
const result = {
rowCount: 1,
columnInfo: [],
rows: [[
{
displayValue: 'tb1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'dbo',
isNull: false,
invariantCultureDisplayValue: ''
}
], [
{
displayValue: 'tb2',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'dbo',
isNull: false,
invariantCultureDisplayValue: ''
}
]]
};
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.safeRunQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(result));
let service = new PredictService(
testContext.apiWrapper.object,
testContext.queryRunner.object);
const actual = await service.getTableList('db1');
should.deepEqual(actual, expected);
});
it('getTableColumnsList should return table columns successfully', async function (): Promise<void> {
const testContext = createContext();
const expected: TableColumn[] = [
{
columnName: 'c1',
dataType: 'int'
},
{
columnName: 'c2',
dataType: 'varchar'
}
];
const table: DatabaseTable =
{
databaseName: 'db1',
schema: 'dbo',
tableName: 'tb1'
};
const result = {
rowCount: 1,
columnInfo: [],
rows: [[
{
displayValue: 'c1',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'int',
isNull: false,
invariantCultureDisplayValue: ''
}
], [
{
displayValue: 'c2',
isNull: false,
invariantCultureDisplayValue: ''
},
{
displayValue: 'varchar',
isNull: false,
invariantCultureDisplayValue: ''
}
]]
};
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.safeRunQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(result));
let service = new PredictService(
testContext.apiWrapper.object,
testContext.queryRunner.object);
const actual = await service.getTableColumnsList(table);
should.deepEqual(actual, expected);
});
it('generatePredictScript should generate the script successfully using model', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
const predictParams: PredictParameters = {
inputColumns: [
{
paramName: 'p1',
dataType: 'int',
columnName: ''
},
{
paramName: 'p2',
dataType: 'varchar',
columnName: ''
}
],
outputColumns: [
{
paramName: 'o1',
dataType: 'int',
columnName: ''
},
],
databaseName: '',
tableName: '',
schema: ''
};
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
version: '1.1',
table: testContext.importTable
};
let service = new PredictService(
testContext.apiWrapper.object,
testContext.queryRunner.object);
const document: vscode.TextDocument = {
uri: vscode.Uri.parse('file:///usr/home'),
fileName: '',
isUntitled: true,
languageId: 'sql',
version: 1,
isDirty: true,
isClosed: false,
save: undefined!,
eol: undefined!,
lineCount: 1,
lineAt: undefined!,
offsetAt: undefined!,
positionAt: undefined!,
getText: undefined!,
getWordRangeAtPosition: undefined!,
validateRange: undefined!,
validatePosition: undefined!
};
testContext.apiWrapper.setup(x => x.openTextDocument(TypeMoq.It.isAny())).returns(() => Promise.resolve(document));
testContext.apiWrapper.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.apiWrapper.setup(x => x.runQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => { });
const actual = await service.generatePredictScript(predictParams, model, undefined);
should.notEqual(actual, undefined);
should.equal(actual.indexOf('FROM PREDICT(MODEL = @model') > 0, true);
});
it('generatePredictScript should generate the script successfully using file', async function (): Promise<void> {
const testContext = createContext();
const connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
const predictParams: PredictParameters = {
inputColumns: [
{
paramName: 'p1',
dataType: 'int',
columnName: ''
},
{
paramName: 'p2',
dataType: 'varchar',
columnName: ''
}
],
outputColumns: [
{
paramName: 'o1',
dataType: 'int',
columnName: ''
},
],
databaseName: '',
tableName: '',
schema: ''
};
const tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${UUID.generateUuid()}`);
await fs.promises.writeFile(tempFilePath, 'test');
let service = new PredictService(
testContext.apiWrapper.object,
testContext.queryRunner.object);
const document: vscode.TextDocument = {
uri: vscode.Uri.parse('file:///usr/home'),
fileName: '',
isUntitled: true,
languageId: 'sql',
version: 1,
isDirty: true,
isClosed: false,
save: undefined!,
eol: undefined!,
lineCount: 1,
lineAt: undefined!,
offsetAt: undefined!,
positionAt: undefined!,
getText: undefined!,
getWordRangeAtPosition: undefined!,
validateRange: undefined!,
validatePosition: undefined!
};
testContext.apiWrapper.setup(x => x.openTextDocument(TypeMoq.It.isAny())).returns(() => Promise.resolve(document));
testContext.apiWrapper.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.apiWrapper.setup(x => x.runQuery(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => { });
const actual = await service.generatePredictScript(predictParams, undefined, tempFilePath);
should.notEqual(actual, undefined);
should.equal(actual.indexOf('FROM PREDICT(MODEL = 0X') > 0, true);
});
});

View File

@@ -0,0 +1,303 @@
/*---------------------------------------------------------------------------------------------
* 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 { ApiWrapper } from '../common/apiWrapper';
import * as TypeMoq from 'typemoq';
import * as should from 'should';
import { QueryRunner } from '../common/queryRunner';
import { IPackageDetails } from '../typings/notebookServices';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
queryProvider: azdata.QueryProvider;
}
function createContext(): TestContext {
return {
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
queryProvider: {
providerId: '',
cancelQuery: () => {return Promise.reject();},
runQuery: () => {return Promise.reject();},
runQueryStatement: () => {return Promise.reject();},
runQueryString: () => {return Promise.reject();},
runQueryAndReturn: () => { return Promise.reject(); },
parseSyntax: () => {return Promise.reject();},
getQueryRows: () => {return Promise.reject();},
disposeQuery: () => {return Promise.reject();},
saveResults: () => {return Promise.reject();},
setQueryExecutionOptions: () => {return Promise.reject();},
registerOnQueryComplete: () => {return Promise.reject();},
registerOnBatchStart: () => {return Promise.reject();},
registerOnBatchComplete: () => {return Promise.reject();},
registerOnResultSetAvailable: () => {return Promise.reject();},
registerOnResultSetUpdated: () => {return Promise.reject();},
registerOnMessage: () => {return Promise.reject();},
commitEdit: () => {return Promise.reject();},
createRow: () => {return Promise.reject();},
deleteRow: () => {return Promise.reject();},
disposeEdit: () => {return Promise.reject();},
initializeEdit: () => {return Promise.reject();},
revertCell: () => {return Promise.reject();},
revertRow: () => {return Promise.reject();},
updateCell: () => {return Promise.reject();},
getEditRows: () => {return Promise.reject();},
registerOnEditSessionReady: () => {return Promise.reject();},
}
};
}
describe('Query Runner', () => {
it('getPythonPackages Should return empty list if not provider found', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
let queryProvider: azdata.QueryProvider;
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => queryProvider);
let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, []);
});
it('getPythonPackages Should return empty list if not provider throws', async function (): Promise<void> {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.reject(); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, []);
});
it('getPythonPackages Should return list if provider runs the query successfully', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
[{
displayValue: 'p1',
isNull: false,
invariantCultureDisplayValue: ''
}, {
displayValue: '1.1.1',
isNull: false,
invariantCultureDisplayValue: ''
}],
[{
displayValue: 'p2',
isNull: false,
invariantCultureDisplayValue: ''
}, {
displayValue: '1.1.2',
isNull: false,
invariantCultureDisplayValue: ''
}]
];
let expected = [
{
'name': 'p1',
'version': '1.1.1'
},
{
'name': 'p2',
'version': '1.1.2'
}
];
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, expected);
});
it('getPythonPackages Should return empty list if provider return no rows', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
];
let expected: IPackageDetails[] = [];
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, expected);
});
it('updateExternalScriptConfig Should update config successfully', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
];
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
await should(queryRunner.updateExternalScriptConfig(connection, true)).resolved();
});
it('isPythonInstalled Should return true is provider returns valid result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
[{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
}]
];
let expected = true;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isPythonInstalled(connection);
should.deepEqual(actual, expected);
});
it('isPythonInstalled Should return true is provider returns 0 as result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
[{
displayValue: '0',
isNull: false,
invariantCultureDisplayValue: ''
}]
];
let expected = false;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isPythonInstalled(connection);
should.deepEqual(actual, expected);
});
it('isPythonInstalled Should return false is provider returns no result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [];
let expected = false;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isPythonInstalled(connection);
should.deepEqual(actual, expected);
});
it('isMachineLearningServiceEnabled Should return true is provider returns valid result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
[{
displayValue: '1',
isNull: false,
invariantCultureDisplayValue: ''
}]
];
let expected = true;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isMachineLearningServiceEnabled(connection);
should.deepEqual(actual, expected);
});
it('isMachineLearningServiceEnabled Should return true is provider returns 0 as result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [
[{
displayValue: '0',
isNull: false,
invariantCultureDisplayValue: ''
}]
];
let expected = false;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isMachineLearningServiceEnabled(connection);
should.deepEqual(actual, expected);
});
it('isMachineLearningServiceEnabled Should return false is provider returns no result', async function (): Promise<void> {
let testContext = createContext();
let rows: azdata.DbCellValue[][] = [];
let expected = false;
let result : azdata.SimpleExecuteResult = {
rowCount: 2,
columnInfo: [],
rows: rows,
};
let connection = new azdata.connection.ConnectionProfile();
let queryRunner = new QueryRunner(testContext.apiWrapper.object);
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider<azdata.QueryProvider>(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
let actual = await queryRunner.isMachineLearningServiceEnabled(connection);
should.deepEqual(actual, expected);
});
});

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* 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 azdata from 'azdata';
export interface TestContext {
outputChannel: vscode.OutputChannel;
op: azdata.BackgroundOperation;
getOpStatus: () => azdata.TaskStatus;
}
export function createContext(): TestContext {
let opStatus: azdata.TaskStatus;
return {
outputChannel: {
name: '',
append: () => { },
appendLine: () => { },
clear: () => { },
show: () => { },
hide: () => { },
dispose: () => { }
},
op: {
updateStatus: (status: azdata.TaskStatus) => {
opStatus = status;
},
id: '',
onCanceled: new vscode.EventEmitter<void>().event,
},
getOpStatus: () => { return opStatus; }
};
}

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../common/apiWrapper';
import { createViewContext } from './utils';
import { DashboardWidget } from '../../views/widgets/dashboardWidget';
interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
view: azdata.ModelView;
onClick: vscode.EventEmitter<any>;
}
function createContext(): TestContext {
let viewTestContext = createViewContext();
return {
apiWrapper: viewTestContext.apiWrapper,
view: viewTestContext.view,
onClick: viewTestContext.onClick
};
}
describe('Dashboard widget', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
const dashboard = new DashboardWidget(testContext.apiWrapper.object, '');
dashboard.register();
testContext.onClick.fire(undefined);
testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
});
});

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext, ParentDialog } from './utils';
import { AddEditLanguageTab } from '../../../views/externalLanguages/addEditLanguageTab';
import { LanguageUpdateModel } from '../../../views/externalLanguages/languageViewBase';
describe('Add Edit External Languages Tab', () => {
it('Should create AddEditLanguageTab for new language successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: parent.createNewContent(),
language: parent.createNewLanguage(),
newLang: true
};
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
should.notEqual(tab.languageView, undefined, 'Failed to create language view for add');
});
it('Should create AddEditLanguageTab for edit successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: {
extensionFileName: 'filename',
isLocalFile: true,
pathToExtension: 'path',
},
language: {
name: 'name',
contents: []
},
newLang: false
};
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
should.notEqual(tab.languageView, undefined, 'Failed to create language view for edit');
should.equal(tab.saveButton, undefined);
});
it('Should reset AddEditLanguageTab successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: {
extensionFileName: 'filename',
isLocalFile: true,
pathToExtension: 'path',
},
language: {
name: 'name',
contents: []
},
newLang: false
};
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
if (tab.languageName) {
tab.languageName.value = 'some value';
}
await tab.reset();
should.equal(tab.languageName?.value, 'name');
});
it('Should load content successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: {
extensionFileName: 'filename',
isLocalFile: true,
pathToExtension: 'path',
environmentVariables: 'env vars',
parameters: 'params'
},
language: {
name: 'name',
contents: []
},
newLang: false
};
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
let content = tab.languageView?.updatedContent;
should.notEqual(content, undefined);
if (content) {
should.equal(content.extensionFileName, languageUpdateModel.content.extensionFileName);
should.equal(content.pathToExtension, languageUpdateModel.content.pathToExtension);
should.equal(content.environmentVariables, languageUpdateModel.content.environmentVariables);
should.equal(content.parameters, languageUpdateModel.content.parameters);
}
});
it('Should raise save event if save button clicked ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: parent.createNewContent(),
language: parent.createNewLanguage(),
newLang: true
};
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
should.notEqual(tab.saveButton, undefined);
let updateCalled = false;
let promise = new Promise(resolve => {
parent.onUpdate(() => {
updateCalled = true;
resolve();
});
});
testContext.onClick.fire(undefined);
parent.onUpdatedLanguage(languageUpdateModel);
await promise;
should.equal(updateCalled, true);
should.notEqual(tab.updatedData, undefined);
});
});

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
import { createContext } from './utils';
import { LanguageController } from '../../../views/externalLanguages/languageController';
import * as mssql from '../../../../../mssql';
describe('External Languages Controller', () => {
it('Should open dialog for manage languages successfully ', async function (): Promise<void> {
let testContext = createContext();
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
let dialog = await controller.manageLanguages();
testContext.apiWrapper.verify(x => x.openDialog(TypeMoq.It.isAny()), TypeMoq.Times.once());
should.notEqual(dialog, undefined);
});
it('Should list languages successfully ', async function (): Promise<void> {
let testContext = createContext();
let languages: mssql.ExternalLanguage[] = [{
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
}];
testContext.dialogModel.setup( x=> x.getLanguageList()).returns(() => Promise.resolve(languages));
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
let dialog = await controller.manageLanguages();
let actual = await dialog.listLanguages();
should.deepEqual(actual, languages);
});
it('Should update languages successfully ', async function (): Promise<void> {
let testContext = createContext();
let language: mssql.ExternalLanguage = {
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
};
testContext.dialogModel.setup( x=> x.updateLanguage(language)).returns(() => Promise.resolve());
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
let dialog = await controller.manageLanguages();
await dialog.updateLanguage({
language: language,
content: language.contents[0],
newLang: false
});
testContext.dialogModel.verify(x => x.updateLanguage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('Should delete language successfully ', async function (): Promise<void> {
let testContext = createContext();
let language: mssql.ExternalLanguage = {
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
};
testContext.dialogModel.setup( x=> x.deleteLanguage(language.name)).returns(() => Promise.resolve());
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
let dialog = await controller.manageLanguages();
await dialog.deleteLanguage({
language: language,
content: language.contents[0],
newLang: false
});
testContext.dialogModel.verify(x => x.deleteLanguage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
it('Should open edit dialog for edit language', async function (): Promise<void> {
let testContext = createContext();
let language: mssql.ExternalLanguage = {
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
};
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
let dialog = await controller.manageLanguages();
dialog.onEditLanguage({
language: language,
content: language.contents[0],
newLang: false
});
testContext.apiWrapper.verify(x => x.openDialog(TypeMoq.It.isAny()), TypeMoq.Times.exactly(2));
should.notEqual(dialog, undefined);
});
});

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext, ParentDialog } from './utils';
import { LanguageEditDialog } from '../../../views/externalLanguages/languageEditDialog';
import { LanguageUpdateModel } from '../../../views/externalLanguages/languageViewBase';
describe('Edit External Languages Dialog', () => {
it('Should open dialog successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: parent.createNewContent(),
language: parent.createNewLanguage(),
newLang: true
};
let dialog = new LanguageEditDialog(testContext.apiWrapper.object, parent, languageUpdateModel);
dialog.showDialog();
should.notEqual(dialog.addNewLanguageTab, undefined);
});
it('Should raise save event if save button clicked ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let languageUpdateModel: LanguageUpdateModel = {
content: parent.createNewContent(),
language: parent.createNewLanguage(),
newLang: true
};
let dialog = new LanguageEditDialog(testContext.apiWrapper.object, parent, languageUpdateModel);
dialog.showDialog();
let updateCalled = false;
let promise = new Promise(resolve => {
parent.onUpdate(() => {
updateCalled = true;
parent.onUpdatedLanguage(languageUpdateModel);
resolve();
});
});
dialog.onSave();
await promise;
should.equal(updateCalled, true);
});
});

View File

@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext } from './utils';
import { LanguagesDialog } from '../../../views/externalLanguages/languagesDialog';
describe('External Languages Dialog', () => {
it('Should open dialog successfully ', async function (): Promise<void> {
let testContext = createContext();
let dialog = new LanguagesDialog(testContext.apiWrapper.object, '');
dialog.showDialog();
should.notEqual(dialog.addNewLanguageTab, undefined);
should.notEqual(dialog.currentLanguagesTab, undefined);
});
});

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext } from './utils';
import * as mssql from '../../../../../mssql';
import { LanguageService } from '../../../externalLanguage/languageService';
describe('External Languages Dialog Model', () => {
it('Should list languages successfully ', async function (): Promise<void> {
let testContext = createContext();
let languages: mssql.ExternalLanguage[] = [{
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
}];
testContext.languageExtensionService.listLanguages = () => {return Promise.resolve(languages);};
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
await model.load();
let actual = await model.getLanguageList();
should.deepEqual(actual, languages);
});
it('Should update language successfully ', async function (): Promise<void> {
let testContext = createContext();
let language: mssql.ExternalLanguage = {
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
};
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
await model.load();
await should(model.updateLanguage(language)).resolved();
});
it('Should delete language successfully ', async function (): Promise<void> {
let testContext = createContext();
let language: mssql.ExternalLanguage = {
name: '',
contents: [{
extensionFileName: '',
isLocalFile: true,
pathToExtension: '',
}]
};
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
await model.load();
await should(model.deleteLanguage(language.name)).resolved();
});
});

View File

@@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../../common/apiWrapper';
import { LanguageViewBase } from '../../../views/externalLanguages/languageViewBase';
import * as mssql from '../../../../../mssql';
import { LanguageService } from '../../../externalLanguage/languageService';
import { createViewContext } from '../utils';
export interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
view: azdata.ModelView;
languageExtensionService: mssql.ILanguageExtensionService;
onClick: vscode.EventEmitter<any>;
dialogModel: TypeMoq.IMock<LanguageService>;
}
export class ParentDialog extends LanguageViewBase {
public reset(): Promise<void> {
return Promise.resolve();
}
constructor(
apiWrapper: ApiWrapper) {
super(apiWrapper, '');
}
}
export function createContext(): TestContext {
let viewTestContext = createViewContext();
let connection = new azdata.connection.ConnectionProfile();
viewTestContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
viewTestContext.apiWrapper.setup(x => x.getUriForConnection(TypeMoq.It.isAny())).returns(() => { return Promise.resolve('connectionUrl'); });
let languageExtensionService: mssql.ILanguageExtensionService = {
listLanguages: () => { return Promise.resolve([]); },
deleteLanguage: () => { return Promise.resolve(); },
updateLanguage: () => { return Promise.resolve(); }
};
return {
apiWrapper: viewTestContext.apiWrapper,
view: viewTestContext.view,
languageExtensionService: languageExtensionService,
onClick: viewTestContext.onClick,
dialogModel: TypeMoq.Mock.ofType(LanguageService)
};
}

View File

@@ -0,0 +1,202 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import * as TypeMoq from 'typemoq';
import 'mocha';
import { createContext } from './utils';
import { ImportedModel, ModelParameters } from '../../../modelManagement/interfaces';
import { azureResource } from '../../../typings/azure-resource';
import { Workspace } from '@azure/arm-machinelearningservices/esm/models';
import { WorkspaceModel } from '../../../modelManagement/interfaces';
import { ModelManagementController } from '../../../views/models/modelManagementController';
import { DatabaseTable, TableColumn } from '../../../prediction/interfaces';
import { DeleteModelEventName, UpdateModelEventName } from '../../../views/models/modelViewBase';
import { EditModelDialog } from '../../../views/models/manageModels/editModelDialog';
const accounts: azdata.Account[] = [
{
key: {
accountId: '1',
providerId: ''
},
displayInfo: {
displayName: 'account',
userId: '',
accountType: '',
contextualDisplayName: ''
},
isStale: false,
properties: []
}
];
const subscriptions: azureResource.AzureResourceSubscription[] = [
{
name: 'subscription',
id: '2'
}
];
const groups: azureResource.AzureResourceResourceGroup[] = [
{
name: 'group',
id: '3'
}
];
const workspaces: Workspace[] = [
{
name: 'workspace',
id: '4'
}
];
const models: WorkspaceModel[] = [
{
id: '5',
name: 'model'
}
];
const localModels: ImportedModel[] = [
{
id: 1,
modelName: 'model',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
}
];
const dbNames: string[] = [
'db1',
'db2'
];
const tableNames: DatabaseTable[] = [
{
databaseName: 'db1',
schema: 'dbo',
tableName: 'tb1'
},
{
databaseName: 'db1',
tableName: 'tb2',
schema: 'dbo'
}
];
const columnNames: TableColumn[] = [
{
columnName: 'c1',
dataType: 'int'
},
{
columnName: 'c2',
dataType: 'varchar'
}
];
const modelParameters: ModelParameters = {
inputs: [
{
'name': 'p1',
'type': 'int'
},
{
'name': 'p2',
'type': 'varchar'
}
],
outputs: [
{
'name': 'o1',
'type': 'int'
}
]
};
describe('Model Controller', () => {
it('Should open deploy model wizard successfully ', async function (): Promise<void> {
let testContext = createContext();
let controller = new ModelManagementController(testContext.apiWrapper.object, '', testContext.azureModelService.object, testContext.deployModelService.object, testContext.predictService.object);
testContext.deployModelService.setup(x => x.getRecentImportTable()).returns(() => Promise.resolve({
databaseName: 'db',
tableName: 'table',
schema: 'dbo'
}));
testContext.deployModelService.setup(x => x.getDeployedModels(TypeMoq.It.isAny())).returns(() => Promise.resolve(localModels));
testContext.predictService.setup(x => x.getDatabaseList()).returns(() => Promise.resolve(dbNames));
testContext.predictService.setup(x => x.getTableList(TypeMoq.It.isAny())).returns(() => Promise.resolve(tableNames));
testContext.azureModelService.setup(x => x.getAccounts()).returns(() => Promise.resolve(accounts));
testContext.azureModelService.setup(x => x.getSubscriptions(TypeMoq.It.isAny())).returns(() => Promise.resolve(subscriptions));
testContext.azureModelService.setup(x => x.getGroups(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(groups));
testContext.azureModelService.setup(x => x.getWorkspaces(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(workspaces));
testContext.azureModelService.setup(x => x.getModels(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(models));
const view = await controller.registerModel(undefined);
should.notEqual(view, undefined);
});
it('Should open predict wizard successfully ', async function (): Promise<void> {
let testContext = createContext();
let controller = new ModelManagementController(testContext.apiWrapper.object, '', testContext.azureModelService.object, testContext.deployModelService.object, testContext.predictService.object);
testContext.deployModelService.setup(x => x.getRecentImportTable()).returns(() => Promise.resolve({
databaseName: 'db',
tableName: 'table',
schema: 'dbo'
}));
testContext.deployModelService.setup(x => x.getDeployedModels(TypeMoq.It.isAny())).returns(() => Promise.resolve(localModels));
testContext.predictService.setup(x => x.getDatabaseList()).returns(() => Promise.resolve([
'db', 'db1'
]));
testContext.predictService.setup(x => x.getTableList(TypeMoq.It.isAny())).returns(() => Promise.resolve([
{ tableName: 'tb', databaseName: 'db', schema: 'dbo' }
]));
testContext.azureModelService.setup(x => x.getAccounts()).returns(() => Promise.resolve(accounts));
testContext.azureModelService.setup(x => x.getSubscriptions(TypeMoq.It.isAny())).returns(() => Promise.resolve(subscriptions));
testContext.azureModelService.setup(x => x.getGroups(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(groups));
testContext.azureModelService.setup(x => x.getWorkspaces(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(workspaces));
testContext.azureModelService.setup(x => x.getModels(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(models));
testContext.predictService.setup(x => x.getTableColumnsList(TypeMoq.It.isAny())).returns(() => Promise.resolve(columnNames));
testContext.deployModelService.setup(x => x.loadModelParameters(TypeMoq.It.isAny())).returns(() => Promise.resolve(modelParameters));
testContext.azureModelService.setup(x => x.downloadModel(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('file'));
testContext.deployModelService.setup(x => x.downloadModel(TypeMoq.It.isAny())).returns(() => Promise.resolve('file'));
const view = await controller.predictModel();
should.notEqual(view, undefined);
});
it('Should open edit model dialog successfully ', async function (): Promise<void> {
let testContext = createContext();
testContext.deployModelService.setup(x => x.updateModel(TypeMoq.It.isAny())).returns(() => Promise.resolve());
testContext.deployModelService.setup(x => x.deleteModel(TypeMoq.It.isAny())).returns(() => Promise.resolve());
let controller = new ModelManagementController(testContext.apiWrapper.object, '', testContext.azureModelService.object, testContext.deployModelService.object, testContext.predictService.object);
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
version: '1.1',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
};
const view = <EditModelDialog>await controller.editModel(model);
should.notEqual(view?.editModelPage, undefined);
if (view.editModelPage) {
view.editModelPage.sendRequest(UpdateModelEventName, model);
view.editModelPage.sendRequest(DeleteModelEventName, model);
}
testContext.deployModelService.verify(x => x.updateModel(model), TypeMoq.Times.atLeastOnce());
testContext.deployModelService.verify(x => x.deleteModel(model), TypeMoq.Times.atLeastOnce());
should.notEqual(view, undefined);
});
});

View File

@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import { createContext, ParentDialog } from './utils';
import { AzureModelsComponent } from '../../../views/models/azureModelsComponent';
import { ListAccountsEventName, ListSubscriptionsEventName, ListGroupsEventName, ListWorkspacesEventName, ListAzureModelsEventName } from '../../../views/models/modelViewBase';
import { azureResource } from '../../../typings/azure-resource';
import { Workspace } from '@azure/arm-machinelearningservices/esm/models';
import { ViewBase } from '../../../views/viewBase';
import { WorkspaceModel } from '../../../modelManagement/interfaces';
describe('Azure Models Component', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let view = new AzureModelsComponent(testContext.apiWrapper.object, parent);
view.registerComponent(testContext.view.modelBuilder);
should.notEqual(view.component, undefined);
});
it('Should load data successfully ', async function (): Promise<void> {
let testContext = createContext();
let parent = new ParentDialog(testContext.apiWrapper.object);
let view = new AzureModelsComponent(testContext.apiWrapper.object, parent, false);
view.registerComponent(testContext.view.modelBuilder);
let accounts: azdata.Account[] = [
{
key: {
accountId: '1',
providerId: ''
},
displayInfo: {
displayName: 'account',
userId: '',
accountType: '',
contextualDisplayName: ''
},
isStale: false,
properties: []
}
];
let subscriptions: azureResource.AzureResourceSubscription[] = [
{
name: 'subscription',
id: '2'
}
];
let groups: azureResource.AzureResourceResourceGroup[] = [
{
name: 'group',
id: '3'
}
];
let workspaces: Workspace[] = [
{
name: 'workspace',
id: '4'
}
];
let models: WorkspaceModel[] = [
{
id: '5',
name: 'model'
}
];
parent.on(ListAccountsEventName, () => {
parent.sendCallbackRequest(ViewBase.getCallbackEventName(ListAccountsEventName), { data: accounts });
});
parent.on(ListSubscriptionsEventName, () => {
parent.sendCallbackRequest(ViewBase.getCallbackEventName(ListSubscriptionsEventName), { data: subscriptions });
});
parent.on(ListGroupsEventName, () => {
parent.sendCallbackRequest(ViewBase.getCallbackEventName(ListGroupsEventName), { data: groups });
});
parent.on(ListWorkspacesEventName, () => {
parent.sendCallbackRequest(ViewBase.getCallbackEventName(ListWorkspacesEventName), { data: workspaces });
});
parent.on(ListAzureModelsEventName, () => {
parent.sendCallbackRequest(ViewBase.getCallbackEventName(ListAzureModelsEventName), { data: models });
});
await view.refresh();
testContext.onClick.fire(true);
should.notEqual(view.data, undefined);
should.equal(view.data?.length, 1);
if (view.data) {
should.deepEqual(view.data[0].account, accounts[0]);
should.deepEqual(view.data[0].subscription, subscriptions[0]);
should.deepEqual(view.data[0].group, groups[0]);
should.deepEqual(view.data[0].workspace, workspaces[0]);
should.deepEqual(view.data[0].model, models[0]);
}
});
});

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext } from './utils';
import { ImportedModel } from '../../../modelManagement/interfaces';
import { EditModelDialog } from '../../../views/models/manageModels/editModelDialog';
describe('Edit Model Dialog', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
const model: ImportedModel =
{
id: 1,
modelName: 'name1',
description: 'desc1',
created: '2018-01-01',
version: '1.1',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
};
let view = new EditModelDialog(testContext.apiWrapper.object, '', undefined, model);
view.open();
should.notEqual(view.dialogView, undefined);
});
});

View File

@@ -0,0 +1,203 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import { createContext } from './utils';
import {
ListModelsEventName, ListAccountsEventName, ListSubscriptionsEventName, ListGroupsEventName, ListWorkspacesEventName,
ListAzureModelsEventName, ListDatabaseNamesEventName, ListTableNamesEventName, ListColumnNamesEventName, LoadModelParametersEventName, DownloadAzureModelEventName, DownloadRegisteredModelEventName, ModelSourceType
}
from '../../../views/models/modelViewBase';
import { ImportedModel, ModelParameters } from '../../../modelManagement/interfaces';
import { azureResource } from '../../../typings/azure-resource';
import { Workspace } from '@azure/arm-machinelearningservices/esm/models';
import { ViewBase } from '../../../views/viewBase';
import { WorkspaceModel } from '../../../modelManagement/interfaces';
import { PredictWizard } from '../../../views/models/prediction/predictWizard';
import { DatabaseTable, TableColumn } from '../../../prediction/interfaces';
describe('Predict Wizard', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new PredictWizard(testContext.apiWrapper.object, '');
await view.open();
should.notEqual(view.wizardView, undefined);
should.notEqual(view.modelSourcePage, undefined);
});
it('Should load data successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new PredictWizard(testContext.apiWrapper.object, '');
view.importTable = {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
};
await view.open();
let accounts: azdata.Account[] = [
{
key: {
accountId: '1',
providerId: ''
},
displayInfo: {
displayName: 'account',
userId: '',
accountType: '',
contextualDisplayName: ''
},
isStale: false,
properties: []
}
];
let subscriptions: azureResource.AzureResourceSubscription[] = [
{
name: 'subscription',
id: '2'
}
];
let groups: azureResource.AzureResourceResourceGroup[] = [
{
name: 'group',
id: '3'
}
];
let workspaces: Workspace[] = [
{
name: 'workspace',
id: '4'
}
];
let models: WorkspaceModel[] = [
{
id: '5',
name: 'model'
}
];
let localModels: ImportedModel[] = [
{
id: 1,
modelName: 'model',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
}
];
const dbNames: string[] = [
'db1',
'db2'
];
const tableNames: DatabaseTable[] = [
{
databaseName: 'db1',
schema: 'dbo',
tableName: 'tb1'
},
{
databaseName: 'db1',
tableName: 'tb2',
schema: 'dbo'
}
];
const columnNames: TableColumn[] = [
{
columnName: 'c1',
dataType: 'int'
},
{
columnName: 'c2',
dataType: 'varchar'
}
];
const modelParameters: ModelParameters = {
inputs: [
{
'name': 'p1',
'type': 'int'
},
{
'name': 'p2',
'type': 'varchar'
}
],
outputs: [
{
'name': 'o1',
'type': 'int'
}
]
};
view.on(ListModelsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListModelsEventName), { data: localModels });
});
view.on(ListAccountsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListAccountsEventName), { data: accounts });
});
view.on(ListSubscriptionsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListSubscriptionsEventName), { data: subscriptions });
});
view.on(ListGroupsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListGroupsEventName), { data: groups });
});
view.on(ListWorkspacesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListWorkspacesEventName), { data: workspaces });
});
view.on(ListAzureModelsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListAzureModelsEventName), { data: models });
});
view.on(ListDatabaseNamesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListDatabaseNamesEventName), { data: dbNames });
});
view.on(ListTableNamesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListTableNamesEventName), { data: tableNames });
});
view.on(ListColumnNamesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListColumnNamesEventName), { data: columnNames });
});
view.on(LoadModelParametersEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(LoadModelParametersEventName), { data: modelParameters });
});
view.on(DownloadAzureModelEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(DownloadAzureModelEventName), { data: 'path' });
});
view.on(DownloadRegisteredModelEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(DownloadRegisteredModelEventName), { data: 'path' });
});
if (view.modelBrowsePage) {
view.modelBrowsePage.modelSourceType = ModelSourceType.Azure;
}
await view.refresh();
should.notEqual(view.azureModelsComponent?.data, undefined);
if (view.modelBrowsePage) {
view.modelBrowsePage.modelSourceType = ModelSourceType.RegisteredModels;
}
await view.refresh();
testContext.onClick.fire(undefined);
should.equal(view.modelSourcePage?.data, ModelSourceType.RegisteredModels);
should.notEqual(view.localModelsComponent?.data, undefined);
should.notEqual(view.modelBrowsePage?.registeredModelsComponent?.data, undefined);
if (view.modelBrowsePage?.registeredModelsComponent?.data) {
should.equal(view.modelBrowsePage.registeredModelsComponent.data.length, 1);
}
should.notEqual(await view.getModelFileName(), undefined);
await view.columnsSelectionPage?.onEnter();
should.notEqual(view.columnsSelectionPage?.data, undefined);
should.equal(view.columnsSelectionPage?.data?.inputColumns?.length, modelParameters.inputs.length, modelParameters.inputs[0].name);
should.equal(view.columnsSelectionPage?.data?.outputColumns?.length, modelParameters.outputs.length);
});
});

View File

@@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------------------------
* 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 should from 'should';
import 'mocha';
import { createContext } from './utils';
import { ListModelsEventName, ListAccountsEventName, ListSubscriptionsEventName, ListGroupsEventName, ListWorkspacesEventName, ListAzureModelsEventName, ModelSourceType, ListDatabaseNamesEventName, ListTableNamesEventName } from '../../../views/models/modelViewBase';
import { ImportedModel } from '../../../modelManagement/interfaces';
import { azureResource } from '../../../typings/azure-resource';
import { Workspace } from '@azure/arm-machinelearningservices/esm/models';
import { ViewBase } from '../../../views/viewBase';
import { WorkspaceModel } from '../../../modelManagement/interfaces';
import { ImportModelWizard } from '../../../views/models/manageModels/importModelWizard';
describe('Register Model Wizard', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new ImportModelWizard(testContext.apiWrapper.object, '');
view.importTable = {
databaseName: 'db',
tableName: 'table',
schema: 'dbo'
};
await view.open();
should.notEqual(view.wizardView, undefined);
should.notEqual(view.modelSourcePage, undefined);
});
it('Should load data successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new ImportModelWizard(testContext.apiWrapper.object, '');
view.importTable = {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
};
await view.open();
let accounts: azdata.Account[] = [
{
key: {
accountId: '1',
providerId: ''
},
displayInfo: {
displayName: 'account',
userId: '',
accountType: '',
contextualDisplayName: ''
},
isStale: false,
properties: []
}
];
let subscriptions: azureResource.AzureResourceSubscription[] = [
{
name: 'subscription',
id: '2'
}
];
let groups: azureResource.AzureResourceResourceGroup[] = [
{
name: 'group',
id: '3'
}
];
let workspaces: Workspace[] = [
{
name: 'workspace',
id: '4'
}
];
let models: WorkspaceModel[] = [
{
id: '5',
name: 'model'
}
];
let localModels: ImportedModel[] = [
{
id: 1,
modelName: 'model',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
}
];
view.on(ListModelsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListModelsEventName), { data: localModels });
});
view.on(ListDatabaseNamesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListDatabaseNamesEventName), { data: [
'db', 'db1'
] });
});
view.on(ListTableNamesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListTableNamesEventName), { data: [
'tb', 'tb1'
] });
});
view.on(ListAccountsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListAccountsEventName), { data: accounts });
});
view.on(ListSubscriptionsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListSubscriptionsEventName), { data: subscriptions });
});
view.on(ListGroupsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListGroupsEventName), { data: groups });
});
view.on(ListWorkspacesEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListWorkspacesEventName), { data: workspaces });
});
view.on(ListAzureModelsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListAzureModelsEventName), { data: models });
});
if (view.modelBrowsePage) {
view.modelBrowsePage.modelSourceType = ModelSourceType.Azure;
}
await view.refresh();
should.notEqual(view.azureModelsComponent?.data ,undefined);
should.notEqual(view.localModelsComponent?.data, undefined);
});
});

View File

@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import 'mocha';
import { createContext } from './utils';
import { ManageModelsDialog } from '../../../views/models/manageModels/manageModelsDialog';
import { ListModelsEventName } from '../../../views/models/modelViewBase';
import { ImportedModel } from '../../../modelManagement/interfaces';
import { ViewBase } from '../../../views/viewBase';
describe('Registered Models Dialog', () => {
it('Should create view components successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new ManageModelsDialog(testContext.apiWrapper.object, '');
view.open();
should.notEqual(view.dialogView, undefined);
should.notEqual(view.currentLanguagesTab, undefined);
});
it('Should load data successfully ', async function (): Promise<void> {
let testContext = createContext();
let view = new ManageModelsDialog(testContext.apiWrapper.object, '');
view.open();
let models: ImportedModel[] = [
{
id: 1,
modelName: 'model',
table: {
databaseName: 'db',
tableName: 'tb',
schema: 'dbo'
}
}
];
view.on(ListModelsEventName, () => {
view.sendCallbackRequest(ViewBase.getCallbackEventName(ListModelsEventName), { data: models });
});
await view.refresh();
});
});

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../../common/apiWrapper';
import { createViewContext } from '../utils';
import { ModelViewBase } from '../../../views/models/modelViewBase';
import { AzureModelRegistryService } from '../../../modelManagement/azureModelRegistryService';
import { DeployedModelService } from '../../../modelManagement/deployedModelService';
import { PredictService } from '../../../prediction/predictService';
export interface TestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
view: azdata.ModelView;
onClick: vscode.EventEmitter<any>;
azureModelService: TypeMoq.IMock<AzureModelRegistryService>;
deployModelService: TypeMoq.IMock<DeployedModelService>;
predictService: TypeMoq.IMock<PredictService>;
}
export class ParentDialog extends ModelViewBase {
public refresh(): Promise<void> {
return Promise.resolve();
}
public reset(): Promise<void> {
return Promise.resolve();
}
constructor(
apiWrapper: ApiWrapper) {
super(apiWrapper, '');
}
}
export function createContext(): TestContext {
let viewTestContext = createViewContext();
return {
apiWrapper: viewTestContext.apiWrapper,
view: viewTestContext.view,
onClick: viewTestContext.onClick,
azureModelService: TypeMoq.Mock.ofType(AzureModelRegistryService),
deployModelService: TypeMoq.Mock.ofType(DeployedModelService),
predictService: TypeMoq.Mock.ofType(PredictService)
};
}

View File

@@ -0,0 +1,333 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../common/apiWrapper';
export interface ViewTestContext {
apiWrapper: TypeMoq.IMock<ApiWrapper>;
view: azdata.ModelView;
onClick: vscode.EventEmitter<any>;
}
export function createViewContext(): ViewTestContext {
let onClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let apiWrapper = TypeMoq.Mock.ofType(ApiWrapper);
let componentBase: azdata.Component = {
id: '',
updateProperties: () => Promise.resolve(),
updateProperty: () => Promise.resolve(),
updateCssStyles: undefined!,
onValidityChanged: undefined!,
valid: true,
validate: undefined!,
focus: undefined!
};
let button: azdata.ButtonComponent = Object.assign({}, componentBase, {
onDidClick: onClick.event
});
let link: azdata.HyperlinkComponent = Object.assign({}, componentBase, {
onDidClick: onClick.event,
label: '',
url: ''
});
let radioButton: azdata.RadioButtonComponent = Object.assign({}, componentBase, {
checked: true,
onDidClick: onClick.event
});
let checkbox: azdata.CheckBoxComponent = Object.assign({}, componentBase, {
checked: true,
onChanged: onClick.event
});
let container = {
clearItems: () => { },
addItems: () => { },
addItem: () => { },
removeItem: () => true,
insertItem: () => { },
items: [],
setLayout: () => { }
};
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
});
let flex: azdata.FlexContainer = Object.assign({}, componentBase, container, {
});
let div: azdata.DivContainer = Object.assign({}, componentBase, container, {
onDidClick: onClick.event
});
let buttonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = {
component: () => button,
withProperties: () => buttonBuilder,
withValidation: () => buttonBuilder
};
let hyperLinkBuilder: azdata.ComponentBuilder<azdata.HyperlinkComponent> = {
component: () => link,
withProperties: () => hyperLinkBuilder,
withValidation: () => hyperLinkBuilder
};
let radioButtonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = {
component: () => radioButton,
withProperties: () => radioButtonBuilder,
withValidation: () => radioButtonBuilder
};
let checkBoxBuilder: azdata.ComponentBuilder<azdata.CheckBoxComponent> = {
component: () => checkbox,
withProperties: () => checkBoxBuilder,
withValidation: () => checkBoxBuilder
};
let inputBox: () => azdata.InputBoxComponent = () => Object.assign({}, componentBase, {
onTextChanged: onClick.event!,
onEnterKeyPressed: undefined!,
value: ''
});
let image: () => azdata.ImageComponent = () => Object.assign({}, componentBase, {
});
let dropdown: () => azdata.DropDownComponent = () => Object.assign({}, componentBase, {
onValueChanged: onClick.event,
value: {
name: '',
displayName: ''
},
values: []
});
let declarativeTable: () => azdata.DeclarativeTableComponent = () => Object.assign({}, componentBase, {
onDataChanged: undefined!,
data: [],
columns: []
});
let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, {
loading: false,
component: undefined!
});
let card: () => azdata.CardComponent = () => Object.assign({}, componentBase, {
label: '',
onDidActionClick: new vscode.EventEmitter<azdata.ActionDescriptor>().event,
onCardSelectedChanged: onClick.event
});
let declarativeTableBuilder: azdata.ComponentBuilder<azdata.DeclarativeTableComponent> = {
component: () => declarativeTable(),
withProperties: () => declarativeTableBuilder,
withValidation: () => declarativeTableBuilder
};
let loadingBuilder: azdata.LoadingComponentBuilder = {
component: () => loadingComponent(),
withProperties: () => loadingBuilder,
withValidation: () => loadingBuilder,
withItem: () => loadingBuilder
};
let formBuilder: azdata.FormBuilder = Object.assign({}, {
component: () => form,
addFormItem: () => { },
insertFormItem: () => { },
removeFormItem: () => true,
addFormItems: () => { },
withFormItems: () => formBuilder,
withProperties: () => formBuilder,
withValidation: () => formBuilder,
withItems: () => formBuilder,
withLayout: () => formBuilder
});
let flexBuilder: azdata.FlexBuilder = Object.assign({}, {
component: () => flex,
withProperties: () => flexBuilder,
withValidation: () => flexBuilder,
withItems: () => flexBuilder,
withLayout: () => flexBuilder
});
let divBuilder: azdata.DivBuilder = Object.assign({}, {
component: () => div,
withProperties: () => divBuilder,
withValidation: () => divBuilder,
withItems: () => divBuilder,
withLayout: () => divBuilder
});
let inputBoxBuilder: azdata.ComponentBuilder<azdata.InputBoxComponent> = {
component: () => {
let r = inputBox();
return r;
},
withProperties: () => inputBoxBuilder,
withValidation: () => inputBoxBuilder
};
let cardBuilder: azdata.ComponentBuilder<azdata.CardComponent> = {
component: () => {
let r = card();
return r;
},
withProperties: () => cardBuilder,
withValidation: () => cardBuilder
};
let imageBuilder: azdata.ComponentBuilder<azdata.ImageComponent> = {
component: () => {
let r = image();
return r;
},
withProperties: () => imageBuilder,
withValidation: () => imageBuilder
};
let dropdownBuilder: azdata.ComponentBuilder<azdata.DropDownComponent> = {
component: () => {
let r = dropdown();
return r;
},
withProperties: () => dropdownBuilder,
withValidation: () => dropdownBuilder
};
let view: azdata.ModelView = {
onClosed: undefined!,
connection: undefined!,
serverInfo: undefined!,
valid: true,
onValidityChanged: undefined!,
validate: undefined!,
initializeModel: () => { return Promise.resolve(); },
modelBuilder: {
radioCardGroup: undefined!,
navContainer: undefined!,
divContainer: () => divBuilder,
flexContainer: () => flexBuilder,
splitViewContainer: undefined!,
dom: undefined!,
card: () => cardBuilder,
inputBox: () => inputBoxBuilder,
checkBox: () => checkBoxBuilder!,
radioButton: () => radioButtonBuilder,
webView: undefined!,
editor: undefined!,
diffeditor: undefined!,
text: () => inputBoxBuilder,
image: () => imageBuilder,
button: () => buttonBuilder,
dropDown: () => dropdownBuilder,
tree: undefined!,
listBox: undefined!,
table: undefined!,
declarativeTable: () => declarativeTableBuilder,
dashboardWidget: undefined!,
dashboardWebview: undefined!,
formContainer: () => formBuilder,
groupContainer: undefined!,
toolbarContainer: undefined!,
loadingComponent: () => loadingBuilder,
fileBrowserTree: undefined!,
hyperlink: () => hyperLinkBuilder,
tabbedPanel: undefined!,
separator: undefined!,
propertiesContainer: undefined!
}
};
let tab: azdata.window.DialogTab = {
title: '',
content: '',
registerContent: async (handler) => {
try {
await handler(view);
} catch (err) {
throw err;
}
},
onValidityChanged: undefined!,
valid: true,
modelView: undefined!
};
let dialogButton: azdata.window.Button = {
label: '',
enabled: true,
hidden: false,
onClick: onClick.event,
};
let dialogMessage: azdata.window.DialogMessage = {
text: '',
};
let dialog: azdata.window.Dialog = {
title: '',
isWide: false,
content: [],
okButton: dialogButton,
cancelButton: dialogButton,
customButtons: [],
message: dialogMessage,
registerCloseValidator: () => { },
registerOperation: () => { },
onValidityChanged: new vscode.EventEmitter<boolean>().event,
registerContent: () => { },
modelView: undefined!,
valid: true
};
let wizard: azdata.window.Wizard = {
title: '',
pages: [],
currentPage: 0,
doneButton: dialogButton,
cancelButton: dialogButton,
generateScriptButton: dialogButton,
nextButton: dialogButton,
backButton: dialogButton,
customButtons: [],
displayPageTitles: true,
onPageChanged: onClick.event,
addPage: () => { return Promise.resolve(); },
removePage: () => { return Promise.resolve(); },
setCurrentPage: () => { return Promise.resolve(); },
open: () => { return Promise.resolve(); },
close: () => { return Promise.resolve(); },
registerNavigationValidator: () => { },
message: dialogMessage,
registerOperation: () => { }
};
let wizardPage: azdata.window.WizardPage = {
title: '',
content: '',
customButtons: [],
enabled: true,
description: '',
onValidityChanged: onClick.event,
registerContent: async (handler) => {
try {
await handler(view);
} catch (err) {
throw err;
}
},
modelView: undefined!,
valid: true
};
apiWrapper.setup(x => x.createButton(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => dialogButton);
apiWrapper.setup(x => x.createTab(TypeMoq.It.isAny())).returns(() => tab);
apiWrapper.setup(x => x.createWizard(TypeMoq.It.isAny())).returns(() => wizard);
apiWrapper.setup(x => x.createWizardPage(TypeMoq.It.isAny())).returns(() => wizardPage);
apiWrapper.setup(x => x.createModelViewDialog(TypeMoq.It.isAny())).returns(() => dialog);
apiWrapper.setup(x => x.openDialog(TypeMoq.It.isAny())).returns(() => { });
apiWrapper.setup(x => x.registerWidget(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(async (id, handler) => {
if (id) {
return await handler(view);
} else {
Promise.reject();
}
});
return {
apiWrapper: apiWrapper,
view: view,
onClick: onClick,
};
}