Test/dacpac tests (#11428)

* Adding test for select operation page and dacpac wizard

* skipping one test for investigation

* Changes as per PR comments
This commit is contained in:
Udeesha Gautam
2020-07-20 14:23:27 -07:00
committed by GitHub
parent ca900464e5
commit 921beebd79
9 changed files with 406 additions and 45 deletions

View File

@@ -8,7 +8,7 @@ import * as should from 'should';
import { DataTierApplicationWizard, PageName } from '../wizard/dataTierApplicationWizard';
import { DacFxDataModel } from '../wizard/api/models';
import { TestContext, createContext } from './testContext';
import { TestDeployConfigPage, TestExtractConfigPage } from './DacFxTestConfigPages';
import { TestDeployConfigPage, TestExtractConfigPage } from './testDacFxConfigPages';
let wizard: DataTierApplicationWizard;
let testContext: TestContext;

View File

@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
import * as should from 'should';
import * as loc from '../localizedConstants';
import { DataTierApplicationWizard, PageName } from '../wizard/dataTierApplicationWizard';
import { DacFxDataModel } from '../wizard/api/models';
import { TestContext, createContext } from './testContext';
import { TestSelectOperationPage } from './testDacFxConfigPages';
let wizard: DataTierApplicationWizard;
let testContext: TestContext;
describe('Dacpac Select OperationPage Tests', function (): void {
beforeEach(async function (): Promise<void> {
wizard = new DataTierApplicationWizard();
wizard.model = <DacFxDataModel>{};
wizard.model.server = undefined;
});
it('Select Operations Page should start correctly', async () => {
testContext = createContext();
wizard.setPages();
const opPage: TestSelectOperationPage = new TestSelectOperationPage(wizard, wizard.pages.get(PageName.selectOperation).wizardPage, wizard.model, testContext.viewContext.view);
const onPageStart = await opPage.start();
const onPageEnter = await opPage.onPageEnter();
should(onPageStart).equal(true);
should(onPageEnter).equal(true);
should(opPage.WizardState.wizard.pages.length).equal(4);
should(opPage.WizardState.wizard.pages[1].title).equal(loc.deployConfigPageName);
should(opPage.WizardState.wizard.pages[2].title).equal(loc.deployPlanPageName);
should(opPage.WizardState.wizard.pages[3].title).equal(loc.summaryPageName);
});
it('Select Operations Page clicks should work correctly', async () => {
testContext = createContext();
wizard.setPages();
const opPage: TestSelectOperationPage = new TestSelectOperationPage(wizard, wizard.pages.get(PageName.selectOperation).wizardPage, wizard.model, testContext.viewContext.view);
await opPage.start();
testContext.viewContext.extractOnClick.fire(undefined);
should(opPage.WizardState.wizard.doneButton.label).equal(loc.extract);
testContext.viewContext.exportOnClick.fire(undefined);
should(opPage.WizardState.wizard.doneButton.label).equal(loc.exportText);
testContext.viewContext.importOnClick.fire(undefined);
should(opPage.WizardState.wizard.doneButton.label).equal(loc.importText);
testContext.viewContext.deployOnClick.fire(undefined);
should(opPage.WizardState.wizard.doneButton.label).equal(loc.deploy);
});
});

View File

@@ -47,6 +47,10 @@ export interface ViewTestContext {
onValueChanged: vscode.EventEmitter<any>;
newDatabaseRadioOnClick: vscode.EventEmitter<any>;
updateExistingRadioOnClick: vscode.EventEmitter<any>;
deployOnClick: vscode.EventEmitter<any>,
extractOnClick: vscode.EventEmitter<any>,
exportOnClick: vscode.EventEmitter<any>,
importOnClick: vscode.EventEmitter<any>,
}
export function createViewContext(): ViewTestContext {
@@ -55,6 +59,10 @@ export function createViewContext(): ViewTestContext {
let onValueChanged: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let newDatabaseRadioOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let updateExistingRadioOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let deployOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let extractOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let exportOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let importOnClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
let componentBase: azdata.Component = {
id: '',
@@ -111,13 +119,31 @@ export function createViewContext(): ViewTestContext {
let builder: azdata.ComponentBuilder<azdata.RadioButtonComponent> = {
component: () => button,
withProperties: (properties) => {
if ((properties as any).label === loc.newDatabase) {
button.label = loc.newDatabase;
button.onDidClick = newDatabaseRadioOnClick.event;
}
else if ((properties as any).label === loc.upgradeExistingDatabase) {
button.label = loc.upgradeExistingDatabase;
button.onDidClick = updateExistingRadioOnClick.event;
switch ((properties as any).label) {
case loc.newDatabase:
button.label = loc.newDatabase;
button.onDidClick = newDatabaseRadioOnClick.event;
break;
case loc.upgradeExistingDatabase:
button.label = loc.upgradeExistingDatabase;
button.onDidClick = updateExistingRadioOnClick.event;
break;
case loc.deployDescription:
button.label = loc.deployDescription;
button.onDidClick = deployOnClick.event;
break;
case loc.exportDescription:
button.label = loc.exportDescription;
button.onDidClick = exportOnClick.event;
break;
case loc.extractDescription:
button.label = loc.extractDescription;
button.onDidClick = extractOnClick.event;
break;
case loc.importDescription:
button.label = loc.importDescription;
button.onDidClick = importOnClick.event;
break;
}
return builder;
},
@@ -265,5 +291,9 @@ export function createViewContext(): ViewTestContext {
onValueChanged: onValueChanged,
newDatabaseRadioOnClick: newDatabaseRadioOnClick,
updateExistingRadioOnClick: updateExistingRadioOnClick,
deployOnClick: deployOnClick,
extractOnClick: extractOnClick,
exportOnClick: exportOnClick,
importOnClick: importOnClick,
};
}

View File

@@ -8,6 +8,7 @@ import { DacFxDataModel } from '../wizard/api/models';
import { DeployConfigPage } from '../wizard/pages/deployConfigPage';
import { ExtractConfigPage } from '../wizard/pages/extractConfigPage';
import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard';
import { SelectOperationPage } from '../wizard/pages/selectOperationpage';
export class TestDeployConfigPage extends DeployConfigPage {
@@ -37,5 +38,15 @@ export class TestExtractConfigPage extends ExtractConfigPage {
get Model(): DacFxDataModel {
return this.model;
}
}
export class TestSelectOperationPage extends SelectOperationPage {
constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
super(instance, wizardPage, model, view);
}
get WizardState(): DataTierApplicationWizard {
return this.instance;
}
}

View File

@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as mssql from '../../../mssql/src/mssql';
export const deployOperationId = 'deploy dacpac';
export const extractOperationId = 'extract dacpac';
export const exportOperationId = 'export bacpac';
export const importOperationId = 'import bacpac';
export const generateScript = 'genenrate script';
export const generateDeployPlan = 'genenrate deploy plan';
export class DacFxTestService implements mssql.IDacFxService {
dacfxResult: mssql.DacFxResult = {
success: true,
operationId: 'test',
errorMessage: ''
};
constructor() {
}
exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = exportOperationId;
return Promise.resolve(this.dacfxResult);
}
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = importOperationId;
return Promise.resolve(this.dacfxResult);
}
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = extractOperationId;
return Promise.resolve(this.dacfxResult);
}
importDatabaseProject(databaseName: string, targetFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, extractTarget: mssql.ExtractTarget, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = importOperationId;
return Promise.resolve(this.dacfxResult);
}
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record<string, string>): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = deployOperationId;
return Promise.resolve(this.dacfxResult);
}
generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record<string, string>): Thenable<mssql.DacFxResult> {
this.dacfxResult.operationId = generateScript;
return Promise.resolve(this.dacfxResult);
}
generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.GenerateDeployPlanResult> {
this.dacfxResult.operationId = generateDeployPlan;
const deployPlan: mssql.GenerateDeployPlanResult = {
operationId: generateDeployPlan,
success: true,
errorMessage: '',
report: generateDeployPlan
};
return Promise.resolve(deployPlan);
}
}

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
import * as azdata from 'azdata';
import * as should from 'should';
import * as sinon from 'sinon';
import { DataTierApplicationWizard, Operation } from '../wizard/dataTierApplicationWizard';
import { DacFxDataModel } from '../wizard/api/models';
import { DacFxTestService, deployOperationId, extractOperationId, importOperationId, exportOperationId, generateDeployPlan } from './testDacFxService';
let wizard: DataTierApplicationWizard;
let connectionProfileMock: azdata.connection.ConnectionProfile = new azdata.connection.ConnectionProfile();
connectionProfileMock.connectionId = 'TEST ID';
let dacfxServiceMock: DacFxTestService = new DacFxTestService();
let connectionMock: azdata.connection.Connection = {
connectionId: 'TEST ID',
providerName: 'MsSql',
options: null,
};
describe('Dacfx wizard with connection', function (): void {
beforeEach(async function (): Promise<void> {
sinon.stub(azdata.connection, 'getConnections').returns(Promise.resolve([]));
sinon.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('my test uri'));
wizard = new DataTierApplicationWizard(dacfxServiceMock);
wizard.model = <DacFxDataModel>{};
});
afterEach(function (): void {
sinon.restore();
});
it('Should return false if connection is not present', async () => {
sinon.stub(azdata.connection, 'getCurrentConnection').returns(Promise.resolve(undefined));
sinon.stub(azdata.connection, 'openConnectionDialog').returns(Promise.resolve(undefined));
let profile = { connectionProfile: connectionProfileMock };
const result = await wizard.start(profile);
should(result).equal(false);
});
// [udgautam] Skipping this for now since it gives intermittent Error: write EPIPE error. Investigating...
it.skip('Should return true if connection is present', async () => {
sinon.stub(azdata.connection, 'getCurrentConnection').returns(Promise.resolve(connectionProfileMock));
sinon.stub(azdata.connection, 'openConnectionDialog').returns(Promise.resolve(connectionMock));
let profile = { connectionProfile: connectionProfileMock };
const result = await wizard.start(profile);
should(result).equal(true);
});
it('Should call all service methods correctly', async () => {
wizard.model.server = connectionProfileMock;
await validateServiceCalls(wizard, Operation.deploy, deployOperationId);
await validateServiceCalls(wizard, Operation.extract, extractOperationId);
await validateServiceCalls(wizard, Operation.import, importOperationId);
await validateServiceCalls(wizard, Operation.export, exportOperationId);
});
it('Should call deploy plan generator correctly', async () => {
wizard.model.server = connectionProfileMock;
const report = await wizard.generateDeployPlan();
should(report).equal(generateDeployPlan);
});
async function validateServiceCalls(wizard: DataTierApplicationWizard, selectedOperation: Operation, expectedOperationId: string): Promise<void> {
wizard.selectedOperation = selectedOperation;
let result = await wizard.executeOperation();
should(result.success).equal(true);
should(result.operationId).equal(expectedOperationId);
}
});

