Sql projects: Tests for Create project from database for vscode extension (#21257)

* Test changes

* Tests for CreateProjectFromDatabaseQuickpick

* Address comments

* Update pwd to placeholder
This commit is contained in:
Sakshi Sharma
2022-11-21 11:04:46 -08:00
committed by GitHub
parent ff56398fa9
commit e79ec552e6
4 changed files with 406 additions and 36 deletions

View File

@@ -0,0 +1,252 @@
/*---------------------------------------------------------------------------------------------
* 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 * as mssql from 'mssql';
import * as path from 'path';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import * as constants from '../../common/constants';
import * as utils from '../../common/utils'
import * as quickpickHelper from '../../dialogs/quickpickHelper'
import * as createProjectFromDatabaseQuickpick from '../../dialogs/createProjectFromDatabaseQuickpick';
import { createTestUtils, mockConnectionInfo, TestUtils } from './testUtils';
import { promises as fs } from 'fs';
import { ImportDataModel } from '../../models/api/import';
import { createTestFile, deleteGeneratedTestFolder } from '../testUtils';
let testUtils: TestUtils;
const projectFilePath = 'test';
const dbList: string[] = constants.systemDbs.concat(['OtherDatabase', 'Database', 'OtherDatabase2']);
describe('Create Project From Database Quickpick', () => {
beforeEach(function (): void {
testUtils = createTestUtils();
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object); //set vscode mssql extension api
});
afterEach(function (): void {
sinon.restore();
});
it('Should prompt for connection and exit when connection is not selected', async function (): Promise<void> {
//promptForConnection spy to verify test
const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick();
//verify that prompt for connection was called
should(promptForConnectionSpy.calledOnce).be.true('promptForConnection should have been called');
//verify quickpick exited with undefined, since promptForConnection was set to cancel (resolves to undefined)
should.equal(model, undefined);
});
it('Should not prompt for connection when connectionInfo is provided and exit when db is not selected', async function (): Promise<void> {
//promptForConnection spy to verify test
const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined);
//user chooses connection
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
// user chooses to cancel when prompted for database
sinon.stub(vscode.window, 'showQuickPick').resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify connection prompt wasn't presented, since connectionInfo was passed during the call
should(promptForConnectionSpy.notCalled).be.true('promptForConnection should not be called when connectionInfo is provided');
//verify quickpick exited with undefined, since database wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when project name is not selected', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
// user chooses to provide empty project name when prompted
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').resolves('');
// user chooses to cancel when prompted to enter project name
inputBoxStub.onSecondCall().resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showInputBox exited with undefined, since project name wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when project location is not selected', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
//user chooses to exit
quickPickStub.onSecondCall().resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when project location is not selected (test repeatedness for project location)', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
// user chooses to browse for folder
quickPickStub.onSecondCall().resolves((constants.browseEllipsisWithIcon) as any);
// user doesn't choose any folder when prompted and exits the showOpenDialog
let openDialogStub = sinon.stub(vscode.window, 'showOpenDialog').withArgs(sinon.match.any).resolves(undefined);
// user chooses to browse for folder
quickPickStub.onThirdCall().resolves((constants.browseEllipsisWithIcon) as any);
// user doesn't choose any folder when prompted and exits the showOpenDialog
openDialogStub.onSecondCall().resolves(undefined);
// user chooses to browse for folder
quickPickStub.onCall(3).resolves((constants.browseEllipsisWithIcon) as any);
// user doesn't choose any folder when prompted and exits the showOpenDialog
openDialogStub.onSecondCall().resolves(undefined);
//user chooses to exit
quickPickStub.onCall(4).resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when folder structure is not selected and folder is selected through browsing (test repeatedness for project location)', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
// user chooses to browse for folder
quickPickStub.onSecondCall().resolves((constants.browseEllipsisWithIcon) as any);
// user doesn't choose any folder when prompted and exits the showOpenDialog
let openDialogStub = sinon.stub(vscode.window, 'showOpenDialog').withArgs(sinon.match.any).resolves(undefined);
// user chooses to browse for folder again
quickPickStub.onThirdCall().resolves((constants.browseEllipsisWithIcon) as any);
// user chooses folder- stub out folder to be chosen (showOpenDialog)
openDialogStub.onSecondCall().resolves([vscode.Uri.file(projectFilePath)]);
//user chooses to exit when prompted for folder structure
quickPickStub.onCall(3).resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when folder structure is not selected and existing folder/file location is selected', async function (): Promise<void> {
//create folder and project file
const projectFileName = 'TestProject';
const testProjectFilePath = 'TestProjectPath'
await fs.rm(testProjectFilePath, { force: true, recursive: true }); //clean up if it already exists
await createTestFile('', projectFileName, testProjectFilePath);
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves(projectFileName);
// user chooses a folder/file combination that already exists
quickPickStub.onSecondCall().resolves(testProjectFilePath as any);
//user chooses another folder when prompted again
quickPickStub.onThirdCall().resolves(path.join(projectFilePath, 'test') as any);
//user chooses to exit when prompted for folder structure
quickPickStub.onCall(3).resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
await deleteGeneratedTestFolder();
//verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when include permissions is not selected', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
// user chooses a folder
quickPickStub.onSecondCall().resolves(projectFilePath as any);
//user chooses Object type when prompted for folder structure
quickPickStub.onThirdCall().resolves(constants.objectType as any);
//user chooses to exit when prompted for include permissions
quickPickStub.onCall(3).resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showQuickPick exited with undefined, since include permissions wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should exit when sdk style project is not selected', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
// user chooses a folder
quickPickStub.onSecondCall().resolves(projectFilePath as any);
//user chooses Object type when prompted for folder structure
quickPickStub.onThirdCall().resolves(constants.objectType as any);
//user chooses No when prompted for include permissions
quickPickStub.onCall(3).resolves(constants.noStringDefault as any);
//user chooses to exit when prompted for sdk style project
sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(undefined);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
//verify showQuickPick exited with undefined, since sdk style project wasn't selected (resolved to undefined)
should.equal(model, undefined);
});
it('Should create correct import data model when all the information is provided', async function (): Promise<void> {
//user chooses connection and database
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'connect').resolves('testConnectionURI');
sinon.stub(testUtils.vscodeMssqlIExtension.object, 'listDatabases').withArgs(sinon.match.any).resolves(dbList);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('Database' as any);
//user chooses project name
sinon.stub(vscode.window, 'showInputBox').resolves('TestProject');
// user chooses a folder
quickPickStub.onSecondCall().resolves(projectFilePath as any);
//user chooses Object type when prompted for folder structure
quickPickStub.onThirdCall().resolves(constants.objectType as any);
//user chooses No when prompted for include permissions
quickPickStub.onCall(3).resolves(constants.noStringDefault as any);
//user chooses sdk style project to be true
sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(true);
const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo);
const expectedImportDataModel: ImportDataModel = {
connectionUri: 'testConnectionURI',
database: 'Database',
projName: 'TestProject',
filePath: projectFilePath,
version: '1.0.0.0',
extractTarget: mssql.ExtractTarget.objectType,
sdkStyle: true,
includePermissions: false
};
//verify the model is correctly generated
should(model!).deepEqual(expectedImportDataModel);
});
});

View File

@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as TypeMoq from 'typemoq';
import * as vscodeMssql from 'vscode-mssql';
import { RequestType } from 'vscode-languageclient';
export interface TestUtils {
vscodeMssqlIExtension: TypeMoq.IMock<vscodeMssql.IExtension>;
}
export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension {
sqlToolsServicePath: string = '';
dacFx: vscodeMssql.IDacFxService;
schemaCompare: vscodeMssql.ISchemaCompareService;
azureAccountService: vscodeMssql.IAzureAccountService;
azureResourceService: vscodeMssql.IAzureResourceService;
constructor() {
this.dacFx = TypeMoq.Mock.ofType<vscodeMssql.IDacFxService>().object;
this.schemaCompare = TypeMoq.Mock.ofType<vscodeMssql.ISchemaCompareService>().object;
this.azureAccountService = TypeMoq.Mock.ofType<vscodeMssql.IAzureAccountService>().object;
this.azureResourceService = TypeMoq.Mock.ofType<vscodeMssql.IAzureResourceService>().object;
}
promptForFirewallRule(_: string, __: vscodeMssql.IConnectionInfo): Promise<boolean> {
throw new Error('Method not implemented.');
}
sendRequest<P, R, E>(_: RequestType<P, R, E>, __?: P): Promise<R> {
throw new Error('Method not implemented.');
}
promptForConnection(_?: boolean): Promise<vscodeMssql.IConnectionInfo | undefined> {
throw new Error('Method not implemented.');
}
connect(_: vscodeMssql.IConnectionInfo, __?: boolean): Promise<string> {
throw new Error('Method not implemented.');
}
listDatabases(_: string): Promise<string[]> {
throw new Error('Method not implemented.');
}
getDatabaseNameFromTreeNode(_: vscodeMssql.ITreeNodeInfo): string {
throw new Error('Method not implemented.');
}
getConnectionString(_: string | vscodeMssql.ConnectionDetails, ___?: boolean, _____?: boolean): Promise<string> {
throw new Error('Method not implemented.');
}
createConnectionDetails(_: vscodeMssql.IConnectionInfo): vscodeMssql.ConnectionDetails {
throw new Error('Method not implemented.');
}
getServerInfo(_: vscodeMssql.IConnectionInfo): vscodeMssql.ServerInfo {
throw new Error('Method not implemented.');
}
}
export function createTestUtils(): TestUtils {
return {
vscodeMssqlIExtension: TypeMoq.Mock.ofType(MockVscodeMssqlIExtension)
};
}
// Mock test data
export const mockConnectionInfo: vscodeMssql.IConnectionInfo = {
server: 'Server',
database: 'Database',
user: 'User',
password: 'Placeholder',
email: 'test-email',
accountId: 'test-account-id',
tenantId: 'test-tenant-id',
port: 1234,
authenticationType: vscodeMssql.AuthenticationType.SqlLogin,
azureAccountToken: '',
expiresOn: 0,
encrypt: false,
trustServerCertificate: false,
persistSecurityInfo: false,
connectTimeout: 15,
connectRetryCount: 0,
connectRetryInterval: 0,
applicationName: 'vscode-mssql',
workstationId: 'test',
applicationIntent: '',
currentLanguage: '',
pooling: true,
maxPoolSize: 15,
minPoolSize: 0,
loadBalanceTimeout: 0,
replication: false,
attachDbFilename: '',
failoverPartner: '',
multiSubnetFailover: false,
multipleActiveResultSets: false,
packetSize: 8192,
typeSystemVersion: 'Latest',
connectionString: ''
};