diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 2359dbc772..d8d0108fb8 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -6,6 +6,7 @@ import * as constants from '../common/constants'; import * as dataSources from '../models/dataSources/dataSources'; import * as mssql from '../../../mssql'; +import * as os from 'os'; import * as path from 'path'; import * as utils from '../common/utils'; import * as UUID from 'vscode-languageclient/lib/utils/uuid'; @@ -222,13 +223,17 @@ export class ProjectsController { return undefined; // buildProject() handles displaying the error } - const dacFxService = await ProjectsController.getDaxFxService(); + // copy dacpac to temp location before deployment + const tempPath = path.join(os.tmpdir(), `${path.parse(dacpacPath).name}_${new Date().getTime()}${constants.sqlprojExtension}`); + await fs.copyFile(dacpacPath, tempPath); + + const dacFxService = await this.getDaxFxService(); if ((profile).upgradeExisting) { - return await dacFxService.deployDacpac(dacpacPath, profile.databaseName, (profile).upgradeExisting, profile.connectionUri, TaskExecutionMode.execute, profile.sqlCmdVariables); + return await dacFxService.deployDacpac(tempPath, profile.databaseName, (profile).upgradeExisting, profile.connectionUri, TaskExecutionMode.execute, profile.sqlCmdVariables); } else { - return await dacFxService.generateDeployScript(dacpacPath, profile.databaseName, profile.connectionUri, TaskExecutionMode.script, profile.sqlCmdVariables); + return await dacFxService.generateDeployScript(tempPath, profile.databaseName, profile.connectionUri, TaskExecutionMode.script, profile.sqlCmdVariables); } } @@ -511,7 +516,7 @@ export class ProjectsController { } } - private static async getDaxFxService(): Promise { + public async getDaxFxService(): Promise { const ext: Extension = extensions.getExtension(mssql.extension.name)!; await ext.activate(); diff --git a/extensions/sql-database-projects/src/test/projectController.test.ts b/extensions/sql-database-projects/src/test/projectController.test.ts index 3f0619fb74..9ead46cedf 100644 --- a/extensions/sql-database-projects/src/test/projectController.test.ts +++ b/extensions/sql-database-projects/src/test/projectController.test.ts @@ -17,7 +17,7 @@ import * as constants from '../common/constants'; import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProjectTreeViewProvider'; import { ProjectsController } from '../controllers/projectController'; import { promises as fs } from 'fs'; -import { createContext, TestContext } from './testContext'; +import { createContext, TestContext, mockDacFxResult } from './testContext'; import { Project, SystemDatabase } from '../models/project'; import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog'; import { ApiWrapper } from '../common/apiWrapper'; @@ -185,6 +185,36 @@ describe.skip('ProjectsController: project controller operations', function (): should(Object.keys(result.sqlCmdVariables).length).equal(1); should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); }); + + it('Should copy dacpac to temp folder before deploying', async function (): Promise { + const fakeDacpacContents = 'SwiftFlewHiawathasArrow'; + let postCopyContents = ''; + let builtDacpacPath = ''; + let deployedDacpacPath = ''; + + testContext.dacFxService.setup(x => x.generateDeployScript(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(async (p) => { + deployedDacpacPath = p; + postCopyContents = (await fs.readFile(deployedDacpacPath)).toString(); + return Promise.resolve(mockDacFxResult); + }); + + let projController = TypeMoq.Mock.ofType(ProjectsController); + projController.callBase = true; + + projController.setup(x => x.buildProject(TypeMoq.It.isAny())).returns(async () => { + builtDacpacPath = await testUtils.createTestFile(fakeDacpacContents, 'output.dacpac'); + return builtDacpacPath; + }); + + projController.setup(x => x.getDaxFxService()).returns(() => Promise.resolve(testContext.dacFxService.object)); + + await projController.object.executionCallback(new Project(''), { connectionUri: '', databaseName: '' }); + + should(builtDacpacPath).not.equal('', 'built dacpac path should be set'); + should(deployedDacpacPath).not.equal('', 'deployed dacpac path should be set'); + should(builtDacpacPath).not.equal(deployedDacpacPath, 'built and deployed dacpac paths should be different'); + should(postCopyContents).equal(fakeDacpacContents, 'contents of built and deployed dacpacs should match'); + }); }); }); diff --git a/extensions/sql-database-projects/src/test/testContext.ts b/extensions/sql-database-projects/src/test/testContext.ts index f1979446b2..e106f14fea 100644 --- a/extensions/sql-database-projects/src/test/testContext.ts +++ b/extensions/sql-database-projects/src/test/testContext.ts @@ -4,13 +4,33 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as azdata from 'azdata'; import * as path from 'path'; import * as TypeMoq from 'typemoq'; +import * as mssql from '../../../mssql/src/mssql'; import { ApiWrapper } from '../common/apiWrapper'; export interface TestContext { apiWrapper: TypeMoq.IMock; context: vscode.ExtensionContext; + dacFxService: TypeMoq.IMock; +} + +export const mockDacFxResult = { + operationId: '', + success: true, + errorMessage: '', + report: '' +}; + +export class MockDacFxService implements mssql.IDacFxService { + public exportBacpac(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } + public importBacpac(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } + public extractDacpac(_: string, __: string, ___: string, ____: string, _____: string, ______: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } + public importDatabaseProject(_: string, __: string, ___: string, ____: string, _____: string, ______: mssql.ExtractTarget, _______: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } + public deployDacpac(_: string, __: string, ___: boolean, ____: string, _____: azdata.TaskExecutionMode, ______?: Record): Thenable { return Promise.resolve(mockDacFxResult); } + public generateDeployScript(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode, ______?: Record): Thenable { return Promise.resolve(mockDacFxResult); } + public generateDeployPlan(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } } export function createContext(): TestContext { @@ -37,5 +57,6 @@ export function createContext(): TestContext { environmentVariableCollection: undefined as any, extensionMode: undefined as any }, + dacFxService: TypeMoq.Mock.ofType(MockDacFxService) }; }