View File

@@ -79,15 +79,17 @@ export enum PageName {
export class DataTierApplicationWizard {
public wizard: azdata.window.Wizard;
private connection: azdata.connection.ConnectionProfile;
private dacfxService: mssql.IDacFxService;
public model: DacFxDataModel;
public pages: Map<string, Page> = new Map<string, Page>();
public selectedOperation: Operation;
constructor() {
constructor(dacfxInputService?: mssql.IDacFxService) {
this.wizard = azdata.window.createWizard(loc.wizardTitle);
this.dacfxService = dacfxInputService;
}
public async start(p: any, ...args: any[]) {
public async start(p: any, ...args: any[]): Promise<boolean> {
this.model = <DacFxDataModel>{};
let profile = p ? <azdata.IConnectionProfile>p.connectionProfile : undefined;
@@ -109,7 +111,7 @@ export class DataTierApplicationWizard {
}
// don't open the wizard if connection dialog is cancelled
if (!this.connection) {
return;
return false;
}
}
@@ -121,6 +123,7 @@ export class DataTierApplicationWizard {
this.wizard.doneButton.onClick(async () => await this.executeOperation());
this.wizard.open();
return true;
}
public setPages(): void {
@@ -241,61 +244,56 @@ export class DataTierApplicationWizard {
}
}
private async executeOperation() {
public async executeOperation(): Promise<mssql.DacFxResult> {
switch (this.selectedOperation) {
case Operation.deploy: {
await this.deploy();
break;
return await this.deploy();
}
case Operation.extract: {
await this.extract();
break;
return await this.extract();
}
case Operation.import: {
await this.import();
break;
return await this.import();
}
case Operation.export: {
await this.export();
break;
return await this.export();
}
case Operation.generateDeployScript: {
await this.generateDeployScript();
break;
return await this.generateDeployScript();
}
}
}
private async deploy(): Promise<void> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
public async deploy(): Promise<mssql.DacFxResult> {
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
await service.deployDacpac(this.model.filePath, this.model.database, this.model.upgradeExisting, ownerUri, azdata.TaskExecutionMode.execute);
return await service.deployDacpac(this.model.filePath, this.model.database, this.model.upgradeExisting, ownerUri, azdata.TaskExecutionMode.execute);
}
private async extract(): Promise<void> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
private async extract(): Promise<mssql.DacFxResult> {
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
await service.extractDacpac(this.model.database, this.model.filePath, this.model.database, this.model.version, ownerUri, azdata.TaskExecutionMode.execute);
return await service.extractDacpac(this.model.database, this.model.filePath, this.model.database, this.model.version, ownerUri, azdata.TaskExecutionMode.execute);
}
private async export(): Promise<void> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
private async export(): Promise<mssql.DacFxResult> {
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
await service.exportBacpac(this.model.database, this.model.filePath, ownerUri, azdata.TaskExecutionMode.execute);
return await service.exportBacpac(this.model.database, this.model.filePath, ownerUri, azdata.TaskExecutionMode.execute);
}
private async import(): Promise<void> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
private async import(): Promise<mssql.DacFxResult> {
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
await service.importBacpac(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute);
return await service.importBacpac(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute);
}
private async generateDeployScript(): Promise<void> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
private async generateDeployScript(): Promise<mssql.DacFxResult> {
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
this.wizard.message = {
text: loc.generatingScriptMessage,
@@ -303,7 +301,7 @@ export class DataTierApplicationWizard {
description: ''
};
await service.generateDeployScript(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.script);
return await service.generateDeployScript(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.script);
}
public getPage(idx: number): Page {
@@ -346,7 +344,7 @@ export class DataTierApplicationWizard {
}
public async generateDeployPlan(): Promise<string> {
const service = await DataTierApplicationWizard.getService(msSqlProvider);
const service = await this.getService(msSqlProvider);
const ownerUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
const result = await service.generateDeployPlan(this.model.filePath, this.model.database, ownerUri, azdata.TaskExecutionMode.execute);
@@ -358,8 +356,10 @@ export class DataTierApplicationWizard {
return result.report;
}
private static async getService(providerName: string): Promise<mssql.IDacFxService> {
const service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).dacFx;
return service;
private async getService(providerName: string): Promise<mssql.IDacFxService> {
if (!this.dacfxService) {
this.dacfxService = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).dacFx;
}
return this.dacfxService;
}
}