From d67fd038dc8a0db7a0559188dbae57edc348869a Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Tue, 18 Jun 2019 17:21:52 -0700 Subject: [PATCH] DacFx integration tests (#6049) * tests working * add bacpac * formatting * addressing comments * ignore bacpacs for hygiene check * add check for error message when checking for db creation * adding comments --- build/gulpfile.hygiene.js | 5 +- .../integration-tests/src/dacpac.test.ts | 93 ++++++++++++++++++ .../src/testData/Database1.bacpac | Bin 0 -> 4473 bytes extensions/integration-tests/src/utils.ts | 55 +++++++++++ 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 extensions/integration-tests/src/dacpac.test.ts create mode 100644 extensions/integration-tests/src/testData/Database1.bacpac diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index b8006f27b8..1b1b520d99 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -92,7 +92,7 @@ const indentationFilter = [ '!**/*.dockerfile', '!extensions/markdown-language-features/media/*.js', // {{SQL CARBON EDIT}} - '!**/*.{xlf,docx,sql,vsix}', + '!**/*.{xlf,docx,sql,vsix,bacpac}', '!extensions/mssql/sqltoolsservice/**', '!extensions/import/flatfileimportservice/**', '!extensions/admin-tool-ext-win/ssmsmin/**', @@ -156,7 +156,8 @@ const copyrightFilter = [ '!extensions/notebook/resources/jupyter_config/**', '!**/*.gif', '!**/*.xlf', - '!**/*.dacpac' + '!**/*.dacpac', + '!**/*.bacpac' ]; const eslintFilter = [ diff --git a/extensions/integration-tests/src/dacpac.test.ts b/extensions/integration-tests/src/dacpac.test.ts new file mode 100644 index 0000000000..d0d0e9d48a --- /dev/null +++ b/extensions/integration-tests/src/dacpac.test.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'mocha'; +import * as azdata from 'azdata'; +import * as utils from './utils'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; +import { context } from './testContext'; +import { getStandaloneServer } from './testConfig'; +import assert = require('assert'); + +const retryCount = 24; // 2 minutes +if (context.RunTest) { + suite('Dacpac integration test suite', () => { + suiteSetup(async function () { + await utils.sleep(5000); // To ensure the providers are registered. + console.log(`Start dacpac tests`); + }); + + test('Deploy and extract dacpac', async function () { + const server = await getStandaloneServer(); + await utils.connectToServer(server); + + const nodes = await azdata.objectexplorer.getActiveConnectionNodes(); + const index = nodes.findIndex(node => node.nodePath.includes(server.serverName)); + const ownerUri = await azdata.connection.getUriForConnection(nodes[index].connectionId); + const now = new Date(); + const databaseName = 'ADS_deployDacpac_' + now.getTime().toString(); + + try { + const dacfxService = await azdata.dataprotocol.getProvider('MSSQL', azdata.DataProviderType.DacFxServicesProvider); + assert(dacfxService, 'DacFx Service Provider is not available'); + + // Deploy dacpac + const deployResult = await dacfxService.deployDacpac(path.join(__dirname, 'testData/Database1.dacpac'), databaseName, false, ownerUri, azdata.TaskExecutionMode.execute); + await utils.assertDatabaseCreationResult(databaseName, ownerUri, retryCount); + assert(deployResult.success === true && deployResult.errorMessage === '', `Deploy dacpac should succeed Expected: there should be no error. Actual Error message: "${deployResult.errorMessage}"`); + + // Extract dacpac + const folderPath = path.join(os.tmpdir(), 'DacFxTest'); + if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath); + } + const packageFilePath = path.join(folderPath, `${databaseName}.dacpac`); + const extractResult = await dacfxService.extractDacpac(databaseName, packageFilePath, databaseName, '1.0.0.0', ownerUri, azdata.TaskExecutionMode.execute); + await utils.assertFileGenerationResult(packageFilePath, retryCount); + + assert(extractResult.success === true && extractResult.errorMessage === '', `Extract dacpac should succeed. Expected: there should be no error. Actual Error message: "${extractResult.errorMessage}"`); + } finally { + await utils.deleteDB(databaseName, ownerUri); + } + }); + + test('Import and export bacpac', async function () { + const server = await getStandaloneServer(); + await utils.connectToServer(server); + + const nodes = await azdata.objectexplorer.getActiveConnectionNodes(); + const index = nodes.findIndex(node => node.nodePath.includes(server.serverName)); + const ownerUri = await azdata.connection.getUriForConnection(nodes[index].connectionId); + const now = new Date(); + const databaseName = 'ADS_importBacpac_' + now.getTime().toString(); + + try { + let dacfxService = await azdata.dataprotocol.getProvider('MSSQL', azdata.DataProviderType.DacFxServicesProvider); + assert(dacfxService, 'DacFx Service Provider is not available'); + + // Import bacpac + const importResult = await dacfxService.importBacpac(path.join(__dirname, 'testData/Database1.bacpac'), databaseName, ownerUri, azdata.TaskExecutionMode.execute); + await utils.assertDatabaseCreationResult(databaseName, ownerUri, retryCount); + assert(importResult.success === true && importResult.errorMessage === '', `Expected: Import bacpac should succeed and there should be no error. Actual Error message: "${importResult.errorMessage}"`); + + // Export bacpac + const folderPath = path.join(os.tmpdir(), 'DacFxTest'); + if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath); + } + const packageFilePath = path.join(folderPath, `${databaseName}.bacpac`); + const exportResult = await dacfxService.exportBacpac(databaseName, packageFilePath, ownerUri, azdata.TaskExecutionMode.execute); + await utils.assertFileGenerationResult(packageFilePath, retryCount); + assert(exportResult.success === true && exportResult.errorMessage === '', `Expected: Export bacpac should succeed and there should be no error. Actual Error message: "${exportResult.errorMessage}"`); + } finally { + await utils.deleteDB(databaseName, ownerUri); + } + }); + }); +} diff --git a/extensions/integration-tests/src/testData/Database1.bacpac b/extensions/integration-tests/src/testData/Database1.bacpac new file mode 100644 index 0000000000000000000000000000000000000000..a3646d2d915c20b17145e996ba60fd01432088cc GIT binary patch literal 4473 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 jt^! await queryProvider.runQueryAndReturn(ownerUri, query); } +export async function runQuery(query: string, ownerUri: string): Promise { + let queryProvider = azdata.dataprotocol.getProvider('MSSQL', azdata.DataProviderType.QueryProvider); + let result = await queryProvider.runQueryAndReturn(ownerUri, query); + return result; +} + export async function assertThrowsAsync(fn: () => Promise, msg: string): Promise { let f = () => { // Empty @@ -89,3 +96,51 @@ export async function assertThrowsAsync(fn: () => Promise, msg: string): Pr assert.throws(f, msg); } } + +/** + * + * @param databaseName name of database to check for + * @param ownerUri owner uri + * @param retryCount number of times to retry with a 5 second wait between each try + * Checks for database getting created for operations that have async database creation + */ +export async function assertDatabaseCreationResult(databaseName: string, ownerUri: string, retryCount: number): Promise { + let result: azdata.SimpleExecuteResult; + while (retryCount > 0) { + --retryCount; + await sleep(5000); + + let query = `BEGIN TRY + SELECT name FROM master.dbo.sysdatabases WHERE name='${databaseName}' + END TRY + BEGIN CATCH + SELECT ERROR_MESSAGE() AS ErrorMessage; + END CATCH`; + result = await runQuery(query, ownerUri); + if (result.rowCount > 0) { + break; + } + } + + assert(result.rowCount === 1, `Database ${databaseName} should be created`); + assert(result.columnInfo[0].columnName !== 'ErrorMessage', 'Checking for db creation threw error'); +} + +/** + * + * @param filepath File path to check for + * @param retryCount number of times to retry with a 5 second wait between each try + * Checks for file getting created for async file generation and deletes file + */ +export async function assertFileGenerationResult(filepath: string, retryCount: number): Promise { + let exists = false; + while (retryCount > 0 && !exists) { + --retryCount; + exists = fs.existsSync(filepath); + await sleep(5000); + } + + assert(exists, `File ${filepath} is expected to be present`); + assert(fs.readFileSync(filepath).byteLength > 0, 'File should not be empty'); + fs.unlinkSync(filepath); +} \ No newline at end of file