From b1a6929e6708031b79bd32708b81f3685ead67c3 Mon Sep 17 00:00:00 2001 From: Z Chen <13544267+zijchen@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:00:28 -0800 Subject: [PATCH] Fix target platform when creating project from database via quickpick (#26049) * Create project from quickpick * Fix test * Fix test --- .../src/controllers/projectController.ts | 5 +- .../createProjectFromDatabaseQuickpick.ts | 12 ++- ...createProjectFromDatabaseQuickpick.test.ts | 93 ++++++++++++------- 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 528b52ede9d3..9f3bec1036c7 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -1581,10 +1581,7 @@ export class ProjectsController { const databaseName = (await utils.getVscodeMssqlApi()).getDatabaseNameFromTreeNode(treeNodeContext); (profile as mssqlVscode.IConnectionInfo).database = databaseName; } - const model = await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo); - if (model) { - await this.createProjectFromDatabaseCallback(model, profile as mssqlVscode.IConnectionInfo, (profile as mssqlVscode.IConnectionInfo)?.server); - } + await createNewProjectFromDatabaseWithQuickpick(profile as mssqlVscode.IConnectionInfo, this.createProjectFromDatabaseCallback); return undefined; } diff --git a/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts b/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts index d49390bb7db9..d87246bfaa27 100644 --- a/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts +++ b/extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseQuickpick.ts @@ -17,8 +17,9 @@ import { getSDKStyleProjectInfo } from './quickpickHelper'; /** * Create flow for a New Project using only VS Code-native APIs such as QuickPick * @param connectionInfo Optional connection info to use instead of prompting the user for a connection + * @param createProjectFromDatabaseCallback Optional callback function to create the project from the user inputs */ -export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: IConnectionInfo): Promise { +export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: IConnectionInfo, createProjectFromDatabaseCallback?: (model: ImportDataModel, connectionInfo?: string | IConnectionInfo, serverName?: string) => Promise): Promise { const vscodeMssqlApi = await getVscodeMssqlApi(); // 1. Select connection @@ -162,7 +163,7 @@ export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: return; } - return { + const model = { connectionUri: connectionUri, database: selectedDatabase, projName: projectName, @@ -171,5 +172,10 @@ export async function createNewProjectFromDatabaseWithQuickpick(connectionInfo?: extractTarget: mapExtractTargetEnum(folderStructure), sdkStyle: sdkStyle, includePermissions: includePermissions - }; + } as ImportDataModel; + + // 8. Create the project using the callback + if (createProjectFromDatabaseCallback) { + await createProjectFromDatabaseCallback(model, connectionProfile, connectionProfile.server); + } } diff --git a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts index 23f0d9c2809e..61906bd90f8e 100644 --- a/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts +++ b/extensions/sql-database-projects/src/test/dialogs/createProjectFromDatabaseQuickpick.test.ts @@ -40,35 +40,44 @@ describe('Create Project From Database Quickpick', () => { //promptForConnection spy to verify test const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(); + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(undefined, createProjectFromDatabaseCallbackSpy); //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); + //verify create project callback was not called, since promptForConnection was set to cancel (resolves to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should not prompt for connection when connectionInfo is provided and exit when db is not selected', async function (): Promise { //promptForConnection spy to verify test const promptForConnectionSpy = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(sinon.match.any).resolves(undefined); + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); //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); + //verify create project callback was not called, since database wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project name is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -78,13 +87,16 @@ describe('Create Project From Database Quickpick', () => { // user chooses to cancel when prompted to enter project name inputBoxStub.onSecondCall().resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showInputBox exited with undefined, since project name wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project name wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project location is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -94,13 +106,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit quickPickStub.onSecondCall().resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project location wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when project location is not selected (test repeatedness for project location)', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -122,13 +137,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit quickPickStub.onCall(4).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since project location wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since project location wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when folder structure is not selected and folder is selected through browsing (test repeatedness for project location)', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -146,13 +164,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for folder structure quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since folder structure wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when folder structure is not selected and existing folder/file location is selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //create folder and project file const projectFileName = 'TestProject'; const testProjectFilePath = await generateTestFolderPath(this.test); @@ -172,15 +193,18 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for folder structure quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); await deleteGeneratedTestFolder(); - //verify showQuickPick exited with undefined, since folder structure wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since folder structure wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when include permissions is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -194,13 +218,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for include permissions quickPickStub.onCall(3).resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since include permissions wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since include permissions wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); it('Should exit when sdk style project is not selected', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -216,13 +243,16 @@ describe('Create Project From Database Quickpick', () => { //user chooses to exit when prompted for sdk style project sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(undefined); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); - //verify showQuickPick exited with undefined, since sdk style project wasn't selected (resolved to undefined) - should.equal(model, undefined); + //verify create project callback was not called, since sdk style project wasn't selected (resolved to undefined) + should(createProjectFromDatabaseCallbackSpy.notCalled).be.true('createProjectFromDatabaseCallback should not have been called'); }); - it('Should create correct import data model when all the information is provided', async function (): Promise { + it('Should create project when all the information is provided', async function (): Promise { + //createProjectFromDatabaseQuickpick spy to verify test + const createProjectFromDatabaseCallbackSpy = sinon.stub().resolves(); + //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); @@ -238,7 +268,7 @@ describe('Create Project From Database Quickpick', () => { //user chooses sdk style project to be true sinon.stub(quickpickHelper, 'getSDKStyleProjectInfo').resolves(true); - const model = await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo); + await createProjectFromDatabaseQuickpick.createNewProjectFromDatabaseWithQuickpick(mockConnectionInfo, createProjectFromDatabaseCallbackSpy); const expectedImportDataModel: ImportDataModel = { connectionUri: 'testConnectionURI', @@ -251,7 +281,8 @@ describe('Create Project From Database Quickpick', () => { includePermissions: false }; - //verify the model is correctly generated - should(model!).deepEqual(expectedImportDataModel); + //verify create project callback was called with the correct model + should(createProjectFromDatabaseCallbackSpy.calledOnce).be.true('createProjectFromDatabaseCallback should have been called'); + should(createProjectFromDatabaseCallbackSpy.calledWithMatch(expectedImportDataModel)).be.true('createProjectFromDatabaseCallback should have been called with the correct model'); }); });